package org.cyclops.integrateddynamics.tileentity;

import com.google.common.collect.Sets;
import lombok.Getter;
import lombok.Setter;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.cyclops.cyclopscore.datastructure.DimPos;
import org.cyclops.cyclopscore.helper.L10NHelpers;
import org.cyclops.cyclopscore.helper.MinecraftHelpers;
import org.cyclops.cyclopscore.persist.nbt.NBTPersist;
import org.cyclops.integrateddynamics.IntegratedDynamics;
import org.cyclops.integrateddynamics.api.item.IProxyVariableFacade;
import org.cyclops.integrateddynamics.api.item.IVariableFacade;
import org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;
import org.cyclops.integrateddynamics.api.network.INetworkElement;
import org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderConfig;
import org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;
import org.cyclops.integrateddynamics.core.evaluate.ProxyVariableFacadeHandler;
import org.cyclops.integrateddynamics.core.helper.L10NValues;
import org.cyclops.integrateddynamics.core.item.ProxyVariableFacade;
import org.cyclops.integrateddynamics.core.tileentity.TileActiveVariableBase;
import org.cyclops.integrateddynamics.network.ProxyNetworkElement;

/**
 * A part entity for the variable proxy.
 * @author rubensworks
 */
public class TileProxy extends TileActiveVariableBase<ProxyNetworkElement> {

    public static final int SLOT_READ = 0;
    public static final int SLOT_WRITE_IN = 1;
    public static final int SLOT_WRITE_OUT = 2;

    public static final String GLOBALCOUNTER_KEY = "proxy";

    @NBTPersist
    @Getter
    private int proxyId = -1;

    @Setter
    private EntityPlayer lastPlayer = null;

    public TileProxy() {
        super(3, "proxy");

        addSlotsToSide(EnumFacing.UP, Sets.newHashSet(SLOT_READ));
        addSlotsToSide(EnumFacing.DOWN, Sets.newHashSet(SLOT_READ));
        addSlotsToSide(EnumFacing.SOUTH, Sets.newHashSet(SLOT_READ));
        addSlotsToSide(EnumFacing.WEST, Sets.newHashSet(SLOT_WRITE_OUT));
        addSlotsToSide(EnumFacing.EAST, Sets.newHashSet(SLOT_WRITE_IN));

        addCapabilityInternal(NetworkElementProviderConfig.CAPABILITY, new NetworkElementProviderSingleton() {
            @Override
            public INetworkElement createNetworkElement(World world, BlockPos blockPos) {
                return new ProxyNetworkElement(DimPos.of(world, blockPos));
            }
        });
    }

    @Override
    public boolean canInsertItem(int slot, ItemStack itemStack, EnumFacing side) {
        return slot != SLOT_WRITE_OUT && super.canInsertItem(slot, itemStack, side);
    }

    /**
     * This will generate a new proxy id.
     * Be careful when calling this!
     */
    public void generateNewProxyId() {
        this.proxyId = IntegratedDynamics.globalCounters.getNext(GLOBALCOUNTER_KEY);
        markDirty();
    }

    @Override
    public void onLoad() {
        super.onLoad();
        if(!MinecraftHelpers.isClientSide() && this.proxyId == -1) {
            generateNewProxyId();
        }
    }

    @Override
    public int getSlotRead() {
        return SLOT_READ;
    }

    @Override
    public void onDirty() {
        super.onDirty();
        if(!world.isRemote) {
            if (!getStackInSlot(SLOT_WRITE_IN).isEmpty() && getStackInSlot(SLOT_WRITE_OUT).isEmpty()) {
                // Write proxy reference
                ItemStack outputStack = writeProxyInfo(!getWorld().isRemote, removeStackFromSlot(SLOT_WRITE_IN), proxyId);
                setInventorySlotContents(SLOT_WRITE_OUT, outputStack);
            }
        }
    }

    public ItemStack writeProxyInfo(boolean generateId, ItemStack itemStack, final int proxyId) {
        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);
        return registry.writeVariableFacadeItem(generateId, itemStack, ProxyVariableFacadeHandler.getInstance(), new IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IProxyVariableFacade>() {
            @Override
            public IProxyVariableFacade create(boolean generateId) {
                return new ProxyVariableFacade(generateId, proxyId);
            }

            @Override
            public IProxyVariableFacade create(int id) {
                return new ProxyVariableFacade(id, proxyId);
            }
        }, lastPlayer, getBlock());
    }

    @Override
    protected void preValidate(IVariableFacade variableStored) {
        super.preValidate(variableStored);
        // Hard check to make sure the variable is not directly referring to this proxy.
        if(variableStored instanceof IProxyVariableFacade) {
            if(((IProxyVariableFacade) variableStored).getProxyId() == getProxyId()) {
                addError(new L10NHelpers.UnlocalizedString(L10NValues.VARIABLE_ERROR_RECURSION, variableStored.getId()));
            }
        }
    }
}

@@ -1946,24 +1946,14 @@
     
-super(3, %22proxy%22
+this(3
 );%0A%0A
@@ -2304,16 +2304,104 @@
 TE_IN));
+%0A    %7D%0A%0A    public TileProxy(int inventorySize) %7B%0A        super(inventorySize, %22proxy%22);
 %0A%0A      
@@ -3417,32 +3417,182 @@
 OT_READ;%0A    %7D%0A%0A
+    protected int getSlotWriteIn() %7B%0A        return SLOT_WRITE_IN;%0A    %7D%0A%0A    protected int getSlotWriteOut() %7B%0A        return SLOT_WRITE_OUT;%0A    %7D%0A%0A
     @Override%0A  
@@ -3704,29 +3704,32 @@
 kInSlot(
-SLOT_WRITE_IN
+getSlotWriteIn()
 ).isEmpt
@@ -3750,30 +3750,33 @@
 kInSlot(
-SLOT_WRITE_OUT
+getSlotWriteOut()
 ).isEmpt
@@ -3920,29 +3920,32 @@
 romSlot(
-SLOT_WRITE_IN
+getSlotWriteIn()
 ), proxy
@@ -3990,30 +3990,33 @@
 ontents(
-SLOT_WRITE_OUT
+getSlotWriteOut()
 , output

Add constructor to TileProxy to set a different inventory size