package common.init;

import java.util.Map;
import java.util.Set;

import common.collect.Maps;
import common.entity.Entity;
import common.entity.animal.EntityBat;
import common.entity.animal.EntityChicken;
import common.entity.animal.EntityCow;
import common.entity.animal.EntityDragon;
import common.entity.animal.EntityFox;
import common.entity.animal.EntityHorse;
import common.entity.animal.EntityMooshroom;
import common.entity.animal.EntityMouse;
import common.entity.animal.EntityOcelot;
import common.entity.animal.EntityPig;
import common.entity.animal.EntityRabbit;
import common.entity.animal.EntitySheep;
import common.entity.animal.EntitySquid;
import common.entity.animal.EntityWolf;
import common.entity.item.EntityBoat;
import common.entity.item.EntityChestCart;
import common.entity.item.EntityCrystal;
import common.entity.item.EntityExplosion;
import common.entity.item.EntityFalling;
import common.entity.item.EntityFireworks;
import common.entity.item.EntityHopperCart;
import common.entity.item.EntityItem;
import common.entity.item.EntityLeashKnot;
import common.entity.item.EntityMinecart;
import common.entity.item.EntityNuke;
import common.entity.item.EntityOrb;
import common.entity.item.EntityTnt;
import common.entity.item.EntityTntCart;
import common.entity.item.EntityXp;
import common.entity.item.EntityXpBottle;
import common.entity.npc.SpeciesInfo;
import common.entity.projectile.EntityArrow;
import common.entity.projectile.EntityBox;
import common.entity.projectile.EntityBullet;
import common.entity.projectile.EntityDie;
import common.entity.projectile.EntityDynamite;
import common.entity.projectile.EntityEgg;
import common.entity.projectile.EntityFireCharge;
import common.entity.projectile.EntityFireball;
import common.entity.projectile.EntityHook;
import common.entity.projectile.EntityPotion;
import common.entity.projectile.EntitySnowball;
import common.entity.types.EntityLiving;
import common.entity.types.IObjectData;
import common.log.Log;
import common.tags.TagObject;
import common.world.World;

public abstract class EntityRegistry {
	private static final Map<String, Class<? extends Entity>> STRING_TO_CLASS = Maps.<String, Class<? extends Entity>>newHashMap();
	private static final Map<Class<? extends Entity>, String> CLASS_TO_STRING = Maps.<Class<? extends Entity>, String>newHashMap();
	private static final Map<Integer, Class<? extends Entity>> ID_TO_CLASS = Maps.<Integer, Class<? extends Entity>>newHashMap();
	private static final Map<Class<? extends Entity>, Integer> CLASS_TO_ID = Maps.<Class<? extends Entity>, Integer>newHashMap();
	public static final Map<String, EntityInfo> SPAWN_EGGS = Maps.<String, EntityInfo>newLinkedHashMap();
	private static final Map<String, String> STRING_TO_NAME = Maps.<String, String>newHashMap();
	
	private static boolean register;
	private static int nextNetId;

	private static int registerEntity(String name, Class<? extends Entity> clazz, String typename) {
		if(clazz == null)
			throw new IllegalArgumentException("Kann keine null-Klasse registrieren");
		if(STRING_TO_CLASS.containsKey(name))
			throw new IllegalArgumentException("Klasse ist bereits registriert: " + name);
		int id = ++nextNetId;
		STRING_TO_CLASS.put(name, clazz);
		CLASS_TO_STRING.put(clazz, name);
		ID_TO_CLASS.put(id, clazz);
		CLASS_TO_ID.put(clazz, id);
		STRING_TO_NAME.put(name, typename);
		return id;
	}

	private static void registerEntity(String name, Class<? extends EntityLiving> clazz, String origin, String typename, int eggColor, int spotColor) {
		if(register) {
			registerEntity(name, clazz, typename);
		}
		else {
			SPAWN_EGGS.put(name, new EntityInfo(name, origin, eggColor, spotColor));
		}
	}

	public static Entity createEntityByName(String entityName, World worldIn) {
		Entity entity = null;

		try {
			Class<? extends Entity> oclass = (Class)STRING_TO_CLASS.get(entityName);

			if(oclass != null) {
				entity = (Entity)oclass.getConstructor(World.class).newInstance(worldIn);
			}
		}
		catch(Exception exception) {
			exception.printStackTrace();
		}

		return entity;
	}

	public static Entity createFromTags(TagObject tag, World world) {
		Entity entity = null;

		try {
			Class<? extends Entity> oclass = (Class)STRING_TO_CLASS.get(tag.getString("id"));

			if(oclass != null) {
				entity = (Entity)oclass.getConstructor(World.class).newInstance(world);
			}
		}
		catch(Exception exception) {
			exception.printStackTrace();
		}

		if(entity != null) {
			entity.readTags(tag);
		}
		else {
			Log.TICK.warn("Ignoriere Objekt mit Name " + tag.getString("id"));
		}

		return entity;
	}

	public static Entity createEntityByID(int entityID, World worldIn) {
		Entity entity = null;

		try {
			Class<? extends Entity> oclass = ID_TO_CLASS.get(entityID);

			if(oclass != null) {
				entity = oclass.getConstructor(World.class).newInstance(worldIn);
			}
		}
		catch(Exception exception) {
			exception.printStackTrace();
		}

		if(entity == null) {
			Log.TICK.warn("Ignoriere Objekt mit ID " + entityID);
		}

		return entity;
	}

	public static Entity createEntityByID(int entityID, World worldIn, double x, double y, double z, int data) {
		Entity entity = null;

		try {
			Class<? extends Entity> oclass = ID_TO_CLASS.get(entityID);

			if(oclass != null) {
				if(IObjectData.class.isAssignableFrom(oclass)) {
					entity = oclass.getConstructor(World.class, double.class, double.class, double.class, int.class)
							.newInstance(worldIn, x, y, z, data);
				}
				else {
					entity = oclass.getConstructor(World.class, double.class, double.class, double.class)
							.newInstance(worldIn, x, y, z);
				}
			}
		}
		catch(Exception exception) {
			exception.printStackTrace();
		}

		if(entity == null) {
			Log.TICK.warn("Ignoriere Objekt mit ID " + entityID);
		}

		return entity;
	}

	public static int getEntityID(Entity entityIn) {
		Integer integer = CLASS_TO_ID.get(entityIn.getClass());
		return integer == null ? 0 : integer.intValue();
	}

	public static int getEntityID(Class<? extends Entity> clazz) {
		Integer integer = CLASS_TO_ID.get(clazz);
		return integer == null ? 0 : integer.intValue();
	}

	public static Class<? extends Entity> getEntityClass(int id) {
		return ID_TO_CLASS.get(id);
	}

	public static String getEntityString(Entity entityIn) {
		return CLASS_TO_STRING.get(entityIn.getClass());
	}

	public static String getEntityName(String name) {
		String typename = STRING_TO_NAME.get(name);
		return typename == null ? "Unbekannt" : typename;
	}

	public static String getEntityString(Class<? extends Entity> clazz) {
		return CLASS_TO_STRING.get(clazz);
	}

	public static Class<? extends Entity> getEntityClass(String id) {
		return STRING_TO_CLASS.get(id);
	}

	public static Set<Class<? extends Entity>> getAllClasses() {
		return CLASS_TO_STRING.keySet();
	}
	
	static void registerEggs() {
		registerEntity("Dragon", EntityDragon.class, "tbd", "Drache", 0x000000, 0x580094);
		registerEntity("Bat", EntityBat.class, "transylvania", "Fledermaus", 4996656, 986895);
		registerEntity("Pig", EntityPig.class, "terra", "Schwein", 15771042, 14377823);
		registerEntity("Sheep", EntitySheep.class, "terra", "Schaf", 15198183, 16758197);
		registerEntity("Cow", EntityCow.class, "terra", "Kuh", 4470310, 10592673);
		registerEntity("Chicken", EntityChicken.class, "terra", "Huhn", 10592673, 16711680);
		registerEntity("Squid", EntitySquid.class, "tbd", "Tintenfisch", 2243405, 7375001);
		registerEntity("Wolf", EntityWolf.class, "terra", "Wolf", 14144467, 13545366);
		registerEntity("Mooshroom", EntityMooshroom.class, "tbd", "Pilzkuh", 10489616, 12040119);
		registerEntity("Ocelot", EntityOcelot.class, "terra", "Ozelot", 15720061, 5653556);
		registerEntity("Horse", EntityHorse.class, "terra", "Pferd", 12623485, 15656192);
		registerEntity("Rabbit", EntityRabbit.class, "terra", "Kaninchen", 10051392, 7555121);
		registerEntity("Mouse", EntityMouse.class, "terra", "Maus", 0x606060, 0xb0b0b0);
		registerEntity("Fox", EntityFox.class, "terra", "Fuchs", 0xae5300, 0x622f00);

		for(int z = 0; z < SpeciesRegistry.SPECIMEN.size(); z++) {
			SpeciesInfo info = SpeciesRegistry.SPECIMEN.get(z);
			registerEntity(info.id, info.clazz, info.origin, info.name, info.color1, info.color2);
        }
	}

	static void register() {
		register = true;
		
		registerEntity("Item", EntityItem.class, "Gegenstand");
		registerEntity("Xp", EntityXp.class, "Erfahrungskugel");
		registerEntity("Egg", EntityEgg.class, "Ei");
		registerEntity("LeashKnot", EntityLeashKnot.class, "Leinenknoten");
		registerEntity("Arrow", EntityArrow.class, "Pfeil");
		registerEntity("Snowball", EntitySnowball.class, "Schneeball");
		registerEntity("Fireball", EntityFireball.class, "Feuerball");
		registerEntity("FireCharge", EntityFireCharge.class, "Feuerkugel");
		registerEntity("Orb", EntityOrb.class, "Geladene Kugel");
		registerEntity("Potion", EntityPotion.class, "Trank");
		registerEntity("XpBottle", EntityXpBottle.class, "Erfahrungsfläschchen");
		registerEntity("Box", EntityBox.class, "Eisenwürfel");
		registerEntity("Tnt", EntityTnt.class, "TNT");
		registerEntity("Falling", EntityFalling.class, "Fallender Block");
		registerEntity("Fireworks", EntityFireworks.class, "Feuerwerk");
		registerEntity("Boat", EntityBoat.class, "Boot");
		registerEntity("Minecart", EntityMinecart.class, "Lore");
		registerEntity("ChestCart", EntityChestCart.class, "Güterlore");
		registerEntity("TntCart", EntityTntCart.class, "TNT-Lore");
		registerEntity("HopperCart", EntityHopperCart.class, "Trichterlore");
		registerEntity("Hook", EntityHook.class, "Angelhaken");
		registerEntity("Dynamite", EntityDynamite.class, "Dynamit");
		registerEntity("Nuke", EntityNuke.class, "T-17");
		registerEntity("Die", EntityDie.class, "Würfel");
		registerEntity("Explosion", EntityExplosion.class, "Explosion");
		registerEntity("Crystal", EntityCrystal.class, "Kristall");
		registerEntity("Bullet", EntityBullet.class, "Kugel");

		registerEggs();
	}
}
