package vg.civcraft.mc.civmodcore.world;

import com.google.common.base.Preconditions;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.Location;
import org.bukkit.block.Beacon;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;

public final class BeaconTracker implements Listener {

	private static final Set<Beacon> STORAGE = new HashSet<>();

	@EventHandler(priority = EventPriority.MONITOR)
	public void onChunkLoad(final ChunkLoadEvent event) {
		for (final BlockState tile : event.getChunk().getTileEntities()) {
			if (tile instanceof Beacon) {
				STORAGE.add((Beacon) tile);
			}
		}
	}

	@EventHandler(priority = EventPriority.MONITOR)
	public void onChunkUnload(final ChunkUnloadEvent event) {
		for (final BlockState tile : event.getChunk().getTileEntities()) {
			if (tile instanceof Beacon) {
				STORAGE.remove(tile);
			}
		}
	}

	@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
	public void onBlockBreak(final BlockBreakEvent event) {
		final BlockState state = event.getBlock().getState();
		if (state instanceof Beacon) {
			STORAGE.remove(state);
		}
	}

	@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
	public void onExplosion(final BlockExplodeEvent event) {
		for (final Block block : event.blockList()) {
			final BlockState state = block.getState();
			if (state instanceof Beacon) {
				STORAGE.remove(state);
			}
		}
	}

	// Prevent Beacons from being moved.. ultimately necessary
	// TODO: Replace this when you find a way to track beacon movements that
	//       doesn't instantly bug out immediately with a piston machine.
	@EventHandler(ignoreCancelled = true)
	public void onBlockMove(final BlockFromToEvent event) {
		final BlockState state = event.getBlock().getState();
		if (state instanceof Beacon) {
			event.setCancelled(true);
		}
	}

	/**
	 * Returns all the beacons with fields encompassing the given location.
	 *
	 * @param location The location to test.
	 * @return Returns a set of beacons that encompass the given location, which is never null.
	 */
	public static Set<Beacon> getEncompassingBeacons(final Location location) {
		Preconditions.checkArgument(WorldUtils.isValidLocation(location));
		return STORAGE.stream()
				.filter(beacon -> isInBeaconRange(beacon, location))
				.collect(Collectors.toCollection(HashSet::new));
	}

	/**
	 * Determines if a given location is within the field of a given beacon.
	 *
	 * @param beacon The beacon to test.
	 * @param location The location to test.
	 * @return Returns true if the given location is within the given beacon's field.
	 */
	public static boolean isInBeaconRange(final Beacon beacon, final Location location) {
		final Location beaconLocation = beacon.getLocation();
		final int distanceXZ = WorldUtils.blockDistance(beaconLocation, location, true);
		final double range = beacon.getEffectRange();
		if (distanceXZ < 0d || distanceXZ > range) {
			return false;
		}
		// Beacon range also extends towards, but upwards is infinite
		if (location.getY() < (beacon.getY() - range)) {
			return false;
		}
		return true;
	}

}

@@ -152,26 +152,22 @@
 .stream.
-Collectors
+Stream
 ;%0Aimport
@@ -498,56 +498,8 @@
 nt;%0A
-import org.bukkit.event.block.BlockFromToEvent;%0A
 impo
@@ -587,16 +587,70 @@
 adEvent;
+%0Aimport vg.civcraft.mc.civmodcore.utilities.CivLogger;
 %0A%0Apublic
@@ -699,16 +699,99 @@
 ener %7B%0A%0A
+%09private static final CivLogger LOGGER = CivLogger.getLogger(BeaconTracker.class);%0A
 %09private
@@ -1900,435 +1900,8 @@
 %09%7D%0A%0A
-%09// Prevent Beacons from being moved.. ultimately necessary%0A%09// TODO: Replace this when you find a way to track beacon movements that%0A%09//       doesn't instantly bug out immediately with a piston machine.%0A%09@EventHandler(ignoreCancelled = true)%0A%09public void onBlockMove(final BlockFromToEvent event) %7B%0A%09%09final BlockState state = event.getBlock().getState();%0A%09%09if (state instanceof Beacon) %7B%0A%09%09%09event.setCancelled(true);%0A%09%09%7D%0A%09%7D%0A%0A
 %09/**
@@ -2134,18 +2134,21 @@
 static S
-e
 t
+ream
 %3CBeacon%3E
@@ -2353,60 +2353,8 @@
 on))
-%0A%09%09%09%09.collect(Collectors.toCollection(HashSet::new))
 ;%0A%09%7D
@@ -2882,20 +2882,23 @@
 ge();%0A%09%09
-if (
+return 
 distance
@@ -2904,15 +2904,19 @@
 eXZ 
-%3C 0d %7C%7C
+%3E= 0%0A%09%09%09%09&&
  dis
@@ -2927,110 +2927,74 @@
 eXZ 
-%3E
+%3C=
  range
-) %7B
 %0A%09%09%09
-return false;%0A%09%09%7D%0A%09%09// Beacon range also extends towards, but upwards is infinite%0A%09%09if (
+%09&& location.getY() %3E= (beacon.getY() - range)%0A%09%09%09%09&& 
 loca
@@ -3006,16 +3006,17 @@
 getY() %3C
+=
  (beacon
@@ -3027,54 +3027,22 @@
 Y() 
--
++
  range
-)) %7B%0A%09%09%09return false;%0A%09%09%7D%0A%09%09return true
+ + 256)
 ;%0A%09%7D

Update BeaconTracker