package net.minecraft.src;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

/**
 * Direct Crafting slot, used in Crafting Tool.<br>
 * "It works, and that's important. No matter how and why."
 * 
 * @author MightyPork
 * @copy (c) 2012
 * 
 */
public class PCco_SlotDirectCrafting extends Slot {
	private EntityPlayer thePlayer;
	/** Stack craftable from this slot. Read only, please. */
	public ItemStack product;
	private static final int RECURSION_LIMIT = 50;

	/**
	 * @param entityplayer the Player
	 * @param product product stack (Must be in the same size as crafted from the recipe!)
	 * @param index slot index
	 * @param x slot position X
	 * @param y slot position Y
	 */
	public PCco_SlotDirectCrafting(EntityPlayer entityplayer, ItemStack product, int index, int x, int y) {
		super(null, index, x, y);
		thePlayer = entityplayer;
		this.product = product;
	}

	@Override
	public boolean isItemValid(ItemStack itemstack) {
		return false;
	}

	@Override
	public void onPickupFromSlot(ItemStack itemstack) {
		// itemstack.onCrafting(thePlayer.worldObj, thePlayer);
		super.onPickupFromSlot(itemstack);

		doCrafting(product);
	}

	@Override
	public ItemStack decrStackSize(int i) {
		if (isAvailable()) {
			// doCrafting(product);
			return product.copy();
		}
		return null;
	}

	@Override
	public ItemStack getStack() {
		if (isAvailable()) { return product.copy(); }
		return null;
	}

	@Override
	public void putStack(ItemStack itemstack) {
		product = itemstack;
	}

	@Override
	public void onSlotChanged() {}

	@Override
	public int getSlotStackLimit() {
		if (product == null) { return 1; }
		return product.getMaxStackSize();
	}

	/**
	 * @return true if the item can be crafted
	 */
	private boolean isAvailable() {
		if (product == null) { return false; }

		if (ModLoader.getMinecraftInstance().playerController.isInCreativeMode() || mod_PCcore.survival_cheating) { return true; }

		lastRecipe = -1;
		recipeEndReached = false;
		while (true) {
			IRecipe irecipe = getNextRecipeForProduct(product);
			if (irecipe == null || recipeEndReached) { return false; }
			recursionCount = 0;
			allocatedStacks.clear();
			stacksSeekedInThisRecursion.clear();
			if (tryToFindCraftingSequenceForProduct(irecipe,  product.itemID == Block.chest.blockID)) { return true; }
		}
	}

	private boolean doCrafting(ItemStack prod) {
		if (prod == null) { return false; }

		if (ModLoader.getMinecraftInstance().playerController.isInCreativeMode() || mod_PCcore.survival_cheating) { return true; }

		lastRecipe = -1;
		recipeEndReached = false;
		while (true) {
			IRecipe irecipe = getNextRecipeForProduct(prod);
			if (irecipe == null || recipeEndReached) { return false; }
			recursionCount = 0;
			allocatedStacks.clear();
			toConsume.clear();
			toGiveBack.clear();
			stacksSeekedInThisRecursion.clear();
			if (tryToFindCraftingSequenceForProduct(irecipe, true)) {				
				for(ItemStack stack: toConsume){
					consumePlayerItems(stack, stack.stackSize);		
				}
				
				for(ItemStack stack: toGiveBack){
					thePlayer.inventory.addItemStackToInventory(stack);
					if (stack.stackSize > 0) {
						thePlayer.dropPlayerItem(stack);
					}			
				}
				return true;
			}
		}
	}

	private String getStackDescriptor(ItemStack stack) {
		return Item.itemsList[stack.itemID].getItemName() + "@" + stack.getItemDamage();
	}

	// stackName@meta, count
	private Hashtable<String, Integer> allocatedStacks = new Hashtable<String, Integer>();

	private boolean countPlayerItems(ItemStack stack1, int needed) {
		if (stack1 == null) { return true; }

		Integer alloc = allocatedStacks.get(getStackDescriptor(stack1));
		if (alloc == null) alloc = 0;

		if (ModLoader.getMinecraftInstance().playerController.isInCreativeMode() || mod_PCcore.survival_cheating) { return true; }

		int counter = 0;
		for (int i = 0; i < thePlayer.inventory.getSizeInventory(); i++) {
			ItemStack curStack = thePlayer.inventory.getStackInSlot(i);
			if (curStack != null && (curStack.isItemEqual(stack1) || (curStack.itemID == stack1.itemID && stack1.getItemDamage() == -1))) {
				counter += curStack.stackSize;


				if (counter - alloc >= needed) {
					allocatedStacks.put(getStackDescriptor(stack1), alloc + needed);
					return true;
				}
			}
		}

		if (counter - alloc >= needed) {
			allocatedStacks.put(getStackDescriptor(stack1), alloc + needed);
			return true;
		}
		return false;
	}

	private boolean consumePlayerItems(ItemStack stack1, int count) {

		if (ModLoader.getMinecraftInstance().playerController.isInCreativeMode() || mod_PCcore.survival_cheating) { return true; }

		if (stack1 == null) { return true; }

		for (int i = 0; i < thePlayer.inventory.getSizeInventory(); i++) {
			ItemStack curStack = thePlayer.inventory.getStackInSlot(i);
			if (curStack != null && (curStack.isItemEqual(stack1) || (curStack.itemID == stack1.itemID && stack1.getItemDamage() == -1))) {
				if (curStack.stackSize > count) {
					curStack.stackSize -= count;
					return true;
				} else if (curStack.stackSize <= count) {
					count -= curStack.stackSize;
					thePlayer.inventory.setInventorySlotContents(i, null);
				}
			}
		}
		if (count > 0) { return false; }
		return true;
	}

	private int lastRecipe = -1;
	private boolean recipeEndReached = false;

	@SuppressWarnings("rawtypes")
	private IRecipe getNextRecipeForProduct(ItemStack prod) {
		List recipes = CraftingManager.getInstance().getRecipeList();

		if (lastRecipe == recipes.size() - 1) { return null; }

		int k;
		for (k = lastRecipe + 1; k < recipes.size(); k++) {
			IRecipe irecipe = (IRecipe) recipes.get(k);
			try {
				if (irecipe.getRecipeOutput().isItemEqual(prod) || (irecipe.getRecipeOutput().itemID == prod.itemID && prod.getItemDamage() == -1)) {
					lastRecipe = k;
					return irecipe;
				}
			} catch (NullPointerException npe) {
				continue;
			}
		}

		recipeEndReached = true;
		lastRecipe = k;
		return null;
	}

	private int recursionCount = 0;

	private ArrayList<String> stacksSeekedInThisRecursion = new ArrayList<String>();
	
	private ArrayList<ItemStack> toConsume = new ArrayList<ItemStack>();
	private ArrayList<ItemStack> toGiveBack = new ArrayList<ItemStack>();
	

	@SuppressWarnings("unchecked")
	private boolean tryToFindCraftingSequenceForProduct(IRecipe irecipe, boolean verbose) {
		if (ModLoader.getMinecraftInstance().playerController.isInCreativeMode() || mod_PCcore.survival_cheating) { return true; }
		
		recursionCount++;
		if (recursionCount > RECURSION_LIMIT) {
			if(verbose) System.out.println("RECURSION LIMIT");
			return false;
		}
		try {

			ItemStack[] tmps;
			if (irecipe instanceof ShapedRecipes) {
				tmps = (ItemStack[]) ModLoader.getPrivateValue(net.minecraft.src.ShapedRecipes.class, irecipe, 2);
			} else if (irecipe instanceof ShapelessRecipes) {
				List<ItemStack> foo = ((List<ItemStack>) ModLoader.getPrivateValue(net.minecraft.src.ShapelessRecipes.class, irecipe, 1));
				tmps = foo.toArray(new ItemStack[foo.size()]);
			} else {
				return false;
			}

			ItemStack[] recipeStacks = new ItemStack[tmps.length];

			for (int i = 0; i < tmps.length; i++) {
				if (tmps[i] != null) {
					recipeStacks[i] = tmps[i].copy();
				}
			}

			for (int i = 0; i < recipeStacks.length; i++) {
				if (recipeStacks[i] != null) {
					recipeStacks[i].stackSize = 1;
					for (int j = i + 1; j < recipeStacks.length; j++) {
						if (recipeStacks[j] != null && recipeStacks[j].isItemEqual(recipeStacks[i])) {
							recipeStacks[i].stackSize++;
							recipeStacks[j] = null;
						}
					}
				}
			}

			recloop:
			for (int i = 0; i < recipeStacks.length; i++) {
				if (recipeStacks[i] == null) {
					continue recloop;
				}
				if (!countPlayerItems(recipeStacks[i], recipeStacks[i].stackSize)) {

					if (!mod_PCcore.recursive_crafting) { return false; }

					if (stacksSeekedInThisRecursion.contains(getStackDescriptor(recipeStacks[i]))) {
						return false;
					}

					if (recursionCount > RECURSION_LIMIT) {
						System.out.println("RECURSION LIMIT");
						return false;
					}

					int stacked = lastRecipe;


					lastRecipe = -1;
					ItemStack needed = recipeStacks[i].copy();
					boolean anymeta = false;
					int tmpmeta = 0;
					if (needed.getItemDamage() < 0) {
						needed.setItemDamage(tmpmeta);
						anymeta = true;
					}

					while (true) {

						IRecipe irecipe2 = getNextRecipeForProduct(needed);
						if (irecipe2 == null) {
							if (anymeta && tmpmeta <= 15) {
								needed.setItemDamage(++tmpmeta);
								lastRecipe = -1;
								continue;
							} else {
								lastRecipe = stacked;
								return false;
							}
						}

						innerloop:
						while(true){
							stacksSeekedInThisRecursion.add(getStackDescriptor(recipeStacks[i]));
							if (tryToFindCraftingSequenceForProduct(irecipe2,verbose)) {
	
								stacksSeekedInThisRecursion.remove(getStackDescriptor(recipeStacks[i]));
	
								recipeStacks[i].stackSize -= irecipe2.getRecipeOutput().stackSize;
								
								if(recipeStacks[i].stackSize > 0){
									if(recipeStacks[i].stackSize>0){

										lastRecipe--;
										continue innerloop;
									}
								}else if(recipeStacks[i].stackSize < 0){
									ItemStack got = recipeStacks[i].copy();
									got.stackSize = Math.abs(got.stackSize);
									if(got.getItemDamage()<0) got.setItemDamage(0);
									toGiveBack.add(got);
								}
								
								lastRecipe = stacked;
								continue recloop;
							}else{
								break;
							}
						}
					}
				} else {
					toConsume.add(recipeStacks[i]);
					continue recloop;
				}
			}
			return true;

		} catch (IllegalArgumentException et) {
			et.printStackTrace();
		} catch (SecurityException es) {
			es.printStackTrace();
		} catch (NoSuchFieldException en) {
			en.printStackTrace();
		}
		return false;
	}

}
