package cz.cuni.amis.pogamut.ut2004.communication.messages;

import java.util.HashMap;
import java.util.Map;

/**
 * Type of the item.
 * 
 * <p>
 * Note: Items of the same type might have different names in UT engine.
 * <b>Always use {@link #equals(Object)} to safely compare two ItemTypes.</b>
 * 
 * <p>
 * Use {@link #getCategory()} to obtain basic categorization of items.
 * 
 * <p>
 * Use {@link #getGroup()} to obtain detailed group info of items.
 * 
 * @author Juraj 'Loque' Simlovic
 */
public class ItemType {
	/**
	 * List of all item categories. Categories divide items into several basic
	 * categories, based on what the item does and what is it intended to do.
	 */
	public enum Category {
		/** Weapons of all sorts. */
		WEAPON("Weapon"),
		/** Ammunition for weapons of all sorts. */
		AMMO("Ammo"),
		/** Health packs and other health restorers. */
		HEALTH("Health"),
		/** Shield packs and other armor restorers. */
		ARMOR("Armor"),
		/** Adrenaline */
		ADRENALINE("Adrenaline"),
		/** UDamage, Keys + user defined items */
		OTHER("Other");

		/* =================================================================== */

		/** Human-readable name of the category. */
		public String name;

		/* =================================================================== */

		/**
		 * Constructor.
		 * 
		 * @param name
		 *            Human-readable name of the category.
		 */
		Category(String name) {
			this.name = name;
		}
		
	}

	/* ======================================================================== */

	/**
	 * List of all item groups. Groups fine down the categories into specific
	 * groups, based on what the item belongs to. Also, groups join items from
	 * different categories together, if they belong together (e.g. weapon with
	 * its ammo).
	 */
	public enum Group {
		/** Translocating weapon and accessory. */
		TRANSLOCATOR("Translocator"),
		/** ShieldGun weapon and accessory. */
		SHIELD_GUN("ShieldGun"),
		/** AssaultRifle weapon and accessory. */
		ASSAULT_RIFLE("AssaultRifle"),
		/** BioRifle weapon and accessory. */
		BIO_RIFLE("BioRifle"),
		/** ShockRifle weapon and accessory. */
		SHOCK_RIFLE("ShockRifle"),
		/** LinkGun weapon and accessory. */
		LINK_GUN("LinkGun"),
		/** Minigun weapon and accessory. */
		MINIGUN("Minigun"),
		/** FlakCannon weapon and accessory. */
		FLAK_CANNON("FlakCannon"),
		/** RocketLauncher weapon and accessory. */
		ROCKET_LAUNCHER("RocketLauncher"),
		/** LightningGun weapon and accessory. */
		LIGHTNING_GUN("LightningGun"),
		/** SniperRifle weapon and accessory. */
		SNIPER_RIFLE("SniperRifle"),
		/** IonPainter weapon and accessory. */
		ION_PAINTER("IonPainter"),
		/** Redeemer weapon and accessory. */
		REDEEMER("Redeemer"),
		/** SuperShockRifle weapon and accessory. */
		SUPER_SHOCK_RIFLE("SuperShockRifle"),
		/** OnsMineLayer weapon and accessory. */
		ONS_MINE_LAYER("ONS MineLayer"),
		/** OnsGrenadeLauncher weapon and accessory. */
		ONS_GRENADE_LAUNCHER("ONS GrenadeLauncher"),
		/** OnsAvril weapon and accessory. */
		ONS_AVRIL("ONS AVRiL"),
		/** TargetPainter weapon and accessory. */
		ONS_TARGET_PAINTER("TargetPainter"),

		/** Classic health pack. */
		HEALTH("HealthKit"),
		/** Mini health vial. */
		MINI_HEALTH("HealthVial"),
		/** Big health recharger. */
		SUPER_HEALTH("SuperHealth"),

		/** Shield pack. */
		SMALL_ARMOR("SmallShield"),
		/** Shield pack. */
		SUPER_ARMOR("SuperShield"),

		/** Adrenaline packs and adrenaline restorers. */
		ADRENALINE("Adrenaline"),
		/** UDamage bonus items. */
		UDAMAGE("UDamage"),
		/** Keys. */
		KEY("Key"),
		/** Other items with user-defined group. */
		OTHER("Unknown"),
		/** No group, used for the prototype None */
		NONE("None");

		/* =================================================================== */

		/** Human-readable name of the group. */
		public String name;

		/* =================================================================== */

		/**
		 * Constructor.
		 * 
		 * @param name
		 *            Human-readable name of the group.
		 */
		Group(String name) {
			this.name = name;
		}
	}

	/* ======================================================================== */

	/**
	 * Map of all registered ItemType prototypes.
	 */
	private static HashMap<String, ItemType> protos = new HashMap<String, ItemType>();

	/* ======================================================================== */

	/** Translocator. */
	public static final ItemType TRANSLOCATOR = MakePrototype(Category.WEAPON,
			Group.TRANSLOCATOR, new String[] { "XWeapons.Transpickup" });

	/** ShieldGun weapon. */
	public static final ItemType SHIELD_GUN = MakePrototype(Category.WEAPON,
			Group.SHIELD_GUN, new String[] { "XWeapons.ShieldGunPickup" });

	/** AssaultRifle weapon. */
	public static final ItemType ASSAULT_RIFLE = MakePrototype(Category.WEAPON,
			Group.ASSAULT_RIFLE, new String[] { "XWeapons.AssaultRiflePickup" });
	/** AssaultRifle ammo. */
	public static final ItemType ASSAULT_RIFLE_AMMO = MakePrototype(
			Category.AMMO, Group.ASSAULT_RIFLE,
			new String[] { "XWeapons.AssaultAmmoPickup" });

	/** BioRifle weapon. */
	public static final ItemType BIO_RIFLE = MakePrototype(Category.WEAPON,
			Group.BIO_RIFLE, new String[] { "XWeapons.BioRiflePickup",
					"UTClassic.ClassicBioRiflePickup" });
	/** BioRifle ammo. */
	public static final ItemType BIO_RIFLE_AMMO = MakePrototype(Category.AMMO,
			Group.BIO_RIFLE, new String[] { "XWeapons.BioAmmoPickup" });

	/** ShockRifle weapon. */
	public static final ItemType SHOCK_RIFLE = MakePrototype(Category.WEAPON,
			Group.SHOCK_RIFLE, new String[] { "XWeapons.ShockRiflePickup",
					"UTClassic.ClassicShockRiflePickup" });
	/** ShockRifle ammo. */
	public static final ItemType SHOCK_RIFLE_AMMO = MakePrototype(
			Category.AMMO, Group.SHOCK_RIFLE,
			new String[] { "XWeapons.ShockAmmoPickup" });

	/** LinkGun weapon. */
	public static final ItemType LINK_GUN = MakePrototype(Category.WEAPON,
			Group.LINK_GUN, new String[] { "XWeapons.LinkGunPickup" });
	/** LinkGun ammo. */
	public static final ItemType LINK_GUN_AMMO = MakePrototype(Category.AMMO,
			Group.LINK_GUN, new String[] { "XWeapons.LinkAmmoPickup" });

	/** Minigun weapon. */
	public static final ItemType MINIGUN = MakePrototype(Category.WEAPON,
			Group.MINIGUN, new String[] { "XWeapons.MinigunPickup",
					"UTClassic.ClassicMinigunPickup" });
	/** Minigun ammo. */
	public static final ItemType MINIGUN_AMMO = MakePrototype(Category.AMMO,
			Group.MINIGUN, new String[] { "XWeapons.MinigunAmmoPickup" });

	/** FlakCannon weapon. */
	public static final ItemType FLAK_CANNON = MakePrototype(Category.WEAPON,
			Group.FLAK_CANNON, new String[] { "XWeapons.FlakCannonPickup",
					"UTClassic.ClassicFlakCannonPickup" });
	/** FlakCannon ammo. */
	public static final ItemType FLAK_CANNON_AMMO = MakePrototype(
			Category.AMMO, Group.FLAK_CANNON,
			new String[] { "XWeapons.FlakAmmoPickup" });

	/** RocketLauncher weapon. */
	public static final ItemType ROCKET_LAUNCHER = MakePrototype(
			Category.WEAPON, Group.ROCKET_LAUNCHER, new String[] {
					"XWeapons.RocketLauncherPickup",
					"UTClassic.ClassicRocketLauncherPickup" });
	/** RocketLauncher ammo. */
	public static final ItemType ROCKET_LAUNCHER_AMMO = MakePrototype(
			Category.AMMO, Group.ROCKET_LAUNCHER,
			new String[] { "XWeapons.RocketAmmoPickup" });

	/** LightningGun weapon (modern sniper weapon). */
	public static final ItemType LIGHTNING_GUN = MakePrototype(Category.WEAPON,
			Group.LIGHTNING_GUN, new String[] { "XWeapons.SniperRiflePickup" });
	/** LightningGun ammo. */
	public static final ItemType LIGHTNING_GUN_AMMO = MakePrototype(
			Category.AMMO, Group.LIGHTNING_GUN,
			new String[] { "XWeapons.SniperAmmoPickup" });

	/** SniperRifle weapon (classic sniper weapon). */
	public static final ItemType SNIPER_RIFLE = MakePrototype(Category.WEAPON,
			Group.SNIPER_RIFLE,
			new String[] { "UTClassic.ClassicSniperRiflePickup" });
	/** SniperRifle ammo. */
	public static final ItemType SNIPER_RIFLE_AMMO = MakePrototype(
			Category.AMMO, Group.SNIPER_RIFLE,
			new String[] { "UTClassic.ClassicSniperAmmoPickup" });

	/** Redeemer weapon. */
	public static final ItemType REDEEMER = MakePrototype(Category.WEAPON,
			Group.REDEEMER, new String[] { "XWeapons.RedeemerPickup" });

	/** SuperShockRifle weapon (instagib weapon). */
	public static final ItemType SUPER_SHOCK_RIFLE = MakePrototype(
			Category.WEAPON, Group.SUPER_SHOCK_RIFLE,
			new String[] { "XWeapons.SuperShockRiflePickup" });

	/** IonPainter weapon. */
	public static final ItemType ION_PAINTER = MakePrototype(Category.WEAPON,
			Group.ION_PAINTER, new String[] { "XWeapons.PainterPickup" });

	/** MineLayer Onslaught weapon. */
	public static final ItemType ONS_MINE_LAYER = MakePrototype(
			Category.WEAPON, Group.ONS_MINE_LAYER,
			new String[] { "Onslaught.ONSMineLayerPickup" });
	/** MineLayer ammo. */
	public static final ItemType ONS_MINE_LAYER_AMMO = MakePrototype(
			Category.AMMO, Group.ONS_MINE_LAYER,
			new String[] { "Onslaught.ONSMineAmmoPickup" });

	/** GrenadeLauncher Onslaught weapon. */
	public static final ItemType ONS_GRENADE_LAUNCHER = MakePrototype(
			Category.WEAPON, Group.ONS_GRENADE_LAUNCHER,
			new String[] { "Onslaught.ONSGrenadePickup" });
	/** GrenadeLauncher ammo. */
	public static final ItemType ONS_GRENADE_LAUNCHER_AMMO = MakePrototype(
			Category.AMMO, Group.ONS_GRENADE_LAUNCHER,
			new String[] { "Onslaught.ONSGrenadeAmmoPickup" });

	/** AVRiL Onslaught weapon. */
	public static final ItemType ONS_AVRIL = MakePrototype(Category.WEAPON,
			Group.ONS_AVRIL, new String[] { "Onslaught.ONSAVRiLPickup" });
	/** AVRiL ammo. */
	public static final ItemType ONS_AVRIL_AMMO = MakePrototype(Category.AMMO,
			Group.ONS_AVRIL, new String[] { "Onslaught.ONSAVRiLAmmoPickup" });

	/** TargetPainter Onslaught weapon. */
	public static final ItemType ONS_TARGET_PAINTER = MakePrototype(
			Category.WEAPON, Group.ONS_TARGET_PAINTER,
			new String[] { "OnslaughtFull.ONSPainterPickup" });

	/** Health kit. */
	public static final ItemType HEALTH_PACK = MakePrototype(Category.HEALTH,
			Group.HEALTH, new String[] { "XPickups.HealthPack",
					"XPickups.TournamentHealth" });
	/** Health vial. */
	public static final ItemType MINI_HEALTH_PACK = MakePrototype(
			Category.HEALTH, Group.MINI_HEALTH,
			new String[] { "XPickups.MiniHealthPack" });
	/** SuperHealth charger. */
	public static final ItemType SUPER_HEALTH_PACK = MakePrototype(
			Category.HEALTH, Group.SUPER_HEALTH,
			new String[] { "XPickups.SuperHealthPack" });

	/** SmallShield. */
	public static final ItemType SHIELD_PACK = MakePrototype(Category.ARMOR,
			Group.SMALL_ARMOR, new String[] { "XPickups.ShieldPack",
					"XPickups.ShieldPickup" });
	/** SuperShield. */
	public static final ItemType SUPER_SHIELD_PACK = MakePrototype(
			Category.ARMOR, Group.SUPER_ARMOR,
			new String[] { "XPickups.SuperShieldPack" });

	/** UDamage bonus (damage multiplier). */
	public static final ItemType U_DAMAGE_PACK = MakePrototype(
			Category.OTHER, Group.UDAMAGE, new String[] {
					"XPickups.UDamagePack", "XGame.UDamageReward" });

	/** Adrenaline capsule. */
	public static final ItemType ADRENALINE_PACK = MakePrototype(
			Category.ADRENALINE, Group.ADRENALINE,
			new String[] { "XPickups.AdrenalinePickup" });

	/** Key. */
	public static final ItemType KEY = MakePrototype(Category.OTHER,
			Group.KEY, new String[] { "UnrealGame.KeyPickup" });
	
	/** No ItemType */
	public static final ItemType NONE = MakePrototype(Category.OTHER, Group.NONE,
			new String[] { "None", "NONE", "none" });
	
	/* ======================================================================== */

	/**
	 * Name of the item in UT engine.
	 * 
	 * <p>
	 * Note: Items of the same type might have different names in UT engine. Use
	 * {@link #equals(Object)} to safely compare two ItemTypes. This name is
	 * informative only.
	 */
	private String name;
	
	public String toString() {
		return "ItemType[name = " + name + ", category = " + category + ", group = " + group + "]";
	}

	/* ======================================================================== */

	/**
	 * Category of the type.
	 */
	private Category category;

	/**
	 * Group of the type.
	 */
	private Group group;

	/**
	 * Retreives category of the item type.
	 * 
	 * @return Category of the item type.
	 */
	public Category getCategory() {
		// do we have a category already?
		return (category == null) ? (category = getProto().category) : category;
	}

	/**
	 * Retreives group of the item type.
	 * 
	 * @return Group of the item type.
	 */
	public Group getGroup() {
		// do we have a group already?
		return (group == null) ? (group = getProto().group) : group;
	}

	/* ======================================================================== */

	/**
	 * Prototype reference.
	 */
	private ItemType proto;

	/**
	 * Retreives (and caches) ItemType prototype.
	 * 
	 * @return ItemType prototype.
	 */
	private ItemType getProto() {
		// do we have a prototype already?
		if (proto != null) return proto;
		synchronized(protos) {
			return proto = protos.get(name);			
		}
	}

	/* ======================================================================== */

	/**
	 * Indicates whether some other ItemType is "equal to" this one.
	 * 
	 * @param obj
	 *            Object to be compared with.
	 * @return True, if the objects are equal.
	 */
	@Override
	public boolean equals(Object obj) {
		// the same object?
		if (this == obj)
			return true;

		// the same type?
		if (obj instanceof ItemType) {
			// the same prototype?
			if (getProto() == ((ItemType) obj).getProto())
				return true;
		}

		return false;
	}

	/**
	 * Returns a hash code value for the object.
	 * 
	 * @return A hash code value for this object.
	 */
	@Override
	public int hashCode() {
		// provide hash of the string name
		return getProto().name.hashCode();
	}

	/* ======================================================================== */

	/**
	 * Public constructor - creates ItemType of the EXTRA category and Group OTHER.
	 * 
	 * @param name
	 *            Type name from GB engine.
	 */
	public ItemType(String name) {
		this.name = name;
		this.category = Category.OTHER;
		this.group = Group.OTHER;
		this.proto = this;
	}

	/**
	 * Prototypes constructor.
	 */
	private ItemType(String name, Category category, Group group) {
		this.name = name;
		this.category = category;
		this.group = group;
		this.proto = this;
	}

	/* ======================================================================== */

	/**
	 * Proto-constructor.
	 * 
	 * @param category
	 *            Category of the item.
	 * @param group
	 *            Group of the item.
	 * @param utNames
	 *            Names of the item in UT engine.
	 * @return Prototype of known ItemType.
	 */
	public static ItemType MakePrototype(Category category,
			Group group, String[] utNames) {
		ItemType type;
		synchronized(protos) {
			// create new itemtype prototype
			type = new ItemType(utNames[0], category, group);
			// register the itemtype prototype
			for (String utName : utNames)
				protos.put(utName, type);
			// C'est la vie..
		}
		return type;
	}

	/**
	 * Retrieves an ItemType for the specified item type name.
	 * @param utName e.g. Item.getType()
	 * @return
	 */
	public static ItemType getItemType(String utName) {
		ItemType type;
		synchronized(protos) {
			type = protos.get(utName);
			if (type != null) return type;
			
			type = new ItemType(utName);
			protos.put(utName, type);
		}
		return type;
	}

	public String getName() {
		return name;
	}

}