package net.minecraft.entity;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Callable;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.boss.EntityDragon;
import net.minecraft.entity.boss.EntityWither;
import net.minecraft.entity.item.EntityBoat;
import net.minecraft.entity.item.EntityEnderCrystal;
import net.minecraft.entity.item.EntityEnderEye;
import net.minecraft.entity.item.EntityEnderPearl;
import net.minecraft.entity.item.EntityExpBottle;
import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.entity.item.EntityFireworkRocket;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.item.EntityMinecart;
import net.minecraft.entity.item.EntityTNTPrimed;
import net.minecraft.entity.item.EntityXPOrb;
import net.minecraft.entity.passive.EntityBat;
import net.minecraft.entity.passive.EntitySquid;
import net.minecraft.entity.passive.IAnimals;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.entity.projectile.EntityEgg;
import net.minecraft.entity.projectile.EntityFireball;
import net.minecraft.entity.projectile.EntityFishHook;
import net.minecraft.entity.projectile.EntityPotion;
import net.minecraft.entity.projectile.EntitySmallFireball;
import net.minecraft.entity.projectile.EntitySnowball;
import net.minecraft.network.Packet;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.ReportedException;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import cpw.mods.fml.common.registry.EntityRegistry;

public class EntityTracker
{
	private static final Logger logger = LogManager.getLogger();
	private final WorldServer theWorld;
	private Set trackedEntities = new HashSet();
	private IntHashMap trackedEntityIDs = new IntHashMap();
	private int entityViewDistance;
	private static final String __OBFID = "CL_00001431";

	public EntityTracker(WorldServer par1WorldServer)
	{
		this.theWorld = par1WorldServer;
		this.entityViewDistance = par1WorldServer.func_73046_m().getConfigurationManager().getEntityViewDistance();
	}

	public void addEntityToTracker(Entity par1Entity)
	{
		if (EntityRegistry.instance().tryTrackingEntity(this, par1Entity))
		{
			return;
		}

		if (par1Entity instanceof EntityPlayerMP)
		{
			this.addEntityToTracker(par1Entity, 512, 2);
			EntityPlayerMP entityplayermp = (EntityPlayerMP)par1Entity;
			Iterator iterator = this.trackedEntities.iterator();

			while (iterator.hasNext())
			{
				EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)iterator.next();

				if (entitytrackerentry.myEntity != entityplayermp)
				{
					entitytrackerentry.tryStartWachingThis(entityplayermp);
				}
			}
		}
		else if (par1Entity instanceof EntityFishHook)
		{
			this.addEntityToTracker(par1Entity, 64, 5, true);
		}
		else if (par1Entity instanceof EntityArrow)
		{
			this.addEntityToTracker(par1Entity, 64, 20, false);
		}
		else if (par1Entity instanceof EntitySmallFireball)
		{
			this.addEntityToTracker(par1Entity, 64, 10, false);
		}
		else if (par1Entity instanceof EntityFireball)
		{
			this.addEntityToTracker(par1Entity, 64, 10, false);
		}
		else if (par1Entity instanceof EntitySnowball)
		{
			this.addEntityToTracker(par1Entity, 64, 10, true);
		}
		else if (par1Entity instanceof EntityEnderPearl)
		{
			this.addEntityToTracker(par1Entity, 64, 10, true);
		}
		else if (par1Entity instanceof EntityEnderEye)
		{
			this.addEntityToTracker(par1Entity, 64, 4, true);
		}
		else if (par1Entity instanceof EntityEgg)
		{
			this.addEntityToTracker(par1Entity, 64, 10, true);
		}
		else if (par1Entity instanceof EntityPotion)
		{
			this.addEntityToTracker(par1Entity, 64, 10, true);
		}
		else if (par1Entity instanceof EntityExpBottle)
		{
			this.addEntityToTracker(par1Entity, 64, 10, true);
		}
		else if (par1Entity instanceof EntityFireworkRocket)
		{
			this.addEntityToTracker(par1Entity, 64, 10, true);
		}
		else if (par1Entity instanceof EntityItem)
		{
			this.addEntityToTracker(par1Entity, 64, 20, true);
		}
		else if (par1Entity instanceof EntityMinecart)
		{
			this.addEntityToTracker(par1Entity, 80, 3, true);
		}
		else if (par1Entity instanceof EntityBoat)
		{
			this.addEntityToTracker(par1Entity, 80, 3, true);
		}
		else if (par1Entity instanceof EntitySquid)
		{
			this.addEntityToTracker(par1Entity, 64, 3, true);
		}
		else if (par1Entity instanceof EntityWither)
		{
			this.addEntityToTracker(par1Entity, 80, 3, false);
		}
		else if (par1Entity instanceof EntityBat)
		{
			this.addEntityToTracker(par1Entity, 80, 3, false);
		}
		else if (par1Entity instanceof IAnimals)
		{
			this.addEntityToTracker(par1Entity, 80, 3, true);
		}
		else if (par1Entity instanceof EntityDragon)
		{
			this.addEntityToTracker(par1Entity, 160, 3, true);
		}
		else if (par1Entity instanceof EntityTNTPrimed)
		{
			this.addEntityToTracker(par1Entity, 160, 10, true);
		}
		else if (par1Entity instanceof EntityFallingBlock)
		{
			this.addEntityToTracker(par1Entity, 160, 20, true);
		}
		else if (par1Entity instanceof EntityHanging)
		{
			this.addEntityToTracker(par1Entity, 160, Integer.MAX_VALUE, false);
		}
		else if (par1Entity instanceof EntityXPOrb)
		{
			this.addEntityToTracker(par1Entity, 160, 20, true);
		}
		else if (par1Entity instanceof EntityEnderCrystal)
		{
			this.addEntityToTracker(par1Entity, 256, Integer.MAX_VALUE, false);
		}
	}

	public void addEntityToTracker(Entity par1Entity, int par2, int par3)
	{
		this.addEntityToTracker(par1Entity, par2, par3, false);
	}

	public void addEntityToTracker(Entity par1Entity, int par2, final int par3, boolean par4)
	{
		if (par2 > this.entityViewDistance)
		{
			par2 = this.entityViewDistance;
		}

		try
		{
			if (this.trackedEntityIDs.containsItem(par1Entity.getEntityId()))
			{
				throw new IllegalStateException("Entity is already tracked!");
			}

			EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(par1Entity, par2, par3, par4);
			this.trackedEntities.add(entitytrackerentry);
			this.trackedEntityIDs.addKey(par1Entity.getEntityId(), entitytrackerentry);
			entitytrackerentry.sendEventsToPlayers(this.theWorld.playerEntities);
		}
		catch (Throwable throwable)
		{
			CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Adding entity to track");
			CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity To Track");
			crashreportcategory.addCrashSection("Tracking range", par2 + " blocks");
			crashreportcategory.addCrashSectionCallable("Update interval", new Callable()
			{
				private static final String __OBFID = "CL_00001432";
				public String call()
				{
					String s = "Once per " + par3 + " ticks";

					if (par3 == Integer.MAX_VALUE)
					{
						s = "Maximum (" + s + ")";
					}

					return s;
				}
			});
			par1Entity.addEntityCrashInfo(crashreportcategory);
			CrashReportCategory crashreportcategory1 = crashreport.makeCategory("Entity That Is Already Tracked");
			((EntityTrackerEntry)this.trackedEntityIDs.lookup(par1Entity.getEntityId())).myEntity.addEntityCrashInfo(crashreportcategory1);

			try
			{
				throw new ReportedException(crashreport);
			}
			catch (ReportedException reportedexception)
			{
				logger.error("\"Silently\" catching entity tracking error.", reportedexception);
			}
		}
	}

	public void removeEntityFromAllTrackingPlayers(Entity par1Entity)
	{
		if (par1Entity instanceof EntityPlayerMP)
		{
			EntityPlayerMP entityplayermp = (EntityPlayerMP)par1Entity;
			Iterator iterator = this.trackedEntities.iterator();

			while (iterator.hasNext())
			{
				EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)iterator.next();
				entitytrackerentry.removeFromWatchingList(entityplayermp);
			}
		}

		EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry)this.trackedEntityIDs.removeObject(par1Entity.getEntityId());

		if (entitytrackerentry1 != null)
		{
			this.trackedEntities.remove(entitytrackerentry1);
			entitytrackerentry1.informAllAssociatedPlayersOfItemDestruction();
		}
	}

	public void updateTrackedEntities()
	{
		ArrayList arraylist = new ArrayList();
		Iterator iterator = this.trackedEntities.iterator();

		while (iterator.hasNext())
		{
			EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)iterator.next();
			entitytrackerentry.sendLocationToAllClients(this.theWorld.playerEntities);

			if (entitytrackerentry.playerEntitiesUpdated && entitytrackerentry.myEntity instanceof EntityPlayerMP)
			{
				arraylist.add((EntityPlayerMP)entitytrackerentry.myEntity);
			}
		}

		for (int i = 0; i < arraylist.size(); ++i)
		{
			EntityPlayerMP entityplayermp = (EntityPlayerMP)arraylist.get(i);
			Iterator iterator1 = this.trackedEntities.iterator();

			while (iterator1.hasNext())
			{
				EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry)iterator1.next();

				if (entitytrackerentry1.myEntity != entityplayermp)
				{
					entitytrackerentry1.tryStartWachingThis(entityplayermp);
				}
			}
		}
	}

	public void func_151247_a(Entity p_151247_1_, Packet p_151247_2_)
	{
		EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)this.trackedEntityIDs.lookup(p_151247_1_.getEntityId());

		if (entitytrackerentry != null)
		{
			entitytrackerentry.func_151259_a(p_151247_2_);
		}
	}

	public void func_151248_b(Entity p_151248_1_, Packet p_151248_2_)
	{
		EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)this.trackedEntityIDs.lookup(p_151248_1_.getEntityId());

		if (entitytrackerentry != null)
		{
			entitytrackerentry.func_151261_b(p_151248_2_);
		}
	}

	public void removePlayerFromTrackers(EntityPlayerMP par1EntityPlayerMP)
	{
		Iterator iterator = this.trackedEntities.iterator();

		while (iterator.hasNext())
		{
			EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)iterator.next();
			entitytrackerentry.removePlayerFromTracker(par1EntityPlayerMP);
		}
	}

	public void func_85172_a(EntityPlayerMP par1EntityPlayerMP, Chunk par2Chunk)
	{
		Iterator iterator = this.trackedEntities.iterator();

		while (iterator.hasNext())
		{
			EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry)iterator.next();

			if (entitytrackerentry.myEntity != par1EntityPlayerMP && entitytrackerentry.myEntity.chunkCoordX == par2Chunk.xPosition && entitytrackerentry.myEntity.chunkCoordZ == par2Chunk.zPosition)
			{
				entitytrackerentry.tryStartWachingThis(par1EntityPlayerMP);
			}
		}
	}
	
	/* ======================================== FORGE START =====================================*/
	
	// don't expose the EntityTrackerEntry directly so mods can't mess with the data in there as easily
	/**
	 * Get all players tracking the given Entity. The Entity must be part of the World that this Tracker belongs to.
	 * @param entity the Entity
	 * @return all players tracking the Entity
	 */
	public Set<net.minecraft.entity.player.EntityPlayer> getTrackingPlayers(Entity entity)
	{
		EntityTrackerEntry entry = (EntityTrackerEntry) trackedEntityIDs.lookup(entity.getEntityId());
		if (entry == null)
			return java.util.Collections.emptySet();
		else
			return java.util.Collections.unmodifiableSet(entry.trackingPlayers);
	}
	
	/* ======================================== FORGE END   =====================================*/
}