package pneumaticCraft.common.semiblock;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryBasic;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.IFluidHandler;
import net.minecraftforge.fluids.IFluidTank;
import pneumaticCraft.PneumaticCraft;
import pneumaticCraft.common.item.ItemLogisticsFrame;
import pneumaticCraft.common.item.Itemss;
import pneumaticCraft.common.network.DescSynced;
import pneumaticCraft.common.network.GuiSynced;
import pneumaticCraft.common.network.NetworkHandler;
import pneumaticCraft.common.network.PacketSetSemiBlock;
import pneumaticCraft.common.tileentity.TileEntityBase;
import pneumaticCraft.proxy.CommonProxy.EnumGuiId;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public abstract class SemiBlockLogistics extends SemiBlockBasic{
    protected final Map<ItemStack, Integer> incomingStacks = new HashMap<ItemStack, Integer>();
    protected final Map<FluidStackWrapper, Integer> incomingFluid = new HashMap<FluidStackWrapper, Integer>();
    private final IInventory filters = new InventoryBasic("filters", true, 27);//Filters and requests
    @GuiSynced
    private final FluidTank[] fluidFilters = new FluidTank[9];
    @DescSynced
    @GuiSynced
    private boolean invisible;
    private int alpha = 255;

    public SemiBlockLogistics(){
        for(int i = 0; i < fluidFilters.length; i++) {
            fluidFilters[i] = new FluidTank(canFilterStack() ? 64000 : 1000);
        }
    }

    @Override
    public boolean canPlace(){
        return getTileEntity() instanceof IInventory || getTileEntity() instanceof IFluidHandler;
    }

    public abstract int getColor();

    public abstract int getPriority();

    public boolean shouldProvideTo(int level){
        return true;
    }

    public void setInvisible(boolean invisible){
        this.invisible = invisible;
    }

    public boolean isInvisible(){
        return invisible;
    }

    public int getAlpha(){
        return alpha;
    }

    @Override
    public void update(){
        super.update();
        if(!world.isRemote) {
            Iterator<Map.Entry<ItemStack, Integer>> iterator = incomingStacks.entrySet().iterator();
            while(iterator.hasNext()) {
                Map.Entry<ItemStack, Integer> entry = iterator.next();
                int counter = entry.getValue();
                if(counter > 10) {
                    iterator.remove();
                } else {
                    entry.setValue(counter + 1);
                }
            }
            Iterator<Map.Entry<FluidStackWrapper, Integer>> it = incomingFluid.entrySet().iterator();
            while(it.hasNext()) {
                Map.Entry<FluidStackWrapper, Integer> entry = it.next();
                int counter = entry.getValue();
                if(counter > 10) {
                    it.remove();
                } else {
                    entry.setValue(counter + 1);
                }
            }

        } else {
            if(invisible && !playerIsHoldingLogisticItems()) {
                alpha = Math.max(0, alpha - 3);
            } else {
                alpha = Math.min(255, alpha + 3);
            }
        }
    }

    @SideOnly(Side.CLIENT)
    private boolean playerIsHoldingLogisticItems(){
        EntityPlayer player = Minecraft.getMinecraft().thePlayer;
        ItemStack stack = player.getCurrentEquippedItem();
        return stack != null && (stack.getItem() == Itemss.logisticsConfigurator || stack.getItem() instanceof ItemLogisticsFrame);
    }

    public void informIncomingStack(ItemStack stack){
        incomingStacks.put(stack, 0);
    }

    public void clearIncomingStack(ItemStack stack){
        incomingStacks.remove(stack);
    }

    public void informIncomingStack(FluidStackWrapper stack){
        incomingFluid.put(stack, 0);
    }

    public void clearIncomingStack(FluidStackWrapper stack){
        incomingFluid.remove(stack);
    }

    public int getIncomingFluid(Fluid fluid){
        int count = 0;
        for(FluidStackWrapper wrapper : incomingFluid.keySet()) {
            if(wrapper.stack.getFluid() == fluid) count += wrapper.stack.amount;
        }
        return count;
    }

    public int getIncomingItems(ItemStack stack){
        int count = 0;
        for(ItemStack s : incomingStacks.keySet()) {
            if(isItemEqual(s, stack)) {
                count += s.stackSize;
            }
        }
        return count;
    }

    @Override
    public void writeToNBT(NBTTagCompound tag){
        super.writeToNBT(tag);
        TileEntityBase.writeInventoryToNBT(tag, filters, "filters");

        NBTTagList tagList = new NBTTagList();
        for(int i = 0; i < fluidFilters.length; i++) {
            FluidTank filter = fluidFilters[i];
            if(filter.getFluid() != null) {
                NBTTagCompound t = new NBTTagCompound();
                t.setInteger("index", i);
                filter.writeToNBT(t);
                tagList.appendTag(t);
            }
        }
        tag.setTag("fluidFilters", tagList);

        tag.setBoolean("invisible", invisible);
    }

    @Override
    public void readFromNBT(NBTTagCompound tag){
        super.readFromNBT(tag);
        TileEntityBase.readInventoryFromNBT(tag, filters, "filters");

        NBTTagList tagList = tag.getTagList("fluidFilters", 10);
        for(int i = 0; i < tagList.tagCount(); i++) {
            fluidFilters[tagList.getCompoundTagAt(i).getInteger("index")].readFromNBT(tagList.getCompoundTagAt(i));
        }

        invisible = tag.getBoolean("invisible");
    }

    public void setFilter(int filterIndex, FluidStack stack){
        fluidFilters[filterIndex].setFluid(stack);
    }

    public IFluidTank getTankFilter(int filterIndex){
        return fluidFilters[filterIndex];
    }

    public IInventory getFilters(){
        return filters;
    }

    @Override
    public void addDrops(List<ItemStack> drops){
        super.addDrops(drops);

        boolean shouldAddTag = false;
        for(int i = 0; i < filters.getSizeInventory(); i++) {
            if(filters.getStackInSlot(i) != null) {//Only set a tag when there are requests.
                shouldAddTag = true;
                break;
            }
        }

        for(FluidTank fluidFilter : fluidFilters) {
            if(fluidFilter.getFluidAmount() > 0) {
                shouldAddTag = true;
                break;
            }
        }

        if(invisible) shouldAddTag = true;

        if(shouldAddTag) {
            ItemStack drop = drops.get(0);
            NBTTagCompound tag = new NBTTagCompound();
            writeToNBT(tag);
            drop.setTagCompound(tag);
        }
    }

    @Override
    public void onPlaced(EntityPlayer player, ItemStack stack){
        NBTTagCompound tag = stack.getTagCompound();
        if(tag != null) {
            readFromNBT(tag);
        }
    }

    @Override
    public boolean onRightClickWithConfigurator(EntityPlayer player){
        if(getGuiID() != null) {
            NetworkHandler.sendTo(new PacketSetSemiBlock(pos, this), (EntityPlayerMP)player);
            player.openGui(PneumaticCraft.instance, getGuiID().ordinal(), world, pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ);
        }
        return true;
    }

    public EnumGuiId getGuiID(){
        return null;
    }

    public boolean canFilterStack(){
        return false;
    }

    protected boolean isItemEqual(ItemStack s1, ItemStack s2){
        return s1.isItemEqual(s2);
    }

    protected boolean passesFilter(ItemStack stack){
        boolean hasStack = false;
        for(int i = 0; i < filters.getSizeInventory(); i++) {
            ItemStack s = filters.getStackInSlot(i);
            if(s != null) {
                if(isItemEqual(s, stack)) return true;
                hasStack = true;
            }
        }
        return !hasStack;
    }

    protected boolean passesFilter(Fluid fluid){
        boolean hasFilter = false;
        for(FluidTank filter : fluidFilters) {
            if(filter.getFluidAmount() > 0) {
                if(filter.getFluid().getFluid() == fluid) return true;
                hasFilter = true;
            }
        }
        return !hasFilter;
    }

    @Override
    public void handleGUIButtonPress(int guiID, EntityPlayer player){
        if(guiID == 9) {
            invisible = !invisible;
        }
    }

    @Override
    public void addWailaTooltip(List<String> curInfo, NBTTagCompound tag){
        super.addWailaTooltip(curInfo, tag);
        // readFromNBT(tag);
        List<ItemStack> drops = new ArrayList<ItemStack>();
        addDrops(drops);
        drops.get(0).setTagCompound(tag);
        ItemLogisticsFrame.addTooltip(drops.get(0), PneumaticCraft.proxy.getPlayer(), curInfo, true);
    }

    @Override
    public void addWailaInfoToTag(NBTTagCompound tag){
        writeToNBT(tag);
    }

    public static class FluidStackWrapper{
        public final FluidStack stack;

        public FluidStackWrapper(FluidStack stack){
            this.stack = stack;
        }
    }
}