/*************************************************************************
 * 
 * AVRGAMING LLC
 * __________________
 * 
 *  [2013] AVRGAMING LLC
 *  All Rights Reserved.
 * 
 * NOTICE:  All information contained herein is, and remains
 * the property of AVRGAMING LLC and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to AVRGAMING LLC
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from AVRGAMING LLC.
 */
package com.avrgaming.civcraft.listener;

import gpl.HorseModifier;

import java.util.HashSet;
import java.util.Random;

import net.minecraft.server.v1_7_R4.NBTTagCompound;

import org.bukkit.Chunk;
import org.bukkit.Color;
import org.bukkit.FireworkEffect;
import org.bukkit.FireworkEffect.Type;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Fireball;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Painting;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.ThrownPotion;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityBreakDoorEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityCreatePortalEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityInteractEvent;
import org.bukkit.event.entity.PotionSplashEvent;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

import com.avrgaming.civcraft.cache.ArrowFiredCache;
import com.avrgaming.civcraft.cache.CannonFiredCache;
import com.avrgaming.civcraft.cache.CivCache;
import com.avrgaming.civcraft.camp.Camp;
import com.avrgaming.civcraft.camp.CampBlock;
import com.avrgaming.civcraft.config.CivSettings;
import com.avrgaming.civcraft.config.ConfigTempleSacrifice;
import com.avrgaming.civcraft.exception.CivException;
import com.avrgaming.civcraft.main.CivData;
import com.avrgaming.civcraft.main.CivGlobal;
import com.avrgaming.civcraft.main.CivLog;
import com.avrgaming.civcraft.main.CivMessage;
import com.avrgaming.civcraft.object.ControlPoint;
import com.avrgaming.civcraft.object.ProtectedBlock;
import com.avrgaming.civcraft.object.Resident;
import com.avrgaming.civcraft.object.StructureBlock;
import com.avrgaming.civcraft.object.StructureChest;
import com.avrgaming.civcraft.object.StructureSign;
import com.avrgaming.civcraft.object.TownChunk;
import com.avrgaming.civcraft.permission.PlotPermissions;
import com.avrgaming.civcraft.road.Road;
import com.avrgaming.civcraft.road.RoadBlock;
import com.avrgaming.civcraft.structure.Buildable;
import com.avrgaming.civcraft.structure.BuildableLayer;
import com.avrgaming.civcraft.structure.Farm;
import com.avrgaming.civcraft.structure.Pasture;
import com.avrgaming.civcraft.structure.Temple;
import com.avrgaming.civcraft.structure.Wall;
import com.avrgaming.civcraft.structure.farm.FarmChunk;
import com.avrgaming.civcraft.threading.CivAsyncTask;
import com.avrgaming.civcraft.threading.TaskMaster;
import com.avrgaming.civcraft.threading.tasks.FireWorkTask;
import com.avrgaming.civcraft.threading.tasks.StructureBlockHitEvent;
import com.avrgaming.civcraft.util.BlockCoord;
import com.avrgaming.civcraft.util.ChunkCoord;
import com.avrgaming.civcraft.util.CivColor;
import com.avrgaming.civcraft.util.ItemFrameStorage;
import com.avrgaming.civcraft.util.ItemManager;
import com.avrgaming.civcraft.war.War;
import com.avrgaming.civcraft.war.WarRegen;
import com.avrgaming.moblib.MobLib;



public class BlockListener implements Listener {

	/* Experimental, reuse the same object because it is single threaded. */
	public static ChunkCoord coord = new ChunkCoord("", 0, 0);
	public static BlockCoord bcoord = new BlockCoord("", 0,0,0);

	@EventHandler(priority = EventPriority.NORMAL)
	public void onBlockIgniteEvent(BlockIgniteEvent event) {
	//	CivLog.debug("block ignite event");

		for (int x = -1; x <= 1; x++) {
			for (int y = -1; y <= 1; y++) {
				for (int z = -1; z <= 1; z++) {
					Block b = event.getBlock().getRelative(x, y, z);		
					bcoord.setFromLocation(b.getLocation());
					StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
					if (sb != null) {
						if (b.getType().isBurnable()) {
							event.setCancelled(true);
						}
						return;
					}

					RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
					if (rb != null) {
						event.setCancelled(true);
						return;
					}

					CampBlock cb = CivGlobal.getCampBlock(bcoord);
					if (cb != null) {
						event.setCancelled(true);
						return;
					}

					StructureSign structSign = CivGlobal.getStructureSign(bcoord);
					if (structSign != null) {
						event.setCancelled(true);
						return;
					}

					StructureChest structChest = CivGlobal.getStructureChest(bcoord);
					if (structChest != null) {
						event.setCancelled(true);
						return;
					}
				}
			}
	    }


		coord.setFromLocation(event.getBlock().getLocation());
		TownChunk tc = CivGlobal.getTownChunk(coord);

		if (tc == null) {
			return;
		}

		if (tc.perms.isFire() == false) {
			CivMessage.sendError(event.getPlayer(), "Fire disabled in this chunk.");
			event.setCancelled(true);
		}		
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void onEntityBlockChange(EntityChangeBlockEvent event) {
		bcoord.setFromLocation(event.getBlock().getLocation());

		StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
		if (sb != null) {
			event.setCancelled(true);
			return;
		}

		RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
		if (rb != null) {
			event.setCancelled(true);
			return;
		}

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null) {
			event.setCancelled(true);
			return;
		}

		return;
	}

	@EventHandler(priority = EventPriority.LOW)
	public void onBlockBurnEvent(BlockBurnEvent event) {
		bcoord.setFromLocation(event.getBlock().getLocation());

		StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
		if (sb != null) {
			event.setCancelled(true);
			return;
		}

		RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
		if (rb != null) {
			event.setCancelled(true);
			return;
		}

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null) {
			event.setCancelled(true);
			return;
		}
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void onProjectileHitEvent(ProjectileHitEvent event) {
		if (event.getEntity() instanceof Arrow) {
			ArrowFiredCache afc = CivCache.arrowsFired.get(event.getEntity().getUniqueId());
			if (afc != null) {
				afc.setHit(true);
			}
		}

		if (event.getEntity() instanceof Fireball) {
			CannonFiredCache cfc = CivCache.cannonBallsFired.get(event.getEntity().getUniqueId());
			if (cfc != null) {

				cfc.setHit(true);

				FireworkEffect fe = FireworkEffect.builder().withColor(Color.RED).withColor(Color.BLACK).flicker(true).with(Type.BURST).build();

				Random rand = new Random();
				int spread = 30;
				for (int i = 0; i < 15; i++) {
					int x = rand.nextInt(spread) - spread/2;
					int y = rand.nextInt(spread) - spread/2;
					int z = rand.nextInt(spread) - spread/2;


					Location loc = event.getEntity().getLocation();
					Location location = new Location(loc.getWorld(), loc.getX(),loc.getY(), loc.getZ());
					location.add(x, y, z);

					TaskMaster.syncTask(new FireWorkTask(fe, loc.getWorld(), loc, 5), rand.nextInt(30));
				}

			}
		}
	}

	@EventHandler(priority = EventPriority.LOWEST)
	public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
		/* Protect the Protected Item Frames! */
		if (event.getEntity() instanceof ItemFrame) {
			ItemFrameStorage iFrameStorage = CivGlobal.getProtectedItemFrame(event.getEntity().getUniqueId());
			if (iFrameStorage != null) {
				event.setCancelled(true);
				return;
			}
		}

		if (!(event.getEntity() instanceof Player)) {			
			return;
		}	

		Player defender = (Player)event.getEntity();
		/* Only protect agaisnt players and entities that players can throw. */
		if (!CivSettings.playerEntityWeapons.contains(event.getDamager().getType())) {
			return;
		}

		if (event.getDamager() instanceof Arrow) {

		}

		if (event.getDamager() instanceof Fireball) {
			CannonFiredCache cfc = CivCache.cannonBallsFired.get(event.getDamager().getUniqueId());
			if (cfc != null) {

				cfc.setHit(true);
				cfc.destroy(event.getDamager());
				event.setDamage((double)cfc.getFromTower().getDamage());
			}
		}

		coord.setFromLocation(event.getEntity().getLocation());
		TownChunk tc = CivGlobal.getTownChunk(coord);
		boolean allowPVP = false;
		String denyMessage = "";

		if (tc == null) {
			/* In the wilderness, anything goes. */
			allowPVP = true;
		} else {	
			Player attacker = null;
			if (event.getDamager() instanceof Player) {
				attacker = (Player)event.getDamager();
			} else if (event.getDamager() instanceof Projectile) {
				LivingEntity shooter = (LivingEntity) ((Projectile)event.getDamager()).getShooter();
				if (shooter instanceof Player) {
					attacker = (Player) shooter;
				}
			} 

			if (attacker == null) {
				/* Attacker wasnt a player or known projectile, allow it. */
				allowPVP = true;
			} else {
				switch(playersCanPVPHere(attacker, defender, tc)) {
				case ALLOWED:
					allowPVP = true;
					break;
				case NOT_AT_WAR:
					allowPVP = false;
					denyMessage = "You cannot PvP with "+defender.getName()+" as you are not at war.";
					break;
				case NEUTRAL_IN_WARZONE:
					allowPVP = false;
					denyMessage = "You cannot PvP here with "+defender.getName()+" since you are a neutral in a war-zone.";
					break;
				case NON_PVP_ZONE:
					allowPVP = false;
					denyMessage = "You cannot PvP with "+defender.getName()+" since you are in a non-pvp zone.";
					break;
				}
			}

			if (!allowPVP) {
				CivMessage.sendError(attacker, denyMessage);
				event.setCancelled(true);
			} else {

			}
		}

		return;
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void OnCreateSpawnEvent(CreatureSpawnEvent event) {

		if (event.getSpawnReason().equals(SpawnReason.BREEDING)) {
			ChunkCoord coord = new ChunkCoord(event.getEntity().getLocation());
			Pasture pasture = Pasture.pastureChunks.get(coord);

			if (pasture != null) {
				pasture.onBreed(event.getEntity());
			}
		}

		class SyncTask implements Runnable {
			LivingEntity entity;

			public SyncTask(LivingEntity entity) {
				this.entity = entity;
			}

			@Override
			public void run() {
				if (entity != null) {
					if (!HorseModifier.isCivCraftHorse(entity)) {
						CivLog.warning("Removing a normally spawned horse.");
						entity.remove();
					}
				}
			}
		}

		if (event.getEntityType() == EntityType.HORSE) {
			if (event.getSpawnReason().equals(SpawnReason.DEFAULT)) {
				TaskMaster.syncTask(new SyncTask(event.getEntity()));
				return;
			}

			CivLog.warning("Canceling horse spawn reason:"+event.getSpawnReason().name());
			event.setCancelled(true);
		}

		coord.setFromLocation(event.getLocation());
		TownChunk tc = CivGlobal.getTownChunk(coord);
		if (tc == null) {
			return;
		}

		if (tc.perms.isMobs() == false) {
			if (event.getSpawnReason().equals(SpawnReason.CUSTOM)) {
				return;
			}

			if (CivSettings.restrictedSpawns.containsKey(event.getEntityType())) {
				event.setCancelled(true);
				return;
			}
		}		
	}


	@EventHandler(priority = EventPriority.NORMAL)
	public void OnEntityExplodeEvent(EntityExplodeEvent event) {
		if (event.getEntity() == null) {
			return;
		}
		/* prevent ender dragons from breaking blocks. */
		if (event.getEntityType().equals(EntityType.COMPLEX_PART)) {
			event.setCancelled(true);
		} else if (event.getEntityType().equals(EntityType.ENDER_DRAGON)) {
			event.setCancelled(true);
		}

		for (Block block : event.blockList()) {
			bcoord.setFromLocation(block.getLocation());
			StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
			if (sb != null) {
				event.setCancelled(true);
				return;
			}

			RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
			if (rb != null) {
				event.setCancelled(true);
				return;
			}

			CampBlock cb = CivGlobal.getCampBlock(bcoord);
			if (cb != null) {
				event.setCancelled(true);
				return;
			}

			StructureSign structSign = CivGlobal.getStructureSign(bcoord);
			if (structSign != null) {
				event.setCancelled(true);
				return;
			}

			StructureChest structChest = CivGlobal.getStructureChest(bcoord);
			if (structChest != null) {
				event.setCancelled(true);
				return;
			}

			coord.setFromLocation(block.getLocation());

			HashSet<Wall> walls = CivGlobal.getWallChunk(coord);
			if (walls != null) {
				for (Wall wall : walls) {
					if (wall.isProtectedLocation(block.getLocation())) {
						event.setCancelled(true);
						return;
					}
				}
			}

			TownChunk tc = CivGlobal.getTownChunk(coord);
			if (tc == null) {
				continue;
			}

			event.setCancelled(true);
			return;
		}

	}

     private final BlockFace[] faces = new BlockFace[] {
			        BlockFace.DOWN,
		            BlockFace.NORTH,
		            BlockFace.EAST,
		            BlockFace.SOUTH,
		            BlockFace.WEST,		            
		            BlockFace.SELF,
		            BlockFace.UP
	  };

    public BlockCoord generatesCobble(int id, Block b)
    {
        int mirrorID1 = (id == CivData.WATER_RUNNING || id == CivData.WATER ? CivData.LAVA_RUNNING : CivData.WATER_RUNNING);
        int mirrorID2 = (id == CivData.WATER_RUNNING || id == CivData.WATER ? CivData.LAVA : CivData.WATER);
        for(BlockFace face : faces)
        {
            Block r = b.getRelative(face, 1);
            if(ItemManager.getId(r) == mirrorID1 || ItemManager.getId(r) == mirrorID2)
            {
            	
            	return new BlockCoord(r);
            }
        }
        
        return null;
    }

//    private static void destroyLiquidRecursive(Block source) {
//    	//source.setTypeIdAndData(CivData.AIR, (byte)0, false);
//    	NMSHandler nms = new NMSHandler();
//    	nms.setBlockFast(source.getWorld(), source.getX(), source.getY(), source.getZ(), 0, (byte)0);
//    	
//    	for (BlockFace face : BlockFace.values()) {
//    		Block relative = source.getRelative(face);
//    		if (relative == null) {
//    			continue;
//    		}
//    		
//    		if (!isLiquid(relative.getTypeId())) {
//    			continue;
//    		}
//    		
//    		destroyLiquidRecursive(relative);
//    	}
//    }
    
//    private static boolean isLiquid(int id) {
//    	return (id >= CivData.WATER && id <= CivData.LAVA);
//    }
    
    private static HashSet<BlockCoord> stopCobbleTasks = new HashSet<BlockCoord>();
	@EventHandler(priority = EventPriority.NORMAL)
	public void OnBlockFromToEvent(BlockFromToEvent event) {
		/* Disable cobblestone generators. */
		int id = ItemManager.getId(event.getBlock());
	    if(id >= CivData.WATER && id <= CivData.LAVA)
	    {
	        Block b = event.getToBlock();
	        bcoord.setFromLocation(b.getLocation());

	        int toid = ItemManager.getId(b);
	        if(toid == 0)
	        {
	            BlockCoord other = generatesCobble(id, b);
	        	if(other != null)
	            {
	            	//BlockCoord d = new BlockCoord(event.getToBlock());
//	            	BlockCoord fromCoord = new BlockCoord(event.getBlock());
	            	event.setCancelled(true);

	            	class SyncTask implements Runnable {
	            		BlockCoord block;

	            		public SyncTask(BlockCoord block) {
	            			this.block = block;
	            		}

						@Override
						public void run() {
							ItemManager.setTypeIdAndData(block.getBlock(), CivData.NETHERRACK, (byte)0, true);
							stopCobbleTasks.remove(block);
						}
	            	}

	            	if (!stopCobbleTasks.contains(other)) {
	            		stopCobbleTasks.add(other);
	            		TaskMaster.syncTask(new SyncTask(other), 2);
	            	}

//	            	if (!stopCobbleTasks.contains(fromCoord)) {
//	            		stopCobbleTasks.add(fromCoord);
//	            		TaskMaster.syncTask(new SyncTask(fromCoord));
//	            	}
	            }
	        }
	    }
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void OnBlockFormEvent (BlockFormEvent event) {

		/* Disable cobblestone generators. */
		if (ItemManager.getId(event.getNewState()) == CivData.COBBLESTONE) {
			ItemManager.setTypeId(event.getNewState(), CivData.GRAVEL);
			return;
		}

		Chunk spreadChunk = event.getNewState().getChunk();
		coord.setX(spreadChunk.getX());
		coord.setZ(spreadChunk.getZ());
		coord.setWorldname(spreadChunk.getWorld().getName());

		TownChunk tc = CivGlobal.getTownChunk(coord);
		if (tc == null) {
			return;
		}

		if (tc.perms.isFire() == false) {
			if(event.getNewState().getType() == Material.FIRE) {
				event.setCancelled(true);
			}
		}
	}

	@EventHandler(priority = EventPriority.LOW)
	public void OnBlockPlaceEvent(BlockPlaceEvent event) {
		Resident resident = CivGlobal.getResident(event.getPlayer());

		if (resident == null) {
			event.setCancelled(true);
			return;
		}

		if(resident.isSBPermOverride()) {
			return;
		}

		bcoord.setFromLocation(event.getBlockAgainst().getLocation());
		StructureSign sign = CivGlobal.getStructureSign(bcoord);
		if (sign != null) {
			event.setCancelled(true);
			return;
		}

		bcoord.setFromLocation(event.getBlock().getLocation());
		StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
		if (sb != null) {
			event.setCancelled(true);
			CivMessage.sendError(event.getPlayer(), 
					"This block belongs to a "+sb.getOwner().getDisplayName()+" and cannot be destroyed.");
			return;
		}

		RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
		if (rb != null) {
			if (rb.isAboveRoadBlock()) {
				if (resident.getCiv() != rb.getRoad().getCiv()) {
					event.setCancelled(true);
					CivMessage.sendError(event.getPlayer(), 
							"Cannot place blocks "+(Road.HEIGHT-1)+" blocks above a road that does not belong to your civ.");
				}
			}
			return;
		}

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null && !cb.canBreak(event.getPlayer().getName())) {
			event.setCancelled(true);
			CivMessage.sendError(event.getPlayer(), "This block is part of camp "+cb.getCamp().getName()+" owned by "+cb.getCamp().getOwner().getName()+" and cannot be destroyed.");
			return;
		}  		

		coord.setFromLocation(event.getBlock().getLocation());
		TownChunk tc = CivGlobal.getTownChunk(coord);
		if (CivSettings.blockPlaceExceptions.get(event.getBlock().getType()) != null) {
			return;
		}

		if (tc != null) {	
			if(!tc.perms.hasPermission(PlotPermissions.Type.BUILD, resident)) {
				if (War.isWarTime() && resident.hasTown() && 
						resident.getTown().getCiv().getDiplomacyManager().atWarWith(tc.getTown().getCiv())) {
					if (WarRegen.canPlaceThisBlock(event.getBlock())) {
						WarRegen.saveBlock(event.getBlock(), tc.getTown().getName(), true);
						return;
					} else {
						CivMessage.sendErrorNoRepeat(event.getPlayer(), "Cannot place this type of block in an emeny town during WarTime.");
						event.setCancelled(true);
						return;
					}
				} else {
					event.setCancelled(true);
					CivMessage.sendError(event.getPlayer(), "You do not have permission to build here.");
				}
			} 
		}

		/* Check if we're going to break too many structure blocks beneath a structure. */
		//LinkedList<StructureBlock> sbList = CivGlobal.getStructureBlocksAt(bcoord.getWorldname(), bcoord.getX(), bcoord.getZ());
		HashSet<Buildable> buildables = CivGlobal.getBuildablesAt(bcoord);
		if (buildables != null) {
			for (Buildable buildable : buildables) {		
				if (!buildable.validated) {
					try {
						buildable.validate(event.getPlayer());
					} catch (CivException e) {
						e.printStackTrace();
					}
					continue;
				}

				/* Building is validated, grab the layer and determine if this would set it over the limit. */
				BuildableLayer layer = buildable.layerValidPercentages.get(bcoord.getY());
				if (layer == null) {
					continue;
				}

				/* Update the layer. */
				layer.current += Buildable.getReinforcementValue(ItemManager.getId(event.getBlockPlaced()));
				if (layer.current < 0) {
					layer.current = 0;
				}
				buildable.layerValidPercentages.put(bcoord.getY(), layer);
			}
		}
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void OnBlockBreakEvent(BlockBreakEvent event) {
		Resident resident = CivGlobal.getResident(event.getPlayer());

		if (resident == null) {
			event.setCancelled(true);
			return;
		}

		if (resident.isSBPermOverride()) {
			return;
		}

		bcoord.setFromLocation(event.getBlock().getLocation());
		StructureBlock sb = CivGlobal.getStructureBlock(bcoord);

		if (sb != null) {
			event.setCancelled(true);
			TaskMaster.syncTask(new StructureBlockHitEvent(event.getPlayer().getName(), bcoord, sb, event.getBlock().getWorld()), 0);
			return;
		}

		RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
		if (rb != null && !rb.isAboveRoadBlock()) {
			if (War.isWarTime()) {
				/* Allow blocks to be 'destroyed' during war time. */
				WarRegen.destroyThisBlock(event.getBlock(), rb.getTown());
				event.setCancelled(true);
				return;
			} else {
				event.setCancelled(true);
				rb.onHit(event.getPlayer());
				return;
			}
		}

		ProtectedBlock pb = CivGlobal.getProtectedBlock(bcoord);
		if (pb != null) {
			event.setCancelled(true);
			CivMessage.sendError(event.getPlayer(), "This block is protected and cannot be destroyed.");
			return;
		}

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null && !cb.canBreak(event.getPlayer().getName())) {
			ControlPoint cBlock = cb.getCamp().controlBlocks.get(bcoord);
			if (cBlock != null) {
				cb.getCamp().onDamage(1, event.getBlock().getWorld(), event.getPlayer(), bcoord, null);
				event.setCancelled(true);
				return;
			} else {	
				event.setCancelled(true);
				CivMessage.sendError(event.getPlayer(), "This block is part of camp "+cb.getCamp().getName()+" owned by "+cb.getCamp().getOwner().getName()+" and cannot be destroyed.");
				return;
			}
		}

		StructureSign structSign = CivGlobal.getStructureSign(bcoord);
		if (structSign != null && !resident.isSBPermOverride()) {
			event.setCancelled(true);
			CivMessage.sendError(event.getPlayer(), "Please do not destroy signs.");
			return;
		}

		StructureChest structChest = CivGlobal.getStructureChest(bcoord);
		if (structChest != null && !resident.isSBPermOverride()) {
			event.setCancelled(true);
			CivMessage.sendError(event.getPlayer(), "Please do not destroy chests.");
			return;
		}

		coord.setFromLocation(event.getBlock().getLocation());
		HashSet<Wall> walls = CivGlobal.getWallChunk(coord);

		if (walls != null) {
			for (Wall wall : walls) {
				if (wall.isProtectedLocation(event.getBlock().getLocation())) {
					if (resident == null || !resident.hasTown() || resident.getTown().getCiv() != wall.getTown().getCiv() && !resident.isSBPermOverride()) {
						
						StructureBlock tmpStructureBlock = new StructureBlock(bcoord, wall);
						tmpStructureBlock.setAlwaysDamage(true);
						TaskMaster.syncTask(new StructureBlockHitEvent(event.getPlayer().getName(), bcoord, tmpStructureBlock, event.getBlock().getWorld()), 0);
						//CivMessage.sendError(event.getPlayer(), "Cannot destroy this block, protected by a wall, destroy it first.");
						event.setCancelled(true);
						return;
					} else {
						CivMessage.send(event.getPlayer(), CivColor.LightGray+"We destroyed a block protected by a wall. This was allowed because we're a member of "+
								resident.getTown().getCiv().getName());
						break;
					}
				}
			}
		}

		TownChunk tc = CivGlobal.getTownChunk(coord);
		if (tc != null) {
			if(!tc.perms.hasPermission(PlotPermissions.Type.DESTROY, resident)) {
				event.setCancelled(true);

				if (War.isWarTime() && resident.hasTown() && 
						resident.getTown().getCiv().getDiplomacyManager().atWarWith(tc.getTown().getCiv())) {
					WarRegen.destroyThisBlock(event.getBlock(), tc.getTown());
				} else {
					CivMessage.sendErrorNoRepeat(event.getPlayer(), "You do not have permission to destroy here.");
				}
			}
		}

		/* Check if we're going to break too many structure blocks beneath a structure. */
		//LinkedList<StructureBlock> sbList = CivGlobal.getStructureBlocksAt(bcoord.getWorldname(), bcoord.getX(), bcoord.getZ());
		HashSet<Buildable> buildables = CivGlobal.getBuildablesAt(bcoord);
		if (buildables != null) {
			for (Buildable buildable : buildables) {
				if (!buildable.validated) {
					try {
						buildable.validate(event.getPlayer());
					} catch (CivException e) {
						e.printStackTrace();
					}
					continue;
				}

				/* Building is validated, grab the layer and determine if this would set it over the limit. */
				BuildableLayer layer = buildable.layerValidPercentages.get(bcoord.getY());
				if (layer == null) {
					continue;
				}

				double current = layer.current - Buildable.getReinforcementValue(ItemManager.getId(event.getBlock()));
				if (current < 0) {
					current = 0;
				}
				Double percentValid = (double)(current) / (double)layer.max;

				if (percentValid < Buildable.validPercentRequirement) {
					CivMessage.sendError(event.getPlayer(), "Cannot break this block since it's supporting the "+buildable.getDisplayName()+" above it.");
					event.setCancelled(true);
					return;
				}

				/* Update the layer. */
				layer.current = (int)current;
				buildable.layerValidPercentages.put(bcoord.getY(), layer);
			}
		}

	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void OnEntityInteractEvent(EntityInteractEvent event) {
		if (event.getBlock() != null) {			
			if (CivSettings.switchItems.contains(event.getBlock().getType())) {
				coord.setFromLocation(event.getBlock().getLocation());
				TownChunk tc = CivGlobal.getTownChunk(coord);

				if (tc == null) {
					return;
				}

				/* A non-player entity is trying to trigger something, if interact permission is
				 * off for others then disallow it.
				 */
				if (tc.perms.interact.isPermitOthers()) {
					return;
				}

				if (event.getEntity() instanceof Player) {
					CivMessage.sendErrorNoRepeat((Player)event.getEntity(), "You do not have permission to interact here...");
				}

				event.setCancelled(true);
			}
		}

	}

	@EventHandler(priority = EventPriority.HIGHEST)
	public void OnPlayerConsumeEvent(PlayerItemConsumeEvent event) {
		ItemStack stack = event.getItem();

		/* Disable notch apples */
		if (ItemManager.getId(event.getItem()) == ItemManager.getId(Material.GOLDEN_APPLE)) {
			if (event.getItem().getDurability() == (short)0x1) {
				CivMessage.sendError(event.getPlayer(), "You cannot use notch apples. Sorry.");
				event.setCancelled(true);
				return;
			}
		}	

		if (stack.getType().equals(Material.POTION)) {
			int effect = event.getItem().getDurability() & 0x000F;			
			if (effect == 0xE) {
				event.setCancelled(true);
				CivMessage.sendError(event.getPlayer(), "You cannot use invisibility potions for now... Sorry.");
				return;
			}
		}
	}

	@EventHandler(priority = EventPriority.NORMAL) 
	public void onBlockDispenseEvent(BlockDispenseEvent event) {
		ItemStack stack = event.getItem();
		if (stack != null) {
			if (event.getItem().getType().equals(Material.POTION)) {
				int effect = event.getItem().getDurability() & 0x000F;			
				if (effect == 0xE) { 
					event.setCancelled(true);
					return;
				}
			}

			if (event.getItem().getType().equals(Material.INK_SACK)) {
				//if (event.getItem().getDurability() == 15) { 
					event.setCancelled(true);
					return;
				//}
			}
		}
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void OnPlayerInteractEvent(PlayerInteractEvent event) {
		Resident resident = CivGlobal.getResident(event.getPlayer());

		if (resident == null) {
			event.setCancelled(true);
			return;
		}
		
		if (event.isCancelled()) {
			// Fix for bucket bug.
			if (event.getAction() == Action.RIGHT_CLICK_AIR) {
				Integer item = ItemManager.getId(event.getPlayer().getItemInHand());
				// block cheats for placing water/lava/fire/lighter use.
				if (item == 326 || item == 327 || item == 259 || (item >= 8 && item <= 11) || item == 51) { 
					event.setCancelled(true);
				}
			}
			return;
		}		
		
		if (event.hasItem()) {

			if (event.getItem().getType().equals(Material.POTION)) {
				int effect = event.getItem().getDurability() & 0x000F;			
				if (effect == 0xE) {
					event.setCancelled(true);
					CivMessage.sendError(event.getPlayer(), "You cannot use invisibility potions for now.. Sorry.");
					return;
				}
			}

			if (event.getItem().getType().equals(Material.INK_SACK) && event.getItem().getDurability() == 15) {
				Block clickedBlock = event.getClickedBlock();
				if (ItemManager.getId(clickedBlock) == CivData.WHEAT || 
					ItemManager.getId(clickedBlock) == CivData.CARROTS || 
					ItemManager.getId(clickedBlock) == CivData.POTATOES) {
					event.setCancelled(true);
					CivMessage.sendError(event.getPlayer(), "You cannot use bone meal on carrots, wheat, or potatoes.");
					return;
				}
			}
		}

		Block soilBlock = event.getPlayer().getLocation().getBlock().getRelative(BlockFace.DOWN);

		// prevent players trampling crops
		if ((event.getAction() == Action.PHYSICAL)) {
			if ((soilBlock.getType() == Material.SOIL) || (soilBlock.getType() == Material.CROPS)) {
				//CivLog.debug("no crop cancel.");
				event.setCancelled(true);
				return;	
			}
		}

		/* 
		 * Right clicking causes some dupe bugs for some reason with items that have "actions" such as swords.
		 * It also causes block place events on top of signs. So we'll just only allow signs to work with left click.
		 */
		boolean leftClick = event.getAction().equals(Action.LEFT_CLICK_AIR) || event.getAction().equals(Action.LEFT_CLICK_BLOCK);

		if (event.getClickedBlock() != null) {		

			if (MarkerPlacementManager.isPlayerInPlacementMode(event.getPlayer())) {
				Block block;
				if (event.getBlockFace().equals(BlockFace.UP)) {
					block = event.getClickedBlock().getRelative(event.getBlockFace());
				} else {
					block = event.getClickedBlock();
				}

				try {
					MarkerPlacementManager.setMarker(event.getPlayer(), block.getLocation());
					CivMessage.send(event.getPlayer(), CivColor.LightGreen+"Marked Location.");
				} catch (CivException e) {
					CivMessage.send(event.getPlayer(), CivColor.Rose+e.getMessage());
				}

				event.setCancelled(true);
				return;
			}

			// Check for clicked structure signs.
			bcoord.setFromLocation(event.getClickedBlock().getLocation());
			StructureSign sign = CivGlobal.getStructureSign(bcoord);
			if (sign != null) {
				if (leftClick || sign.isAllowRightClick()) {
					if (sign.getOwner() != null && sign.getOwner().isActive()) {
						try {
							sign.getOwner().processSignAction(event.getPlayer(), sign, event);
							event.setCancelled(true);
						} catch (CivException e) {
							CivMessage.send(event.getPlayer(), CivColor.Rose+e.getMessage());
							event.setCancelled(true);
							return;
						}
					}
				}
				return;
			}

			if (CivSettings.switchItems.contains(event.getClickedBlock().getType())) {
				OnPlayerSwitchEvent(event);
				if (event.isCancelled()) {
					return;
				}
			}
		}

		if (event.hasItem()) {

			if (event.getItem() == null) {
			} else {
				if (CivSettings.restrictedItems.containsKey(event.getItem().getType())) {
					OnPlayerUseItem(event);
					if (event.isCancelled()) {
						return;
					}
				}
			}
		}

	}
	
	public void OnPlayerBedEnterEvent(PlayerBedEnterEvent event) {
		
		Resident resident = CivGlobal.getResident(event.getPlayer().getName());

		if (resident == null) {
			event.setCancelled(true);
			return;
		}
				
		coord.setFromLocation(event.getPlayer().getLocation());
		Camp camp = CivGlobal.getCampFromChunk(coord);
		if (camp != null) {
			if (!camp.hasMember(event.getPlayer().getName())) {
				CivMessage.sendError(event.getPlayer(), "You cannot sleep in a camp you do not belong to.");
				event.setCancelled(true);
				return;
			}
		}		
	}

	public static void OnPlayerSwitchEvent(PlayerInteractEvent event) {

		if (event.getClickedBlock() == null) {
			return;
		}

		Resident resident = CivGlobal.getResident(event.getPlayer().getName());

		if (resident == null) {
			event.setCancelled(true);
			return;
		}

		bcoord.setFromLocation(event.getClickedBlock().getLocation());
		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null && !resident.isPermOverride()) {
			if (!cb.getCamp().hasMember(resident.getName())) {
				CivMessage.sendError(event.getPlayer(), "You cannot interact with a camp you do not belong to.");
				event.setCancelled(true);
				return;
			}
		}

		coord.setFromLocation(event.getClickedBlock().getLocation());
		TownChunk tc = CivGlobal.getTownChunk(coord);

		if (tc == null) {
			return;
		}

		if (resident.hasTown()) {
			if (War.isWarTime()) {
				if(tc.getTown().getCiv().getDiplomacyManager().atWarWith(resident.getTown().getCiv())) {

					switch (event.getClickedBlock().getType()) {
					case WOODEN_DOOR:
					case IRON_DOOR:
						return;
					default:
						break;
					}
				}
			}
		}

		event.getClickedBlock().getType();

		if(!tc.perms.hasPermission(PlotPermissions.Type.INTERACT, resident)) {
			event.setCancelled(true);

			if (War.isWarTime() && resident.hasTown() && 
					resident.getTown().getCiv().getDiplomacyManager().atWarWith(tc.getTown().getCiv())) {
				WarRegen.destroyThisBlock(event.getClickedBlock(), tc.getTown());
			} else {
				CivMessage.sendErrorNoRepeat(event.getPlayer(), "You do not have permission to interact with "+event.getClickedBlock().getType().toString()+" here.");
			}
		}

		return;
	}

	private void OnPlayerUseItem(PlayerInteractEvent event) {
		Location loc = (event.getClickedBlock() == null) ? 
				event.getPlayer().getLocation() : 
				event.getClickedBlock().getLocation();

		ItemStack stack = event.getItem();

		coord.setFromLocation(event.getPlayer().getLocation());
		Camp camp = CivGlobal.getCampFromChunk(coord);
		if (camp != null) {
			if (!camp.hasMember(event.getPlayer().getName())) {
				CivMessage.sendError(event.getPlayer(), "You cannot use "+stack.getType().toString()+" in a camp you do not belong to.");
				event.setCancelled(true);
				return;
			}
		}

		TownChunk tc = CivGlobal.getTownChunk(loc);
		if (tc == null) {
			return;
		}

		Resident resident = CivGlobal.getResident(event.getPlayer().getName());

		if (resident == null) {
			event.setCancelled(true);
		}

		if(!tc.perms.hasPermission(PlotPermissions.Type.ITEMUSE, resident)) {
			event.setCancelled(true);
			CivMessage.sendErrorNoRepeat(event.getPlayer(), "You do not have permission to use "+stack.getType().toString()+" here.");
		}

		return;
	}

	/*
	 * Handles rotating of itemframes
	 */
	@EventHandler(priority = EventPriority.HIGHEST)
	public void OnPlayerInteractEntityEvent(PlayerInteractEntityEvent event) {

		if (event.getRightClicked().getType().equals(EntityType.HORSE)) {
			if (!HorseModifier.isCivCraftHorse((LivingEntity)event.getRightClicked())) {
				CivMessage.sendError(event.getPlayer(), "Invalid horse! You can only get horses from stable structures.");
				event.setCancelled(true);
				event.getRightClicked().remove();
				return;
			}
		}

		ItemStack inHand = event.getPlayer().getItemInHand();
			if (inHand != null) {

				boolean denyBreeding = false;
				switch (event.getRightClicked().getType()) {
				case COW:
				case SHEEP:
				case MUSHROOM_COW:
					if (inHand.getType().equals(Material.WHEAT)) {
						denyBreeding = true;
					}
					break;
				case PIG:
					if (inHand.getType().equals(Material.CARROT_ITEM)) {
						denyBreeding = true;
					}
					break;
				case HORSE:
					if (inHand.getType().equals(Material.GOLDEN_APPLE) ||
							inHand.getType().equals(Material.GOLDEN_CARROT)) {
						CivMessage.sendError(event.getPlayer(), "You cannot breed horses, buy them from the stable.");
						event.setCancelled(true);
						return;
					}
					break;
				case CHICKEN:
					if (inHand.getType().equals(Material.SEEDS) ||
						inHand.getType().equals(Material.MELON_SEEDS) ||
						inHand.getType().equals(Material.PUMPKIN_SEEDS)) {
						denyBreeding = true;
					}
					break;
				default:
					break;
				}

				if (denyBreeding) {
					ChunkCoord coord = new ChunkCoord(event.getPlayer().getLocation());
					Pasture pasture = Pasture.pastureChunks.get(coord);

					if (pasture == null) {
						CivMessage.sendError(event.getPlayer(), "You cannot breed mobs in the wild, take them to a pasture.");
						event.setCancelled(true);
					} else {
							int loveTicks;
							NBTTagCompound tag = new NBTTagCompound();
							((CraftEntity)event.getRightClicked()).getHandle().c(tag);
							loveTicks = tag.getInt("InLove");

							if (loveTicks == 0) {	
								if(!pasture.processMobBreed(event.getPlayer(), event.getRightClicked().getType())) {
									event.setCancelled(true);
								}
							} else {
								event.setCancelled(true);
							}
					}

					return;			
				}
			}
		if (!(event.getRightClicked() instanceof ItemFrame) && !(event.getRightClicked() instanceof Painting)) {
			return;
		}

		coord.setFromLocation(event.getPlayer().getLocation());
		TownChunk tc = CivGlobal.getTownChunk(coord);
		if (tc == null) {
			return;
		}

		Resident resident = CivGlobal.getResident(event.getPlayer().getName());
		if (resident == null) {
			return;
		}

		if(!tc.perms.hasPermission(PlotPermissions.Type.INTERACT, resident)) {
			event.setCancelled(true);
			CivMessage.sendErrorNoRepeat(event.getPlayer(), "You do not have permission to interact with this painting/itemframe.");
		}

	}


	/*
	 * Handles breaking of paintings and itemframes.
	 */
	@EventHandler(priority = EventPriority.NORMAL)
	public void OnHangingBreakByEntityEvent(HangingBreakByEntityEvent event) {	
	//	CivLog.debug("hanging painting break event");

		ItemFrameStorage frameStore = CivGlobal.getProtectedItemFrame(event.getEntity().getUniqueId());
		if (frameStore != null) {		
//			if (!(event.getRemover() instanceof Player)) {
//				event.setCancelled(true);
//				return;
//			}
//			
//			if (frameStore.getTown() != null) {
//				Resident resident = CivGlobal.getResident((Player)event.getRemover());
//				if (resident == null) {
//					event.setCancelled(true);
//					return;
//				}
//				
//				if (resident.hasTown() == false || resident.getTown() != frameStore.getTown()) {
//					event.setCancelled(true);
//					CivMessage.sendError((Player)event.getRemover(), "Cannot remove item from protected item frame. Belongs to another town.");
//					return;
//				}
//			}
//			
//			CivGlobal.checkForEmptyDuplicateFrames(frameStore);
//			
//			ItemStack stack = ((ItemFrame)event.getEntity()).getItem();
//			if (stack != null && !stack.getType().equals(Material.AIR)) {
//				BonusGoodie goodie = CivGlobal.getBonusGoodie(stack);
//				if (goodie != null) {
//					frameStore.getTown().onGoodieRemoveFromFrame(frameStore, goodie);
//				}
//				frameStore.clearItem();
//				TaskMaster.syncTask(new DelayItemDrop(stack, event.getEntity().getLocation()));
//			}
			if (event.getRemover() instanceof Player) {
				CivMessage.sendError((Player)event.getRemover(), "Cannot break protected item frames. Right click to interact instead.");
			}
			event.setCancelled(true);	
			return;
		}

		if (event.getRemover() instanceof Player) {
			Player player = (Player)event.getRemover();

			coord.setFromLocation(player.getLocation());
			TownChunk tc = CivGlobal.getTownChunk(coord);

			if (tc == null) {
				return;
			}

			Resident resident = CivGlobal.getResident(player.getName());
			if (resident == null) {
				event.setCancelled(true);
			}

			if (!tc.perms.hasPermission(PlotPermissions.Type.DESTROY, resident)) {
				event.setCancelled(true);
				CivMessage.sendErrorNoRepeat(player, "You do not have permission to destroy here.");
			}
		}


	}

	@EventHandler(priority = EventPriority.HIGH)
	public void onChunkUnloadEvent(ChunkUnloadEvent event) {
		Boolean persist = CivGlobal.isPersistChunk(event.getChunk());		
		if (persist != null && persist == true) {
			event.setCancelled(true);
		}
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void onChunkLoadEvent(ChunkLoadEvent event) {
		ChunkCoord coord = new ChunkCoord(event.getChunk());
		FarmChunk fc = CivGlobal.getFarmChunk(coord);
		if (fc == null) {
			return;
		}

		for (org.bukkit.entity.Entity ent : event.getChunk().getEntities()) {
			if (ent.getType().equals(EntityType.ZOMBIE)) {
				ent.remove();
			}
		}

		class AsyncTask extends CivAsyncTask {

			FarmChunk fc;
			public AsyncTask(FarmChunk fc) {
				this.fc = fc;
			}

			@Override
			public void run() {
				if (fc.getMissedGrowthTicks() > 0) {
					fc.processMissedGrowths(false, this);
					fc.getFarm().saveMissedGrowths();
				}
			}

		}

		TaskMaster.syncTask(new AsyncTask(fc), 500);
	}

	@EventHandler(priority = EventPriority.MONITOR)
	public void onEntityDeath(EntityDeathEvent event) {

		Pasture pasture = Pasture.pastureEntities.get(event.getEntity().getUniqueId());
		if (pasture != null) {
			pasture.onEntityDeath(event.getEntity());
		}


		if (!ConfigTempleSacrifice.isValidEntity(event.getEntityType())) {
			return;
		}

		/* Check if we're 'inside' a temple. */
		bcoord.setFromLocation(event.getEntity().getLocation());
		HashSet<Buildable> buildables = CivGlobal.getBuildablesAt(bcoord);
		if (buildables == null) {
			return;
		}

		for (Buildable buildable : buildables) {
			if (buildable instanceof Temple) {
				if (buildable.getCorner().getY() <= event.getEntity().getLocation().getBlockY()) {
					/* We're 'above' the temple. Good enough. */
					((Temple)buildable).onEntitySacrifice(event.getEntityType());
				}
			}
		}
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void onBlockGrowEvent(BlockGrowEvent event) {
		bcoord.setFromLocation(event.getBlock().getLocation().add(0, -1, 0));
		if (CivGlobal.vanillaGrowthLocations.contains(bcoord)) {
			/* Allow vanilla growth on these plots. */
			return;
		}

		Block b = event.getBlock();

		if (Farm.isBlockControlled(b)) {
			event.setCancelled(true);
		}
	}

	@EventHandler(priority = EventPriority.HIGH)
	public void onEntityBreakDoor(EntityBreakDoorEvent event) {
		bcoord.setFromLocation(event.getBlock().getLocation());
		StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
		if (sb != null) {
			event.setCancelled(true);
		}

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null) {
			event.setCancelled(true);
		}
	}

	@EventHandler(priority = EventPriority.NORMAL)
	public void onCreatureSpawnEvent(CreatureSpawnEvent event) {
		if (War.isWarTime()) {
			if (!event.getSpawnReason().equals(SpawnReason.BREEDING)){
				event.setCancelled(true);
				return;
			}
		}
		
		if (event.getEntity().getType().equals(EntityType.CHICKEN)) {
			if (event.getSpawnReason().equals(SpawnReason.EGG)) {
				event.setCancelled(true);
				return;
			}
			NBTTagCompound compound = new NBTTagCompound();
			if (compound.getBoolean("IsChickenJockey")) {
				event.setCancelled(true);
				return;			
			}
		}

		if (event.getEntity().getType().equals(EntityType.IRON_GOLEM) &&
			event.getSpawnReason().equals(SpawnReason.BUILD_IRONGOLEM)) {
				event.setCancelled(true);
				return;
		}

		if (MobLib.isMobLibEntity(event.getEntity())) {
			return;
		}

		if (event.getEntity().getType().equals(EntityType.ZOMBIE) ||
			event.getEntity().getType().equals(EntityType.SKELETON) ||
			event.getEntity().getType().equals(EntityType.BAT) ||
			event.getEntity().getType().equals(EntityType.CAVE_SPIDER) ||
			event.getEntity().getType().equals(EntityType.SPIDER) ||
			event.getEntity().getType().equals(EntityType.CREEPER) ||
			event.getEntity().getType().equals(EntityType.WOLF) ||
			event.getEntity().getType().equals(EntityType.SILVERFISH) ||
			event.getEntity().getType().equals(EntityType.OCELOT) ||
			event.getEntity().getType().equals(EntityType.WITCH) ||
			event.getEntity().getType().equals(EntityType.ENDERMAN)) {

			event.setCancelled(true);
			return;
		}

		if (event.getSpawnReason().equals(SpawnReason.SPAWNER)) {
			event.setCancelled(true);
			return;
		}
	}

	public boolean allowPistonAction(Location loc) {
		bcoord.setFromLocation(loc);
		StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
		if (sb != null) {
			return false;
		}

		RoadBlock rb = CivGlobal.getRoadBlock(bcoord);
		if (rb != null) {
			return false;
		}

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null) {
			return false;
		}

		/* 
		 * If we're next to an attached protected item frame. Disallow 
		 * we cannot break protected item frames.
		 * 
		 * Only need to check blocks directly next to us.
		 */
		BlockCoord bcoord2 = new BlockCoord(bcoord);
		bcoord2.setX(bcoord.getX() - 1);
		if (ItemFrameStorage.attachedBlockMap.containsKey(bcoord2)) {
			return false;
		}

		bcoord2.setX(bcoord.getX() + 1);
		if (ItemFrameStorage.attachedBlockMap.containsKey(bcoord2)) {
			return false;
		}

		bcoord2.setZ(bcoord.getZ() - 1);
		if (ItemFrameStorage.attachedBlockMap.containsKey(bcoord2)) {
			return false;
		}

		bcoord2.setZ(bcoord.getZ() + 1);
		if (ItemFrameStorage.attachedBlockMap.containsKey(bcoord2)) {
			return false;
		}

		coord.setFromLocation(loc);
		HashSet<Wall> walls = CivGlobal.getWallChunk(coord);

		if (walls != null) {
			for (Wall wall : walls) {
				if (wall.isProtectedLocation(loc)) {
					return false;
				}
			}
		}		

		return true;
	}

	@EventHandler(priority = EventPriority.HIGHEST) 
	public void onBlockPistonExtendEvent(BlockPistonExtendEvent event) {

		/* UGH. If we extend into 'air' it doesnt count them as blocks...
		 * we need to check air to prevent breaking of item frames...
		 */
		final int PISTON_EXTEND_LENGTH = 13;
		Block currentBlock = event.getBlock().getRelative(event.getDirection());
		for (int i = 0; i < PISTON_EXTEND_LENGTH; i++) {
			if(ItemManager.getId(currentBlock) == CivData.AIR) {
				if (!allowPistonAction(currentBlock.getLocation())) {
					event.setCancelled(true);
					return;
				}
			}

			currentBlock = currentBlock.getRelative(event.getDirection());
		}
		
		if (War.isWarTime()) {
			event.setCancelled(true);
			return;
		}

//		if (event.getBlocks().size() == 0) {
//			Block extendInto = event.getBlock().getRelative(event.getDirection());
//			if (!allowPistonAction(extendInto.getLocation())) {
//				event.setCancelled(true);
//				return;
//			}
//		}
		coord.setFromLocation(event.getBlock().getLocation());
		FarmChunk fc = CivGlobal.getFarmChunk(coord);
		if (fc == null) {
			event.setCancelled(true);
			
		}
		
		for (Block block : event.getBlocks()) {
			if (!allowPistonAction(block.getLocation())) {
				event.setCancelled(true);
				break;

			}
		}

	}

	@EventHandler(priority = EventPriority.HIGHEST) 
	public void onBlockPistonRetractEvent(BlockPistonRetractEvent event) {
		if (!allowPistonAction(event.getRetractLocation())) {
			event.setCancelled(true);
			return;
		}
	}

	@EventHandler(priority = EventPriority.HIGHEST) 
	public void onPotionSplashEvent(PotionSplashEvent event) {
		ThrownPotion potion = event.getPotion();

		if (!(potion.getShooter() instanceof Player)) {
			return;
		} 

		Player attacker = (Player)potion.getShooter();

		for (PotionEffect effect : potion.getEffects()) {
			if (effect.getType().equals(PotionEffectType.INVISIBILITY)) {
				event.setCancelled(true);
				return;
			}
		}

		boolean protect = false;
		for (PotionEffect effect : potion.getEffects()) {
			if (effect.getType().equals(PotionEffectType.BLINDNESS) ||
				effect.getType().equals(PotionEffectType.CONFUSION) ||
				effect.getType().equals(PotionEffectType.HARM) ||
				effect.getType().equals(PotionEffectType.POISON) ||
				effect.getType().equals(PotionEffectType.SLOW) ||
				effect.getType().equals(PotionEffectType.SLOW_DIGGING) ||
				effect.getType().equals(PotionEffectType.WEAKNESS) ||
				effect.getType().equals(PotionEffectType.WITHER)) {

				protect = true;
				break;
			}
		}

		if (!protect) {
			return;
		}

		for (LivingEntity entity : event.getAffectedEntities()) {
			if (entity instanceof Player) {
				Player defender = (Player)entity;
				coord.setFromLocation(entity.getLocation());
				TownChunk tc = CivGlobal.getTownChunk(coord);
				if (tc == null) {
					continue;
				}

				switch(playersCanPVPHere(attacker, defender, tc)) {
				case ALLOWED:
					continue;
				case NOT_AT_WAR:
					CivMessage.send(attacker, CivColor.Rose+"You cannot use potions against "+defender.getName()+". You are not at war.");
					event.setCancelled(true);
					return;
				case NEUTRAL_IN_WARZONE:
					CivMessage.send(attacker, CivColor.Rose+"You cannot use potions against "+defender.getName()+". You a neutral in a war-zone.");
					event.setCancelled(true);
					return;
				case NON_PVP_ZONE:
					CivMessage.send(attacker, CivColor.Rose+"You cannot use potions against "+defender.getName()+". You are in a non-pvp zone.");
					event.setCancelled(true);
					return;
				}
			}
		}
	}

	@EventHandler(priority = EventPriority.NORMAL) 
	public void onBlockRedstoneEvent(BlockRedstoneEvent event) {
		
		bcoord.setFromLocation(event.getBlock().getLocation());

		CampBlock cb = CivGlobal.getCampBlock(bcoord);
		if (cb != null) {
			if (ItemManager.getId(event.getBlock()) == CivData.WOOD_DOOR ||
					ItemManager.getId(event.getBlock()) == CivData.IRON_DOOR) {
				event.setNewCurrent(0);
				return;
			}
		}
		
		if (War.isWarTime()) {
			event.setNewCurrent(0);
			return;
		}

	}

	private enum PVPDenyReason {
		ALLOWED,
		NON_PVP_ZONE,
		NOT_AT_WAR,
		NEUTRAL_IN_WARZONE
	}

	private PVPDenyReason playersCanPVPHere(Player attacker, Player defender, TownChunk tc) {
		Resident defenderResident = CivGlobal.getResident(defender);
		Resident attackerResident = CivGlobal.getResident(attacker);
		PVPDenyReason reason = PVPDenyReason.NON_PVP_ZONE;

		/* Outlaws can only pvp each other if they are declared at this location. */
		if (CivGlobal.isOutlawHere(defenderResident, tc) || 
			CivGlobal.isOutlawHere(attackerResident, tc)) {
			return PVPDenyReason.ALLOWED;
		}

		/* 
		 * If it is WarTime and the town we're in is at war, allow neutral players to be 
		 * targeted by anybody.
		 */
		if (War.isWarTime()) {
			if (tc.getTown().getCiv().getDiplomacyManager().isAtWar()) {
				/* 
				 * The defender is neutral if he is not in a town/civ, or not in his own civ AND not 'at war'
				 * with the attacker.
				 */
				if (!defenderResident.hasTown() || (!defenderResident.getTown().getCiv().getDiplomacyManager().atWarWith(tc.getTown().getCiv()) && 
						defenderResident.getTown().getCiv() != tc.getTown().getCiv())) {
					/* Allow neutral players to be hurt, but not hurt them back. */
					return PVPDenyReason.ALLOWED;
				} else if (!attackerResident.hasTown() || (!attackerResident.getTown().getCiv().getDiplomacyManager().atWarWith(tc.getTown().getCiv()) &&
						attackerResident.getTown().getCiv() != tc.getTown().getCiv())) {
					reason = PVPDenyReason.NEUTRAL_IN_WARZONE;
				}
			}
		}

		boolean defenderAtWarWithAttacker = false;
		if (defenderResident != null && defenderResident.hasTown()) {
			defenderAtWarWithAttacker = defenderResident.getTown().getCiv().getDiplomacyManager().atWarWith(attacker);
			/* 
			 * If defenders are at war with attackers allow PVP. Location doesnt matter. Allies should be able to help
			 * defend each other regardless of where they are currently located.
			 */
			if (defenderAtWarWithAttacker) {
				//if (defenderResident.getTown().getCiv() == tc.getTown().getCiv() ||
				//	attackerResident.getTown().getCiv() == tc.getTown().getCiv()) {
					return PVPDenyReason.ALLOWED;
				//}
			} else if (reason.equals(PVPDenyReason.NON_PVP_ZONE)) {
				reason = PVPDenyReason.NOT_AT_WAR;
			}
		}

		return reason;
	}

	@EventHandler(priority = EventPriority.LOW)
	public void onEntityPortalCreate(EntityCreatePortalEvent event) {
		event.setCancelled(true);
	}

}