package nallar.tickthreading.minecraft.tickregion;

import java.util.Iterator;

import nallar.collections.LinkedHashSetTempSetNoClear;
import nallar.tickthreading.Log;
import nallar.tickthreading.minecraft.TickManager;
import nallar.tickthreading.minecraft.profiling.EntityTickProfiler;
import nallar.unsafe.UnsafeAccess;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.ChunkProviderServer;
import sun.misc.Unsafe;

public class EntityTickRegion extends TickRegion {
	private final LinkedHashSetTempSetNoClear<Entity> entitySet = new LinkedHashSetTempSetNoClear<Entity>();

	public EntityTickRegion(World world, TickManager manager, int regionX, int regionZ) {
		super(world, manager, regionX, regionZ);
	}

	@Override
	public void doTick() {
		ChunkProviderServer chunkProvider = (ChunkProviderServer) world.getChunkProvider();
		boolean profilingEnabled = manager.profilingEnabled || this.profilingEnabled;
		EntityTickProfiler entityTickProfiler = profilingEnabled ? EntityTickProfiler.ENTITY_TICK_PROFILER : null;
		long startTime = 0;
		Iterator<Entity> entitiesIterator = entitySet.startIteration();
		try {
			while (entitiesIterator.hasNext()) {
				if (profilingEnabled) {
					startTime = System.nanoTime();
				}
				Entity entity = entitiesIterator.next();
				try {
					Entity ridingEntity = entity.ridingEntity;
					if (ridingEntity != null) {
						if (!ridingEntity.isDead && ridingEntity.riddenByEntity == entity) {
							continue;
						}

						ridingEntity.riddenByEntity = null;
						entity.ridingEntity = null;
					}

					if (!entity.isDead) {
						if (entity instanceof EntityPlayerMP) {
							Unsafe $ = UnsafeAccess.$;
							Object lock = ((EntityPlayerMP) entity).playerNetServerHandler;
							if ($.tryMonitorEnter(lock)) {
								try {
									world.updateEntity(entity);
								} finally {
									$.monitorExit(lock);
								}
							}
						} else {
							world.updateEntity(entity);
						}
					}

					if (entity.isDead) {
						int entityX = entity.chunkCoordX;
						int entityZ = entity.chunkCoordZ;

						if (entity.addedToChunk) {
							Chunk chunk = chunkProvider.getChunkIfExists(entityX, entityZ);
							if (chunk != null) {
								chunk.removeEntity(entity);
							}
						}

						entitiesIterator.remove();
						manager.removed(entity);
						world.releaseEntitySkin(entity);
					} else if (manager.getHashCode(entity) != hashCode) {
						entitiesIterator.remove();
						manager.add(entity, false);
						//Log.severe("Inconsistent state: " + entity + " is in the wrong TickRegion.");
						// Note to self for when I decide this is wrong later:
						// Entities are supposed to move, of course this will happen!
					}
				} catch (Throwable throwable) {
					Log.severe("Exception ticking entity " + entity + " in " + toString() + '/' + Log.name(entity.worldObj) + ':', throwable);
					if (entity.worldObj != world) {
						Log.severe("Seems to be caused by an entity being in a broken state, set to an impossible/incorrect world. Killing this entity.");
						entity.setDead();
					}
				}
				if (profilingEnabled) {
					entityTickProfiler.record(entity, System.nanoTime() - startTime);
				}
			}
		} finally {
			entitySet.done();
		}
	}

	@Override
	protected String getShortTypeName() {
		return "E";
	}

	public boolean add(Entity entity) {
		return entitySet.add(entity);
	}

	public boolean remove(Entity entity) {
		return entitySet.remove(entity);
	}

	@Override
	public boolean isEmpty() {
		return entitySet.isEmpty();
	}

	@Override
	public int size() {
		return entitySet.size();
	}

	@Override
	public void die() {
		entitySet.clear();
	}
}

@@ -2254,24 +2254,56 @@
 kCoordZ;%0D%0A%0D%0A
+%09%09%09%09%09%09synchronized (entity) %7B%0D%0A%09
 %09%09%09%09%09%09if (en
@@ -2331,16 +2331,17 @@
 %0A%09%09%09%09%09%09%09
+%09
 Chunk ch
@@ -2346,16 +2346,70 @@
 chunk = 
+entity.chunk;%0D%0A%09%09%09%09%09%09%09%09if (chunk == null) %7B%0D%0A%09%09%09%09%09%09%09%09%09
 chunkPro
@@ -2447,24 +2447,36 @@
  entityZ);%0D%0A
+%09%09%09%09%09%09%09%09%7D%0D%0A%09
 %09%09%09%09%09%09%09if (c
@@ -2484,32 +2484,33 @@
 unk != null) %7B%0D%0A
+%09
 %09%09%09%09%09%09%09%09chunk.re
@@ -2526,24 +2526,35 @@
 y(entity);%0D%0A
+%09%09%09%09%09%09%09%09%7D%0D%0A
 %09%09%09%09%09%09%09%7D%0D%0A%09%09

Use entity chunk field when removing dead entities in EntityTickRegion