package crazypants.enderio.machine.crafter;

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

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.nbt.NBTTagCompound;
import crazypants.enderio.Config;
import crazypants.enderio.ModObject;
import crazypants.enderio.machine.AbstractMachineEntity;
import crazypants.enderio.machine.SlotDefinition;
import crazypants.enderio.power.Capacitors;

public class TileCrafter extends AbstractMachineEntity {

  DummyCraftingGrid craftingGrid = new DummyCraftingGrid();
  
  public TileCrafter() {
    super(new SlotDefinition(9, 1));    
  }

  @Override
  public String getMachineName() {
    return ModObject.blockCrafter.unlocalisedName;
  }

  @Override
  protected boolean isMachineItemValidForSlot(int slot, ItemStack itemstack) {
    if(!slotDefinition.isInputSlot(slot)) {
      return false;
    }
    return craftingGrid.inv[slot] != null && craftingGrid.inv[slot].isItemEqual(itemstack);
  }

  @Override
  public boolean isActive() {
    return false;
  }

  @Override
  public float getProgress() {
    return 0.5f;
  }

  @Override
  protected boolean processTasks(boolean redstoneCheckPassed) {  
    if(!redstoneCheckPassed || !craftingGrid.hasValidRecipe() || !canMergeOutput() || !hasPower()) {      
      return false;
    }
    if(capacitorType == Capacitors.BASIC_CAPACITOR && worldObj.getTotalWorldTime() % 20 != 0) {
      return false;
    } else if(capacitorType == Capacitors.ACTIVATED_CAPACITOR && worldObj.getTotalWorldTime() % 10 != 0) {
      return false;
    } else if(worldObj.getTotalWorldTime() % 2 != 0) {
      return false;
    }
    
    List<ItemStack> required = new ArrayList<ItemStack>();
    craftingGrid.copyRequiredInputs(required);
    if(hasRequiredInput(required)) {      
      craftRecipe();
      double used = Math.min(powerHandler.getEnergyStored(), Config.crafterMjPerCraft);    
      powerHandler.setEnergy(powerHandler.getEnergyStored() - used);
    }     
    return false;
  }

  private void craftRecipe() {    
    List<ItemStack> required = new ArrayList<ItemStack>();
    craftingGrid.copyRequiredInputs(required);
    for(ItemStack req : required) {
      for(int i=0;i<9 && req.stackSize > 0; i++) {
        ItemStack avail = inventory[i];
        if(avail != null && avail.stackSize > 0 && avail.isItemEqual(req)) {
          req.stackSize--;
          avail = avail.copy();
          avail.stackSize--;
          if(avail.stackSize <= 0) {
            avail = avail.getItem().getContainerItem(avail);
          }
          setInventorySlotContents(i, avail);          
        }
      }
    }
    ItemStack output = craftingGrid.getOutput().copy();
    if(inventory[9] == null) {      
      setInventorySlotContents(9, output);
    } else {
      ItemStack cur = inventory[9].copy();
      cur.stackSize += output.stackSize;
      setInventorySlotContents(9, cur);
    }    
  }

  private boolean canMergeOutput() {
    if(inventory[9] == null) {
      return true;
    }
    ItemStack output = craftingGrid.getOutput();
    if(!inventory[9].isItemEqual(output)) {      
      return false;
    }        
    return output.getMaxStackSize() >= (inventory[9].stackSize + output.stackSize);
  }

  private boolean hasRequiredInput(List<ItemStack> required) {
    List<ItemStack> available = new ArrayList<ItemStack>();
    for(int i=0;i<9;i++) {
      ItemStack is = inventory[i];
      if(is != null) {
        available.add(is.copy());
      }      
    }    
    for(ItemStack req : required) {
      boolean foundReq = false;
      for(ItemStack avail : available) {
        if(req.isItemEqual(avail) && avail.stackSize > 0) {
          avail.stackSize--;
          foundReq = true;
          break;
        }
      }
      if(!foundReq) {
        return false;
      }
    }    
    return true;
  }

  @Override
  public void readCommon(NBTTagCompound nbtRoot) {
    super.readCommon(nbtRoot);
    NBTTagCompound craftRoot = nbtRoot.getCompoundTag("craftingGrid");
    craftingGrid.readFromNBT(craftRoot);
  }

  @Override
  public void writeCommon(NBTTagCompound nbtRoot) {
    super.writeCommon(nbtRoot);    
    NBTTagCompound craftingRoot = new NBTTagCompound();
    craftingGrid.writeToNBT(craftingRoot);
    nbtRoot.setTag("craftingGrid", craftingRoot);
  }

  public void updateCraftingOutput() {
    InventoryCrafting inv = new InventoryCrafting(new Container() {
      
      @Override
      public boolean canInteractWith(EntityPlayer var1) {        
        return false;
      }
    }, 3, 3);
    
    for(int i=0;i<9;i++) {
      inv.setInventorySlotContents(i, craftingGrid.getStackInSlot(i));
    }
    ItemStack matches = CraftingManager.getInstance().findMatchingRecipe(inv, worldObj);
    craftingGrid.setInventorySlotContents(9, matches);
    markDirty();
    
  }
  
  

}

@@ -83,19 +83,67 @@
 va.util.
-Lis
+HashSet;%0Aimport java.util.List;%0Aimport java.util.Se
 t;%0A%0Aimpo
@@ -753,16 +753,60 @@
 d();%0A  %0A
+  private Set%3CItemStack%3E containerItems;%0A  %0A
   public
@@ -858,21 +858,69 @@
 (9, 1));
+%0A
     %0A
+    containerItems = new HashSet%3CItemStack%3E();%0A
   %7D%0A%0A  @
@@ -1943,24 +1943,467 @@
 %0A    %7D%0A    %0A
+    // process buffered container items%0A    if(!containerItems.isEmpty()) %7B%0A      for(ItemStack i : containerItems) %7B%0A%09if(inventory%5B9%5D == null) %7B%0A%09  inventory%5B9%5D = i;%0A%09  containerItems.remove(i);%0A%09%7D else if(ItemStack.areItemStacksEqual(inventory%5B9%5D, i) && inventory%5B9%5D.stackSize + i.stackSize %3C= inventory%5B9%5D.getMaxStackSize()) %7B%0A%09  inventory%5B9%5D.stackSize += i.stackSize;%0A%09  containerItems.remove(i);%0A%09%7D%0A      %7D%0A      return false;%0A    %7D%0A    %0A
     List%3CIte
@@ -3286,16 +3286,139 @@
 avail);%0A
+            if (avail != null) %7B%0A              containerItems.add(avail.copy());%0A              avail = null;%0A            %7D%0A
         

Change the crafter to function on a buffer as per request.