package net.minecraftforge.common;

import java.util.*;

import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.WeightedRandom;
import net.minecraft.util.WeightedRandomChestContent;
import net.minecraft.world.WorldServer;
import net.minecraft.world.gen.feature.WorldGenDungeons;
import net.minecraft.world.gen.structure.*;
import net.minecraft.world.gen.structure.ComponentScatteredFeaturePieces.DesertPyramid;
import net.minecraft.world.gen.structure.ComponentScatteredFeaturePieces.JunglePyramid;
import net.minecraft.world.gen.structure.StructureStrongholdPieces.ChestCorridor;
import net.minecraft.world.gen.structure.StructureStrongholdPieces.Library;
import net.minecraft.world.gen.structure.StructureStrongholdPieces.RoomCrossing;
import net.minecraft.world.gen.structure.StructureVillagePieces.House2;
import net.minecraftforge.oredict.OreDictionary;

public class ChestGenHooks
{
	//Currently implemented categories for chests/dispensers, Dungeon loot is still in DungeonHooks
	public static final String MINESHAFT_CORRIDOR       = "mineshaftCorridor";
	public static final String PYRAMID_DESERT_CHEST     = "pyramidDesertyChest";
	public static final String PYRAMID_JUNGLE_CHEST     = "pyramidJungleChest";
	public static final String PYRAMID_JUNGLE_DISPENSER = "pyramidJungleDispenser";
	public static final String STRONGHOLD_CORRIDOR      = "strongholdCorridor";
	public static final String STRONGHOLD_LIBRARY       = "strongholdLibrary";
	public static final String STRONGHOLD_CROSSING      = "strongholdCrossing";
	public static final String VILLAGE_BLACKSMITH       = "villageBlacksmith";
	public static final String BONUS_CHEST              = "bonusChest";
	public static final String DUNGEON_CHEST            = "dungeonChest";

	private static final HashMap<String, ChestGenHooks> chestInfo = new HashMap<String, ChestGenHooks>();
	private static boolean hasInit = false;
	static
	{
		init();
	}

	private static void init()
	{
		if (hasInit)
		{
			return;
		}

		hasInit = true;

		addInfo(MINESHAFT_CORRIDOR,       StructureMineshaftPieces.mineshaftChestContents,   3,  7);
		addInfo(PYRAMID_DESERT_CHEST,     DesertPyramid.itemsToGenerateInTemple,             2,  7);
		addInfo(PYRAMID_JUNGLE_CHEST,     JunglePyramid.junglePyramidsChestContents,         2,  7);
		addInfo(PYRAMID_JUNGLE_DISPENSER, JunglePyramid.junglePyramidsDispenserContents,     2,  2);
		addInfo(STRONGHOLD_CORRIDOR,      ChestCorridor.strongholdChestContents,             2,  4);
		addInfo(STRONGHOLD_LIBRARY,       Library.strongholdLibraryChestContents,            1,  5);
		addInfo(STRONGHOLD_CROSSING,      RoomCrossing.strongholdRoomCrossingChestContents,  1,  5);
		addInfo(VILLAGE_BLACKSMITH,       House2.villageBlacksmithChestContents,             3,  9);
		addInfo(BONUS_CHEST,              WorldServer.bonusChestContent,                    10, 10);
		addInfo(DUNGEON_CHEST,            WorldGenDungeons.field_111189_a,                   8,  8);

		ItemStack book = new ItemStack(Items.enchanted_book, 1, 0);
		WeightedRandomChestContent tmp = new WeightedRandomChestContent(book, 1, 1, 1);
		getInfo(MINESHAFT_CORRIDOR  ).addItem(tmp);
		getInfo(PYRAMID_DESERT_CHEST).addItem(tmp);
		getInfo(PYRAMID_JUNGLE_CHEST).addItem(tmp);
		getInfo(STRONGHOLD_CORRIDOR ).addItem(tmp);
		getInfo(STRONGHOLD_LIBRARY  ).addItem(new WeightedRandomChestContent(book, 1, 5, 2));
		getInfo(STRONGHOLD_CROSSING ).addItem(tmp);
		getInfo(DUNGEON_CHEST       ).addItem(tmp);
	}

	static void addDungeonLoot(ChestGenHooks dungeon, ItemStack item, int weight, int min, int max)
	{
		dungeon.addItem(new WeightedRandomChestContent(item, min, max, weight));
	}

	private static void addInfo(String category, WeightedRandomChestContent[] items, int min, int max)
	{
		chestInfo.put(category, new ChestGenHooks(category, items, min, max));
	}

	/**
	 * Retrieves, or creates the info class for the specified category.
	 *
	 * @param category The category name
	 * @return A instance of ChestGenHooks for the specified category.
	 */
	public static ChestGenHooks getInfo(String category)
	{
		if (!chestInfo.containsKey(category))
		{
			chestInfo.put(category, new ChestGenHooks(category));
		}
		return chestInfo.get(category);
	}

	/**
	 * Generates an array of items based on the input min/max count.
	 * If the stack can not hold the total amount, it will be split into
	 * stacks of size 1.
	 *
	 * @param rand A random number generator
	 * @param source Source item stack
	 * @param min Minimum number of items
	 * @param max Maximum number of items
	 * @return An array containing the generated item stacks
	 */
	public static ItemStack[] generateStacks(Random rand, ItemStack source, int min, int max)
	{
		int count = min + (rand.nextInt(max - min + 1));

		ItemStack[] ret;
		if (source.getItem() == null)
		{
			ret = new ItemStack[0];
		}
		else if (count > source.getMaxStackSize())
		{
			ret = new ItemStack[count];
			for (int x = 0; x < count; x++)
			{
				ret[x] = source.copy();
				ret[x].stackSize = 1;
			}
		}
		else
		{
			ret = new ItemStack[1];
			ret[0] = source.copy();
			ret[0].stackSize = count;
		}
		return ret;
	}

	//shortcut functions, See the non-static versions below
	public static WeightedRandomChestContent[] getItems(String category, Random rnd){ return getInfo(category).getItems(rnd); }
	public static int getCount(String category, Random rand){ return getInfo(category).getCount(rand); }
	public static void addItem(String category, WeightedRandomChestContent item){ getInfo(category).addItem(item); }
	public static void removeItem(String category, ItemStack item){ getInfo(category).removeItem(item); }
	public static ItemStack getOneItem(String category, Random rand){ return getInfo(category).getOneItem(rand); }

	private String category;
	private int countMin = 0;
	private int countMax = 0;
	private ArrayList<WeightedRandomChestContent> contents = new ArrayList<WeightedRandomChestContent>();

	public ChestGenHooks(String category)
	{
		this.category = category;
	}

	public ChestGenHooks(String category, WeightedRandomChestContent[] items, int min, int max)
	{
		this(category);
		for (WeightedRandomChestContent item : items)
		{
			contents.add(item);
		}
		countMin = min;
		countMax = max;
	}

	/**
	 * Adds a new entry into the possible items to generate.
	 *
	 * @param item The item to add.
	 */
	public void addItem(WeightedRandomChestContent item)
	{
		contents.add(item);
	}

	/**
	 * Removes all items that match the input item stack, Only metadata and item ID are checked.
	 * If the input item has a metadata of -1, all metadatas will match.
	 *
	 * @param item The item to check
	 */
	public void removeItem(ItemStack item)
	{
		Iterator<WeightedRandomChestContent> itr = contents.iterator();
		while(itr.hasNext())
		{
			WeightedRandomChestContent cont = itr.next();
			if (item.isItemEqual(cont.theItemId) || (item.getItemDamage() == OreDictionary.WILDCARD_VALUE && item.getItem() == cont.theItemId.getItem()))
			{
				itr.remove();
			}
		}
	}

	/**
	 * Gets an array of all random objects that are associated with this category.
	 *
	 * @return The random objects
	 */
	public WeightedRandomChestContent[] getItems(Random rnd)
	{
		ArrayList<WeightedRandomChestContent> ret = new ArrayList<WeightedRandomChestContent>();

		for (WeightedRandomChestContent orig : contents)
		{
			Item item = orig.theItemId.getItem();

			if (item != null)
			{
				WeightedRandomChestContent n = item.getChestGenBase(this, rnd, orig);
				if (n != null)
				{
					ret.add(n);
				}
			}
		}

		return ret.toArray(new WeightedRandomChestContent[ret.size()]);
	}

	/**
	 * Gets a random number between countMin and countMax.
	 *
	 * @param rand A RNG
	 * @return A random number where countMin <= num <= countMax
	 */
	public int getCount(Random rand)
	{
		return countMin < countMax ? countMin + rand.nextInt(countMax - countMin) : countMin;
	}

	/**
	 * Returns a single ItemStack from the possible items in this registry,
	 * Useful if you just want a quick and dirty random Item.
	 *
	 * @param rand  A Random Number gen
	 * @return A single ItemStack, or null if it could not get one.
	 */
	public ItemStack getOneItem(Random rand)
	{
		WeightedRandomChestContent[] items = getItems(rand);
		WeightedRandomChestContent item = (WeightedRandomChestContent)WeightedRandom.getRandomItem(rand, items);
		ItemStack[] stacks = ChestGenHooks.generateStacks(rand, item.theItemId, item.theMinimumChanceToGenerateItem, item.theMaximumChanceToGenerateItem);
		return (stacks.length > 0 ? stacks[0] : null);
	}

	//Accessors
	public int getMin(){ return countMin; }
	public int getMax(){ return countMax; }
	public void setMin(int value){ countMin = value; }
	public void setMax(int value){ countMax = value; }
}
