package codechicken.nei;

import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;

import java.util.ArrayList;

import static codechicken.lib.gui.GuiDraw.drawRect;

public class ItemPanel extends Widget
{
    /**
     * Should not be externally modified, use updateItemList
     */
    public static ArrayList<ItemStack> items = new ArrayList<ItemStack>();
    /**
     * Swapped into visible items on update
     */
    private static ArrayList<ItemStack> _items = items;

    public static void updateItemList(ArrayList<ItemStack> newItems) {
        _items = newItems;
    }

    public class ItemPanelSlot
    {
        public ItemStack item;
        public int slotIndex;

        public ItemPanelSlot(int index) {
            item = items.get(index);
            slotIndex = index;
        }
    }

    public ItemStack draggedStack;
    public int mouseDownSlot = -1;

    private int marginLeft;
    private int marginTop;
    private int rows;
    private int columns;

    private boolean[] validSlotMap;
    private int firstIndex;
    private int itemsPerPage;

    private int page;
    private int numPages;

    public void resize() {
        items = _items;

        marginLeft = x + (w % 18) / 2;
        marginTop = y + (h % 18) / 2;
        columns = w / 18;
        rows = h / 18;
        //sometimes width and height can be negative with certain resizing
        if(rows < 0) rows = 0;
        if(columns < 0) columns = 0;

        calculatePage();
        updateValidSlots();
    }

    private void calculatePage() {
        if (itemsPerPage == 0)
            numPages = 0;
        else
            numPages = (int) Math.ceil((float) items.size() / (float) itemsPerPage);

        if (firstIndex >= items.size())
            firstIndex = 0;

        if (numPages == 0)
            page = 0;
        else
            page = firstIndex / itemsPerPage + 1;
    }

    private void updateValidSlots() {
        GuiContainer gui = NEIClientUtils.getGuiContainer();
        validSlotMap = new boolean[rows * columns];
        itemsPerPage = 0;
        for (int i = 0; i < validSlotMap.length; i++)
            if (slotValid(gui, i)) {
                validSlotMap[i] = true;
                itemsPerPage++;
            }
    }

    private boolean slotValid(GuiContainer gui, int i) {
        Rectangle4i rect = getSlotRect(i);
        for (INEIGuiHandler handler : GuiInfo.guiHandlers)
            if (handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h))
                return false;
        return true;
    }

    public Rectangle4i getSlotRect(int i) {
        return getSlotRect(i / columns, i % columns);
    }

    public Rectangle4i getSlotRect(int row, int column) {
        return new Rectangle4i(marginLeft + column * 18, marginTop + row * 18, 18, 18);
    }

    @Override
    public void draw(int mousex, int mousey) {
        if (itemsPerPage == 0)
            return;

        GuiContainerManager.enableMatrixStackLogging();
        int index = firstIndex;
        for (int i = 0; i < rows * columns && index < items.size(); i++) {
            if (validSlotMap[i]) {
                Rectangle4i rect = getSlotRect(i);
                if (rect.contains(mousex, mousey))
                    drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555);//highlight

                GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, items.get(index));

                index++;
            }
        }
        GuiContainerManager.disableMatrixStackLogging();
    }

    @Override
    public void postDraw(int mousex, int mousey) {
        if (draggedStack != null) {
            GuiContainerManager.drawItems.zLevel += 100;
            GuiContainerManager.drawItem(mousex - 8, mousey - 8, draggedStack);
            GuiContainerManager.drawItems.zLevel -= 100;
        }
    }

    @Override
    public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
        if (mouseDownSlot >= 0 && draggedStack == null && NEIClientUtils.getHeldItem() == null &&
                NEIClientConfig.hasSMPCounterPart() && !GuiInfo.hasCustomSlots(NEIClientUtils.getGuiContainer())) {
            ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
            ItemStack stack = new ItemPanelSlot(mouseDownSlot).item;
            if (stack != null && (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500)) {
                int amount = NEIClientConfig.getItemQuantity();
                if (amount == 0)
                    amount = stack.getMaxStackSize();

                draggedStack = NEIServerUtils.copyStack(stack, amount);
            }
        }
    }

    @Override
    public boolean handleClick(int mousex, int mousey, int button) {
        if (handleDraggedClick(mousex, mousey, button))
            return true;

        if (NEIClientUtils.getHeldItem() != null) {
            for (INEIGuiHandler handler : GuiInfo.guiHandlers)
                if (handler.hideItemPanelSlot(NEIClientUtils.getGuiContainer(), mousex, mousey, 1, 1))
                    return false;

            if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item"))
                if (button == 1)
                    NEIClientUtils.decreaseSlotStack(-999);
                else
                    NEIClientUtils.deleteHeldItem();
            else
                NEIClientUtils.dropHeldItem();

            return true;
        }

        ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
        if (hoverSlot != null) {
            if (button == 2) {
                ItemStack stack = hoverSlot.item;
                if (stack != null) {
                    int amount = NEIClientConfig.getItemQuantity();
                    if (amount == 0)
                        amount = stack.getMaxStackSize();

                    draggedStack = NEIServerUtils.copyStack(stack, amount);
                }
            } else {
                mouseDownSlot = hoverSlot.slotIndex;
            }
            return true;
        }
        return false;
    }

    private boolean handleDraggedClick(int mousex, int mousey, int button) {
        if (draggedStack == null)
            return false;

        GuiContainer gui = NEIClientUtils.getGuiContainer();
        boolean handled = false;
        for (INEIGuiHandler handler : GuiInfo.guiHandlers)
            if (handler.handleDragNDrop(gui, mousex, mousey, draggedStack, button)) {
                handled = true;
                if (draggedStack.stackSize == 0) {
                    draggedStack = null;
                    return true;
                }
            }

        if (handled)
            return true;

        Slot overSlot = gui.getSlotAtPosition(mousex, mousey);
        if (overSlot != null && overSlot.isItemValid(draggedStack)) {
            if (NEIClientConfig.canCheatItem(draggedStack)) {
                int contents = overSlot.getHasStack() ? overSlot.getStack().stackSize : 0;
                int add = button == 0 ? draggedStack.stackSize : 1;
                if (overSlot.getHasStack() && !NEIServerUtils.areStacksSameType(draggedStack, overSlot.getStack()))
                    contents = 0;
                int total = Math.min(contents + add, Math.min(overSlot.getSlotStackLimit(), draggedStack.getMaxStackSize()));

                if (total > contents) {
                    NEIClientUtils.setSlotContents(overSlot.slotNumber, NEIServerUtils.copyStack(draggedStack, total), true);
                    NEICPH.sendGiveItem(NEIServerUtils.copyStack(draggedStack, total), false, false);
                    draggedStack.stackSize -= total - contents;
                }
                if (draggedStack.stackSize == 0)
                    draggedStack = null;
            } else {
                draggedStack = null;
            }
        } else if (mousex < gui.guiLeft || mousey < gui.guiTop || mousex >= gui.guiLeft + gui.xSize || mousey >= gui.guiTop + gui.ySize) {
            draggedStack = null;
        }

        return true;
    }

    @Override
    public boolean handleClickExt(int mousex, int mousey, int button) {
        return handleDraggedClick(mousex, mousey, button);
    }

    @Override
    public void mouseUp(int mousex, int mousey, int button) {
        ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
        if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && draggedStack == null) {
            ItemStack item = hoverSlot.item;
            if (NEIController.manager.window instanceof GuiRecipe || !NEIClientConfig.canCheatItem(item)) {
                if (button == 0)
                    GuiCraftingRecipe.openRecipeGui("item", item);
                else if (button == 1)
                    GuiUsageRecipe.openRecipeGui("item", item);

                draggedStack = null;
                mouseDownSlot = -1;
                return;
            }

            NEIClientUtils.cheatItem(item, button, -1);
        }

        mouseDownSlot = -1;
    }

    @Override
    public boolean onMouseWheel(int i, int mousex, int mousey) {
        if (!contains(mousex, mousey))
            return false;

        scroll(-i);
        return true;
    }

    @Override
    public boolean handleKeyPress(int keyID, char keyChar) {
        if (keyID == NEIClientConfig.getKeyBinding("gui.next")) {
            scroll(1);
            return true;
        }
        if (keyID == NEIClientConfig.getKeyBinding("gui.prev")) {
            scroll(-1);
            return true;
        }
        return false;
    }

    @Override
    public ItemStack getStackMouseOver(int mousex, int mousey) {
        ItemPanelSlot slot = getSlotMouseOver(mousex, mousey);
        return slot == null ? null : slot.item;
    }

    public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
        int index = firstIndex;
        for (int i = 0; i < rows * columns && index < items.size(); i++)
            if (validSlotMap[i]) {
                if (getSlotRect(i).contains(mousex, mousey))
                    return new ItemPanelSlot(index);
                index++;
            }

        return null;
    }

    public void scroll(int i) {
        if (itemsPerPage != 0) {
            int oldIndex = firstIndex;
            firstIndex += i * itemsPerPage;
            if (firstIndex >= items.size())
                firstIndex = 0;
            if (firstIndex < 0)
                if (oldIndex > 0)
                    firstIndex = 0;
                else
                    firstIndex = (items.size() - 1) / itemsPerPage * itemsPerPage;

            calculatePage();
        }
    }

    public int getPage() {
        return page;
    }

    public int getNumPages() {
        return numPages;
    }

    @Override
    public boolean contains(int px, int py) {
        GuiContainer gui = NEIClientUtils.getGuiContainer();
        Rectangle4i rect = new Rectangle4i(px, py, 1, 1);
        for (INEIGuiHandler handler : GuiInfo.guiHandlers)
            if (handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h))
                return false;

        return super.contains(px, py);
    }
}