package org.cyclops.cyclopscore.inventory;

import com.google.common.collect.Lists;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.IChatComponent;
import org.cyclops.cyclopscore.helper.MinecraftHelpers;
import org.cyclops.cyclopscore.persist.IDirtyMarkListener;

import java.util.List;

/**
 * A basic inventory implementation.
 * @author rubensworks
 *
 */
public class SimpleInventory implements IInventory {

    private final ItemStack[] _contents;
    private final String _name;
    private final int _stackLimit;
    private final List<IDirtyMarkListener> dirtyMarkListeners = Lists.newLinkedList();

    /**
     * Make a new instance.
     * @param size The amount of slots in the inventory.
     * @param name The name of the inventory, used for NBT storage.
     * @param stackLimit The stack limit for each slot.
     */
    public SimpleInventory(int size, String name, int stackLimit) {
        _contents = new ItemStack[size];
        _name = name;
        _stackLimit = stackLimit;
    }

    /**
     * Add a dirty marking listener.
     * @param dirtyMarkListener The dirty mark listener.
     */
    public void addDirtyMarkListener(IDirtyMarkListener dirtyMarkListener) {
        this.dirtyMarkListeners.add(dirtyMarkListener);
    }

    /**
     * Remove a dirty marking listener.
     * @param dirtyMarkListener The dirty mark listener.
     */
    public void removeDirtyMarkListener(IDirtyMarkListener dirtyMarkListener) {
        this.dirtyMarkListeners.remove(dirtyMarkListener);
    }

    @Override
    public int getSizeInventory() {
        return _contents.length;
    }

    @Override
    public ItemStack getStackInSlot(int slotId) {
        return _contents[slotId];
    }

    @Override
    public ItemStack decrStackSize(int slotId, int count) {
        if (slotId < _contents.length && _contents[slotId] != null) {
            if (_contents[slotId].stackSize > count) {
                ItemStack result = _contents[slotId].splitStack(count);
                onInventoryChanged();
                return result;
            }
            ItemStack stack = _contents[slotId];
            _contents[slotId] = null;
            onInventoryChanged();
            return stack;
        }
        return null;
    }

    @Override
    public void setInventorySlotContents(int slotId, ItemStack itemstack) {
        if (slotId >= _contents.length) {
            return;
        }
        this._contents[slotId] = itemstack;

        if (itemstack != null && itemstack.stackSize > this.getInventoryStackLimit()) {
            itemstack.stackSize = this.getInventoryStackLimit();
        }
        onInventoryChanged();
    }

    @Override
    public int getInventoryStackLimit() {
        return _stackLimit;
    }

    protected void onInventoryChanged() {
        markDirty();
    }

    @Override
    public boolean isUseableByPlayer(EntityPlayer entityplayer) {
        return true;
    }

    @Override
    public void openInventory(EntityPlayer playerIn) {

    }

    @Override
    public void closeInventory(EntityPlayer playerIn) {

    }

    /**
     * Read inventory data from the given NBT.
     * @param data The NBT data containing inventory data.
     */
    public void readFromNBT(NBTTagCompound data) {
        readFromNBT(data, "items");
    }

    /**
     * Read inventory data from the given NBT.
     * @param data The NBT data containing inventory data.
     * @param tag The NBT tag name where the info is located.
     */
    public void readFromNBT(NBTTagCompound data, String tag) {
        NBTTagList nbttaglist = data.getTagList(tag, MinecraftHelpers.NBTTag_Types.NBTTagCompound.ordinal());
        
        for (int j = 0; j < _contents.length; ++j)
            _contents[j] = null;

        for (int j = 0; j < nbttaglist.tagCount(); ++j) {
            NBTTagCompound slot = nbttaglist.getCompoundTagAt(j);
            int index;
            if (slot.hasKey("index")) {
                index = slot.getInteger("index");
            } else {
                index = slot.getByte("Slot");
            }
            if (index >= 0 && index < _contents.length) {
                _contents[index] = ItemStack.loadItemStackFromNBT(slot);
            }
        }
    }

    /**
     * Write inventory data to the given NBT.
     * @param data The NBT tag that will receive inventory data.
     */
    public void writeToNBT(NBTTagCompound data) {
        writeToNBT(data, "items");
    }

    /**
     * Write inventory data to the given NBT.
     * @param data The NBT tag that will receive inventory data.
     * @param tag The NBT tag name where the info must be located.
     */
    public void writeToNBT(NBTTagCompound data, String tag) {
        NBTTagList slots = new NBTTagList();
        for (byte index = 0; index < _contents.length; ++index) {
            if (_contents[index] != null && _contents[index].stackSize > 0) {
                NBTTagCompound slot = new NBTTagCompound();
                slots.appendTag(slot);
                slot.setByte("Slot", index);
                _contents[index].writeToNBT(slot);
            }
        }
        data.setTag(tag, slots);
    }

    @Override
    public ItemStack getStackInSlotOnClosing(int slotId) {
        if (this._contents[slotId] == null) {
            return null;
        }

        ItemStack stackToTake = this._contents[slotId];
        this._contents[slotId] = null;
        return stackToTake;
    }

    /**
     * Get the array of {@link net.minecraft.item.ItemStack} inside this inventory.
     * @return The items in this inventory.
     */
    public ItemStack[] getItemStacks() {
        return _contents;
    }

    @Override
    public boolean isItemValidForSlot(int i, ItemStack itemstack) {
        return true;
    }

    @Override
    public int getField(int id) {
        return 0;
    }

    @Override
    public void setField(int id, int value) {

    }

    @Override
    public int getFieldCount() {
        return 0;
    }

    @Override
    public void clear() {
        for(int i = 0; i < getSizeInventory(); i++) {
            _contents[i] = null;
        }
    }

    @Override
	public void markDirty() {
		for(IDirtyMarkListener dirtyMarkListener : dirtyMarkListeners) {
            dirtyMarkListener.onDirty();
        }
	}

    @Override
    public String getCommandSenderName() {
        return _name;
    }

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

    @Override
    public IChatComponent getDisplayName() {
        return new ChatComponentTranslation(this.getCommandSenderName());
    }

    /**
     * @return If all slots are empty.
     */
    public boolean isEmpty() {
        for(int i = 0; i < getSizeInventory(); i++) {
            if(getStackInSlot(i) != null) {
                return false;
            }
        }
        return true;
    }

}
@@ -1353,24 +1353,37 @@
 %0A    public 
+synchronized 
 void addDirt
@@ -1619,24 +1619,37 @@
 %0A    public 
+synchronized 
 void removeD
@@ -6417,16 +6417,188 @@
 rty() %7B%0A
+        List%3CIDirtyMarkListener%3E dirtyMarkListeners;%0A        synchronized (this) %7B%0A            dirtyMarkListeners = Lists.newLinkedList(this.dirtyMarkListeners);%0A        %7D%0A
 %09%09for(ID

Add concurrency safety for inventory IDirtyMarkListeners