package common.inventory;

import common.block.Block;
import common.entity.npc.EntityNPC;
import common.item.Item;
import common.item.ItemStack;
import common.item.tool.ItemArmor;
import common.tags.TagObject;
import java.util.List;

public class InventoryPlayer implements IInventory
{
    /**
     * An array of 36 item stacks indicating the main player inventory (including the visible bar).
     */
    public ItemStack[] mainInventory = new ItemStack[36];

    /** An array of 4 item stacks containing the currently worn armor pieces. */
    public ItemStack[] armorInventory = new ItemStack[4];

    /** The index of the currently held item (0-8). */
    public int currentItem;

    /** The player whose inventory this is. */
    public EntityNPC player;
    private ItemStack itemStack;

    /**
     * Set true whenever the inventory changes. Nothing sets it false so you will have to write your own code to check
     * it and reset the value.
     */
    public boolean inventoryChanged;

    public InventoryPlayer(EntityNPC playerIn)
    {
        this.player = playerIn;
    }

    /**
     * Returns the item stack currently held by the player.
     */
    public ItemStack getCurrentItem()
    {
        return this.currentItem < 9 && this.currentItem >= 0 ? this.mainInventory[this.currentItem] : null;
    }

    /**
     * Get the size of the player hotbar inventory
     */
    public static int getHotbarSize()
    {
        return 9;
    }

    private int getInventorySlotContainItem(Item itemIn)
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            if (this.mainInventory[i] != null && this.mainInventory[i].getItem() == itemIn)
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * stores an itemstack in the users inventory
     */
    private int storeItemStack(ItemStack itemStackIn)
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            if (this.mainInventory[i] != null && this.mainInventory[i].getItem() == itemStackIn.getItem() && this.mainInventory[i].isStackable() && this.mainInventory[i].size < this.mainInventory[i].getMaxStackSize() && this.mainInventory[i].size < this.getInventoryStackLimit() && ItemStack.areItemStackTagsEqual(this.mainInventory[i], itemStackIn))
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * Returns the first item stack that is empty.
     */
    public int getFirstEmptyStack()
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            if (this.mainInventory[i] == null)
            {
                return i;
            }
        }

        return -1;
    }

    public boolean setCurrentItem(Item itemIn) // , boolean p_146030_4_)
    {
//        ItemStack itemstack = this.getCurrentItem();
        int i = this.getInventorySlotContainItem(itemIn);

        if (i >= 0 && i < 9)
        {
            this.currentItem = i;
            return true;
        }
        return false;
//        else if (p_146030_4_ && itemIn != null)
//        {
//            int j = this.getFirstEmptyStack();
//
//            if (j >= 0 && j < 9)
//            {
//                this.currentItem = j;
//            }
//
//            if (itemstack == null || !itemstack.isItemEnchantable() || this.getInventorySlotContainItemAndDamage(itemstack.getItem(), itemstack.getItemDamage()) != this.currentItem)
//            {
//                int k = this.getInventorySlotContainItemAndDamage(itemIn, metadataIn);
//                int l;
//
//                if (k >= 0)
//                {
//                    l = this.mainInventory[k].stackSize;
//                    this.mainInventory[k] = this.mainInventory[this.currentItem];
//                }
//                else
//                {
//                    l = 1;
//                }
//
//                this.mainInventory[this.currentItem] = new ItemStack(itemIn, l, metadataIn);
//            }
//        }
    }

    /**
     * Switch the current item to the next one or the previous one
     *  
     * @param direction Direction to switch (1, 0, -1). 1 (any > 0) to select item left of current (decreasing
     * currentItem index), -1 (any < 0) to select item right of current (increasing currentItem index). 0 has no effect.
     */
    public void changeCurrentItem(int direction)
    {
//        if (direction > 0)
//        {
//            direction = 1;
//        }
//
//        if (direction < 0)
//        {
//            direction = -1;
//        }

        for (this.currentItem -= direction; this.currentItem < 0; this.currentItem += 9)
        {
            ;
        }

        while (this.currentItem >= 9)
        {
            this.currentItem -= 9;
        }
    }
    
    public void clearItems() {
        for(int z = 0; z < this.mainInventory.length; z++) {
            this.mainInventory[z] = null;
        }
        for(int z = 0; z < this.armorInventory.length; z++) {
            this.armorInventory[z] = null;
        }
        this.itemStack = null;
    }

    /**
     * This function stores as many items of an ItemStack as possible in a matching slot and returns the quantity of
     * left over items.
     */
    private int storePartialItemStack(ItemStack itemStackIn)
    {
        Item item = itemStackIn.getItem();
        int i = itemStackIn.size;
        int j = this.storeItemStack(itemStackIn);

        if (j < 0)
        {
            j = this.getFirstEmptyStack();
        }

        if (j < 0)
        {
            return i;
        }
        else
        {
            if (this.mainInventory[j] == null)
            {
                this.mainInventory[j] = new ItemStack(item, 0);

                if (itemStackIn.hasTagCompound())
                {
                    this.mainInventory[j].setTagCompound(itemStackIn.getTagCompound().copy());
                }
            }

            int k = i;

            if (i > this.mainInventory[j].getMaxStackSize() - this.mainInventory[j].size)
            {
                k = this.mainInventory[j].getMaxStackSize() - this.mainInventory[j].size;
            }

            if (k > this.getInventoryStackLimit() - this.mainInventory[j].size)
            {
                k = this.getInventoryStackLimit() - this.mainInventory[j].size;
            }

            if (k == 0)
            {
                return i;
            }
            else
            {
                i = i - k;
                this.mainInventory[j].size += k;
//                this.mainInventory[j].animationsToGo = 5;
                return i;
            }
        }
    }

    /**
     * Decrement the number of animations remaining. Only called on client side. This is used to handle the animation of
     * receiving a block.
     */
//    public void decrementAnimations()
//    {
//        for (int i = 0; i < this.mainInventory.length; ++i)
//        {
//            if (this.mainInventory[i] != null)
//            {
//                this.mainInventory[i].updateAnimation(this.player.worldObj, this.player, i, this.currentItem == i);
//            }
//        }
//    }

    /**
     * removed one item of specified Item from inventory (if it is in a stack, the stack size will reduce with 1)
     */
    public boolean consumeInventoryItem(Item itemIn)
    {
        int i = this.getInventorySlotContainItem(itemIn);

        if (i < 0)
        {
            return false;
        }
        else
        {
            if (--this.mainInventory[i].size <= 0)
            {
                this.mainInventory[i] = null;
            }

            return true;
        }
    }

    /**
     * Checks if a specified Item is inside the inventory
     */
    public boolean hasItem(Item itemIn)
    {
        int i = this.getInventorySlotContainItem(itemIn);
        return i >= 0;
    }

    /**
     * Adds the item stack to the inventory, returns false if it is impossible.
     */
    public boolean addItemStackToInventory(final ItemStack itemStackIn)
    {
        if (itemStackIn != null && itemStackIn.size != 0 && itemStackIn.getItem() != null)
        {
            if (itemStackIn.isItemDamaged())
            {
                int j = this.getFirstEmptyStack();

                if (j >= 0)
                {
                    this.mainInventory[j] = ItemStack.copyItemStack(itemStackIn);
//                    this.mainInventory[j].animationsToGo = 5;
                    itemStackIn.size = 0;
                    return true;
                }
//                else if (this.player.creative)
//                {
//                    itemStackIn.stackSize = 0;
//                    return true;
//                }
                else
                {
                    return false;
                }
            }
            else
            {
                int i;

                while (true)
                {
                    i = itemStackIn.size;
                    itemStackIn.size = this.storePartialItemStack(itemStackIn);

                    if (itemStackIn.size <= 0 || itemStackIn.size >= i)
                    {
                        break;
                    }
                }

//                if (itemStackIn.stackSize == i && this.player.creative)
//                {
//                    itemStackIn.stackSize = 0;
//                    return true;
//                }
//                else
//                {
                return itemStackIn.size < i;
//                }
            }
        }
        else
        {
            return false;
        }
    }

    /**
     * Removes up to a specified number of items from an inventory slot and returns them in a new stack.
     */
    public ItemStack decrStackSize(int index, int count)
    {
        ItemStack[] aitemstack = this.mainInventory;

        if (index >= this.mainInventory.length)
        {
            aitemstack = this.armorInventory;
            index -= this.mainInventory.length;
        }

        if (aitemstack[index] != null)
        {
            if (aitemstack[index].size <= count)
            {
                ItemStack itemstack1 = aitemstack[index];
                aitemstack[index] = null;
                return itemstack1;
            }
            else
            {
                ItemStack itemstack = aitemstack[index].splitStack(count);

                if (aitemstack[index].size == 0)
                {
                    aitemstack[index] = null;
                }

                return itemstack;
            }
        }
        else
        {
            return null;
        }
    }

    /**
     * Removes a stack from the given slot and returns it.
     */
    public ItemStack removeStackFromSlot(int index)
    {
        ItemStack[] aitemstack = this.mainInventory;

        if (index >= this.mainInventory.length)
        {
            aitemstack = this.armorInventory;
            index -= this.mainInventory.length;
        }

        if (aitemstack[index] != null)
        {
            ItemStack itemstack = aitemstack[index];
            aitemstack[index] = null;
            return itemstack;
        }
        else
        {
            return null;
        }
    }

    /**
     * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
     */
    public void setInventorySlotContents(int index, ItemStack stack)
    {
        ItemStack[] aitemstack = this.mainInventory;

        if (index >= aitemstack.length)
        {
            index -= aitemstack.length;
            aitemstack = this.armorInventory;
        }

        aitemstack[index] = stack;
    }

    public float getStrVsBlock(Block blockIn)
    {
        float f = 1.0F;

        if (this.mainInventory[this.currentItem] != null)
        {
            f *= this.mainInventory[this.currentItem].getStrVsBlock(blockIn);
        }

        return f;
    }

    /**
     * Writes the inventory out as a list of compound tags. This is where the slot indices are used (+100 for armor, +80
     * for crafting).
     *  
     * @param nbtTagListIn List to append tags to
     */
    public List<TagObject> writeToNBT(List<TagObject> nbtTagListIn)
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            if (this.mainInventory[i] != null)
            {
                TagObject nbttagcompound = new TagObject();
                nbttagcompound.setByte("Slot", (byte)i);
                this.mainInventory[i].writeTags(nbttagcompound);
                nbtTagListIn.add(nbttagcompound);
            }
        }

        for (int j = 0; j < this.armorInventory.length; ++j)
        {
            if (this.armorInventory[j] != null)
            {
                TagObject nbttagcompound1 = new TagObject();
                nbttagcompound1.setByte("Slot", (byte)(j + 100));
                this.armorInventory[j].writeTags(nbttagcompound1);
                nbtTagListIn.add(nbttagcompound1);
            }
        }

        return nbtTagListIn;
    }

    /**
     * Reads from the given tag list and fills the slots in the inventory with the correct items.
     *  
     * @param nbtTagListIn tagList to read from
     */
    public void readFromNBT(List<TagObject> nbtTagListIn)
    {
        this.mainInventory = new ItemStack[36];
        this.armorInventory = new ItemStack[4];

        for (int i = 0; i < nbtTagListIn.size(); ++i)
        {
            TagObject nbttagcompound = nbtTagListIn.get(i);
            int j = nbttagcompound.getByte("Slot") & 255;
            ItemStack itemstack = ItemStack.readFromTag(nbttagcompound);

            if (itemstack != null)
            {
                if (j >= 0 && j < this.mainInventory.length)
                {
                    this.mainInventory[j] = itemstack;
                }

                if (j >= 100 && j < this.armorInventory.length + 100)
                {
                    this.armorInventory[j - 100] = itemstack;
                }
            }
        }
    }

    /**
     * Returns the number of slots in the inventory.
     */
    public int getSizeInventory()
    {
        return this.mainInventory.length + 4;
    }

    /**
     * Returns the stack in the given slot.
     */
    public ItemStack getStackInSlot(int index)
    {
        ItemStack[] aitemstack = this.mainInventory;

        if (index >= aitemstack.length)
        {
            index -= aitemstack.length;
            aitemstack = this.armorInventory;
        }

        return aitemstack[index];
    }

    /**
     * Get the name of this object. For players this returns their username
     */
    public String getName()
    {
        return "Inventar";
    }

    /**
     * Returns true if this thing is named
     */
    public boolean hasCustomName()
    {
        return false;
    }

    /**
     * Get the formatted ChatComponent that will be used for the sender's username in chat
     */
    public String getCommandName()
    {
        return this.getName(); //(TextComponent)(this.hasCustomName() ? new TextComponent(this.getName()) : new TextComponent(this.getName()));
    }

    /**
     * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended.
     */
    public int getInventoryStackLimit()
    {
        return ItemStack.MAX_SIZE;
    }

    public boolean canHeldItemHarvest(Block block) {
        if(!block.getMaterial().isToolRequired())
            return true;
        ItemStack stack = this.getStackInSlot(this.currentItem);
        return stack != null && stack.canHarvestBlock(block);
    }

    /**
     * returns a player armor item (as itemstack) contained in specified armor slot.
     *  
     * @param slotIn the slot index requested
     */
    public ItemStack armorItemInSlot(int slotIn)
    {
        return this.armorInventory[slotIn];
    }

    /**
     * Based on the damage values and maximum damage values of each armor item, returns the current armor value.
     */
    public int getTotalArmorValue()
    {
        int i = 0;

        for (int j = 0; j < this.armorInventory.length; ++j)
        {
            if (this.armorInventory[j] != null && this.armorInventory[j].getItem() instanceof ItemArmor)
            {
                int k = ((ItemArmor)this.armorInventory[j].getItem()).damageReduceAmount;
                i += k;
            }
        }

        return i;
    }

    /**
     * Damages armor in each slot by the specified amount.
     */
    public void damageArmor(int damage)
    {
        damage = damage / 4;

        if (damage < 1)
        {
            damage = 1;
        }

        for (int i = 0; i < this.armorInventory.length; ++i)
        {
            if (this.armorInventory[i] != null && this.armorInventory[i].getItem() instanceof ItemArmor)
            {
                this.armorInventory[i].damageItem(damage, this.player);

                if (this.armorInventory[i].size == 0)
                {
                    this.armorInventory[i] = null;
                }
            }
        }
    }

    /**
     * Drop all armor and main inventory items.
     */
    public void dropAllItems()
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            if (this.mainInventory[i] != null)
            {
                this.player.dropItem(this.mainInventory[i], true, false);
                this.mainInventory[i] = null;
            }
        }

        for (int j = 0; j < this.armorInventory.length; ++j)
        {
            if (this.armorInventory[j] != null)
            {
                this.player.dropItem(this.armorInventory[j], true, false);
                this.armorInventory[j] = null;
            }
        }
    }

    /**
     * For tile entities, ensures the chunk containing the tile entity is saved to disk later - the game won't think it
     * hasn't changed and skip it.
     */
    public void markDirty()
    {
        this.inventoryChanged = true;
    }

    /**
     * Set the stack helds by mouse, used in GUI/Container
     */
    public void setItemStack(ItemStack itemStackIn)
    {
        this.itemStack = itemStackIn;
    }

    /**
     * Stack helds by mouse, used in GUI and Containers
     */
    public ItemStack getItemStack()
    {
        return this.itemStack;
    }

    /**
     * Do not make give this method the name canInteractWith because it clashes with Container
     */
    public boolean isUseableByPlayer(EntityNPC player)
    {
        return this.player.dead ? false : player.getDistanceSqToEntity(this.player) <= 64.0D;
    }

    /**
     * Returns true if the specified ItemStack exists in the inventory.
     */
    public boolean hasItemStack(ItemStack itemStackIn)
    {
        for (int i = 0; i < this.armorInventory.length; ++i)
        {
            if (this.armorInventory[i] != null && this.armorInventory[i].isItemEqual(itemStackIn))
            {
                return true;
            }
        }

        for (int j = 0; j < this.mainInventory.length; ++j)
        {
            if (this.mainInventory[j] != null && this.mainInventory[j].isItemEqual(itemStackIn))
            {
                return true;
            }
        }

        return false;
    }

    public void openInventory(EntityNPC player)
    {
    }

    public void closeInventory(EntityNPC player)
    {
    }

    /**
     * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
     */
    public boolean isItemValidForSlot(int index, ItemStack stack)
    {
        return true;
    }

    /**
     * Copy the ItemStack contents from another InventoryPlayer instance
     */
    public void copyInventory(InventoryPlayer playerInventory)
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            this.mainInventory[i] = ItemStack.copyItemStack(playerInventory.mainInventory[i]);
        }

        for (int j = 0; j < this.armorInventory.length; ++j)
        {
            this.armorInventory[j] = ItemStack.copyItemStack(playerInventory.armorInventory[j]);
        }

        this.currentItem = playerInventory.currentItem;
    }

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

    public void setField(int id, int value)
    {
    }

    public int getFieldCount()
    {
        return 0;
    }

    public void clear()
    {
        for (int i = 0; i < this.mainInventory.length; ++i)
        {
            this.mainInventory[i] = null;
        }

        for (int j = 0; j < this.armorInventory.length; ++j)
        {
            this.armorInventory[j] = null;
        }
    }
}
