package client.gui.container;

import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;

import client.Client;
import client.gui.Font;
import client.gui.Gui;
import client.gui.element.ActButton;
import client.gui.element.Bar;
import client.gui.element.ButtonCallback;
import client.gui.element.Field;
import client.gui.element.FontLabel;
import client.gui.element.DisplayLabel;
import client.gui.element.InventoryButton;
import client.gui.element.Label;
import client.gui.element.MultiLabel;
import client.gui.ingame.GuiRename;
import client.renderer.Drawing;
import client.renderer.GlState;
import client.renderer.ItemRenderer;
import client.renderer.entity.RenderItem;
import client.window.Bind;
import client.window.Button;
import common.collect.Lists;
import common.collect.Sets;
import common.enchantment.Enchantment;
import common.entity.npc.Attribute;
import common.entity.npc.EntityNPC;
import common.init.ItemRegistry;
import common.inventory.Container;
import common.inventory.Slot;
import common.item.CheatTab;
import common.item.ItemStack;
import common.packet.CPacketAction;
import common.packet.CPacketCheat;
import common.util.ExtMath;
import common.util.Util;
import common.util.Color;

public abstract class GuiContainer extends Gui
{
	private class Overlay {
		private final ItemStack stack;
		private final int x;
		private final int y;
		private final String alt;
		
		private Overlay(ItemStack stack, int x, int y, String alt) {
			this.stack = stack;
			this.x = x;
			this.y = y;
			this.alt = alt;
		}
	}

	private static final DecimalFormat DECIMALFORMAT = new DecimalFormat("#.###");
    private static final List<ItemStack> ITEM_LIST = Lists.<ItemStack>newArrayList();
    
    private static CheatTab selectedTab = CheatTab.ALL;

	protected RenderItem itemRender;
    protected int xSize = 14 + 18 * 12;
    protected int ySize = 166;
    public Container inventorySlots;
    private Slot theSlot;
    protected final Set<Slot> dragSplittingSlots = Sets.<Slot>newHashSet();
    protected final List<Overlay> drawnOverlays = Lists.<Overlay>newArrayList();
    protected boolean dragSplitting;
    private int dragSplittingLimit;
    private int dragSplittingButton;
    private boolean ignoreMouseUp;
    private int dragSplittingRemnant;
    private long lastClickTime;
    private Slot lastClickSlot;
    private int lastClickButton;
    private boolean doubleClick;
    private ItemStack shiftClickedSlot;

    protected int container_x;
    protected int container_y;
    protected int container_w;
    protected int container_h;
    protected int container_scale;

    private int hover_x;
    private int hover_y;
    private String tooltip;

    private float currentScroll;
    private boolean isScrolling;
    private boolean wasClicking;
    private ItemStack cheatStack;
    private int cheatX;
    private int cheatY;
    private int cheatWidth;
    private int cheatHeight;
    private Label cheatLabel;
    private MultiLabel cheatDesc;
    private Field cheatSearch;
    private String cheatLast;

	public static String formatAmount(int amount) {
		if(amount < 0)
			return "0";
		if(amount < 1000)
			return "" + amount;
		else if(amount < 100000)
			return (amount / 1000) + "K";
		else if(amount < 1000000)
			return "." + (amount / 100000) + "M";
		else if(amount < 100000000)
			return (amount / 1000000) + "M";
		else if(amount < 1000000000)
			return "." + (amount / 100000000) + "B";
		else
			return (amount / 1000000000) + "B+";
	}

	protected List<String> getTooltip(ItemStack stack) {
		List<String> list = Lists.<String>newArrayList();
		String s = stack.getColoredName();
		if(stack.getSize() != 1)
			s = Color.YELLOW + "" + stack.getSize() + " " + Color.RESET + s;
		s = s + Color.RESET;
		list.add(s);
		if(stack.getItem().getDescription() != null) {
			for(String line : stack.getItem().getDescription()) {
				list.add(Color.GRAY + line);
			}
		}
		stack.getItem().addInformation(stack, this.gm.player, list);
		if(stack.getItem().isAdminItem())
			list.add(Color.RED + "Admin-Gegenstand");
		if(stack.getItem().isMagnetic())
			list.add(Color.LIGHT_GRAY + "Magnetisch");
		if(stack.getItem().isFragile())
			list.add(Color.YELLOW + "Zerbrechlich");
		if(stack.getItem().canBeDyed())
			list.add("Farbe: #" + Integer.toHexString(stack.getDyeColor()).toUpperCase());
		if(stack.getItem().getExplosive() > 0)
			list.add(Color.RED + "Explosiv" + Color.CRIMSON + ": " + Color.DARK_RED + stack.getItem().getExplosive() + "x");
		else if(stack.getItem().getFuelAmount() > 0)
			list.add(Color.ORANGE + "Brennwert" + Color.YELLOW + ": " + Color.LIGHT_YELLOW + stack.getItem().getFuelAmount());

		if(stack.isItemEnchanted()) {
			for(Entry<Enchantment, Integer> enc : stack.getEnchantments()) {
				list.add(enc.getKey().getFormattedName(enc.getValue()));
			}
		}

		Map<Attribute, Float> mods = stack.getAttributeModifiers();
		mods.putAll(stack.getArmorModifiers(this.gm.player, null));

		if(!mods.isEmpty()) {
			list.add("");
			for(Entry<Attribute, Float> entry : mods.entrySet()) {
				float amt = entry.getValue();
				if(stack.getSize() != 1) {
					double total = amt * (double)stack.getSize();
					if(amt > 0.0D)
						list.add(Color.BLUE + String.format("+%s %s [%dx +%s]", DECIMALFORMAT.format(total), entry.getKey(), stack.getSize(),
								DECIMALFORMAT.format(amt)));
					else if(amt < 0.0D)
						list.add(Color.RED + String.format("-%s %s [%dx -%s]", DECIMALFORMAT.format(total * -1.0), entry.getKey(), stack.getSize(),
								DECIMALFORMAT.format(amt * -1.0)));
				}
				else {
					if(amt > 0.0D)
						list.add(Color.BLUE + String.format("+%s %s", DECIMALFORMAT.format(amt), entry.getKey()));
					else if(amt < 0.0D)
						list.add(Color.RED + String.format("-%s %s", DECIMALFORMAT.format(amt * -1.0), entry.getKey()));
				}
			}
		}

		if(stack.getItem().isDamageable()) {
			list.add(String.format("Haltbarkeit: %d" + (stack.isItemDamaged() ? " / %d" : ""),
					stack.isItemDamaged() ? (stack.getItem().getMaxDamage() - stack.getItemDamage()) : stack.getItem().getMaxDamage(), stack.getItem().getMaxDamage()));
		}

		if(stack.getRepairCost() > 0)
			list.add("Reparaturkosten: " + stack.getRepairCost() + " Mana");

		if(stack.getItem().getMaxAmount() == 1)
			list.add("Nicht stapelbar");
		else
			list.add("Stapelbar bis " + stack.getItem().getMaxAmount());

		list.add(Color.GRAY + ItemRegistry.getName(stack.getItem()));

		return list;
	}

	public Label label(String text, int x, int y) {
		x = x * this.container_scale + this.container_x;
		y = y * this.container_scale + this.container_y;
		return this.add(new FontLabel(x, y, 300, this.container_scale > 1 ? Font.LARGE : Font.SMALL, text, true));
	}

	public DisplayLabel display(String text, int x, int y, int w, int lines) {
		x = x * this.container_scale + this.container_x;
		y = y * this.container_scale + this.container_y;
		return this.add(new DisplayLabel(x, y, w * this.container_scale, lines, this.container_scale > 1 ? Font.LARGE : Font.TINY, text));
	}

	public Bar bar(int x, int y, int w, int h) {
		x = x * this.container_scale + this.container_x;
		y = y * this.container_scale + this.container_y;
		return this.add(new Bar(x, y, w * this.container_scale, h * this.container_scale, this.container_scale > 1 ? Font.LARGE : Font.TINY));
	}
	
    public void rect(int x, int y, int width, int height, int color) {
    	Drawing.drawRect(this.container_x + x * this.container_scale, this.container_y + y * this.container_scale, width * this.container_scale, height * this.container_scale, 0xff000000 | color);
	}
	
    public void grad(int x, int y, int width, int height, int top, int bottom, int topleft, int btmright) {
    	Drawing.drawGradient(this.container_x + x * this.container_scale, this.container_y + y * this.container_scale, width * this.container_scale, height * this.container_scale, 0xff000000 | top, 0xff000000 | bottom, 0xff000000 | topleft, 0xff000000 | btmright);
	}
    
	public InventoryButton slot(int x, int y, int w, int h, Slot slot) {
		return this.add(new InventoryButton(this.container_x + x * this.container_scale, this.container_y + y * this.container_scale, w * this.container_scale, h * this.container_scale, this.container_scale > 1, slot));
	}
    
	public InventoryButton slot(int x, int y, int w, int h) {
		return this.slot(x, y, w, h, null);
	}
    
	public ActButton button(int x, int y, int w, int h, ButtonCallback callback, String text) {
		return this.add(new ActButton(this.container_x + x * this.container_scale, this.container_y + y * this.container_scale, w * this.container_scale, h * this.container_scale, callback, text));
	}

	public void mouse(Button btn, int x, int y, boolean ctrl, boolean shift) {
		super.mouse(btn, x, y, ctrl, shift);
		this.mouseClicked((x - this.container_x) / this.container_scale, (y - this.container_y) / this.container_scale, btn.ordinal()); //TODO: enum
	}
    
    public void mouserel(Button btn, int x, int y) {
    	super.mouserel(btn, x, y);
		this.mouseReleased((x - this.container_x) / this.container_scale, (y - this.container_y) / this.container_scale, btn.ordinal()); //TODO: enum
	}
	
	public void drag(int x, int y) {
		super.drag(x, y);
		if(Button.MOUSE_LEFT.isDown() || Button.MOUSE_RIGHT.isDown() || Button.MOUSE_MIDDLE.isDown())
			this.mouseDragged((x - this.container_x) / this.container_scale, (y - this.container_y) / this.container_scale);
	}

    public GuiContainer(Container container)
    {
        this.inventorySlots = container;
        this.ignoreMouseUp = true;
    }

    public GuiContainer(Container container, EntityNPC player)
    {
        this(container);
        this.ySize = 90 + 18 * ((player.getInventoryCapacity() + 11) / 12);
    }
	
	public void init(int width, int height) {
		this.itemRender = this.gm.getRenderItem();
		this.tooltip = null;
		this.cheatStack = null;
		this.container_scale = this.gm.scaleItems && this.xSize * 2 <= Client.MIN_WIDTH && this.ySize * 2 <= Client.MIN_HEIGHT ? 2 : 1;
		this.container_x = (width - (this.container_w = (this.xSize * this.container_scale))) / 2;
		this.container_y = (height - (this.container_h = (this.ySize * this.container_scale))) / 2;
		this.initGui();
		this.addButtons();
		this.addElements();
	}

	public void hover(String text, int x, int y) {
		this.tooltip = text;
		this.hover_x = x * this.container_scale + this.container_x + 16;
		this.hover_y = y * this.container_scale + this.container_y + 16;
	}
	
	public String getTitle() {
		return "Inventar";
	}
    
    public void initGui()
    {
        this.gm.player.openContainer = this.inventorySlots;
        if(this.gm.itemCheat) {
        	this.cheatX = this.container_x + this.container_w + 4;
        	this.cheatY = 16;
        	this.cheatWidth = Math.max((this.gm.fbX - this.container_x - this.container_w - 16) / 18, 1);
        	this.cheatHeight = Math.max((this.gm.fbY - 20 * ((CheatTab.values().length + (this.cheatWidth - 1)) / this.cheatWidth) - 100) / 18, 1);
        }
    }
    
    public void addButtons() {
    	if(this.inventorySlots != null) {
	        for (int i1 = 0; i1 < this.inventorySlots.inventorySlots.size(); ++i1) {
	        	Slot slot = this.inventorySlots.inventorySlots.get(i1);
	        	if(slot.getTexture() == null || !slot.getTexture().isEmpty())
	        		this.slot(slot.xDisplayPosition - 1, slot.yDisplayPosition - 1, 18, 18, slot);
	        }
    	}
    	if(this.gm.itemCheat) {
        	this.cheatLabel = this.add(new FontLabel(this.cheatX, this.cheatY, this.cheatWidth * 18, Font.SMALL, "", true));
        	this.cheatDesc = this.add(new DisplayLabel(this.cheatX, this.cheatY + this.cheatHeight * 18 + 20 * ((CheatTab.values().length + (this.cheatWidth - 1)) / this.cheatWidth) + 4 + 18, this.cheatWidth * 18, 4, Font.SMALL, "Vorsicht: Schummeln\nwird mit Keule bestraft!!\n(Halte Strg beim Klick\nfür vollen Stapel)"));
        	this.cheatSearch = this.add(new Field(this.cheatX, this.cheatY + this.cheatHeight * 18 + 20 * ((CheatTab.values().length + (this.cheatWidth - 1)) / this.cheatWidth) + 4, this.cheatWidth * 18, 0, 128, null, ""));
        	this.cheatLast = "";
	    	for(CheatTab tab : CheatTab.values()) {
	    		this.add(new InventoryButton(this.cheatX + 18 * (tab.getIndex() % this.cheatWidth), this.cheatY + this.cheatHeight * 18 + 4 + 20 * (tab.getIndex() / this.cheatWidth), 18, 18, false, null));
	        }
            this.setCurrentTab(selectedTab);
    	}
    }
    
    public abstract void addElements();
    
    protected void drawSlots(int mouseX, int mouseY) {
        for (int i1 = 0; i1 < this.inventorySlots.inventorySlots.size(); ++i1)
        {
            Slot slot = (Slot)this.inventorySlots.inventorySlots.get(i1);
            this.drawSlot(slot);

            if (this.isMouseOverSlot(slot, mouseX, mouseY) && slot.canBeHovered())
            {
                this.theSlot = slot;
            }
        }
    	
    	if(this.gm.itemCheat) {
        	GL11.glPushMatrix();
        	GL11.glTranslatef(-(float)((this.gm.fbX - this.xSize * this.container_scale) / 2) * (1.0f / (float)this.container_scale), -(float)((this.gm.fbY - this.ySize * this.container_scale) / 2) * (1.0f / (float)this.container_scale), 0.0f);
        	if(this.container_scale != 1)
        		GL11.glScalef(1.0f / (float)this.container_scale, 1.0f / (float)this.container_scale, 1.0f / (float)this.container_scale);
	        int i = (ITEM_LIST.size() + this.cheatWidth - 1) / this.cheatWidth - this.cheatHeight;
	        int j = (int)((double)(this.currentScroll * (float)i) + 0.5D);
	
	        if (j < 0)
	        {
	            j = 0;
	        }
	
	        for (int k = 0; k < this.cheatHeight; ++k)
	        {
	            for (int l = 0; l < this.cheatWidth; ++l)
	            {
	                int i1 = l + (k + j) * this.cheatWidth;
	
	                if (i1 >= 0 && i1 < ITEM_LIST.size()) {
	
	                    this.itemRender.zLevel = 100.0F;
	                    GlState.enableDepth();
	                    this.itemRender.renderItemAndEffectIntoGUI(ITEM_LIST.get(i1), this.cheatX + 18 * l + 1, this.cheatY + k * 18 + 1);
	                    this.itemRender.zLevel = 0.0F;
	                    if(this.isPointInRegion(this.cheatX + 18 * l + 1, this.cheatY + k * 18 + 1, 16, 16, this.gm.mouseX, this.gm.mouseY))
	                        this.renderToolTip(ITEM_LIST.get(i1), mouseX, mouseY);
	                }
	            }
	        }
	
	        for (CheatTab tabs : CheatTab.values())
	        {
	            this.drawTab(tabs);
	        }
            GL11.glPopMatrix();
    	}
    }

    public void drawOverlays() {
    	for(Overlay overlay : this.drawnOverlays) {
    		this.renderItemOverlayIntoGUI(overlay.stack, overlay.x, overlay.y, overlay.alt);
    	}
    	this.drawnOverlays.clear();
		if(this.tooltip != null) {
			int width = Drawing.getBoxWidth(this.tooltip);
			Drawing.drawTextbox(this.tooltip, this.hover_x + width > this.gm.fbX ? this.hover_x - width - 32 : this.hover_x, this.hover_y, 0xaf000000);
		}
		this.tooltip = null;
    }

    public void drawScreen(int mouseX, int mouseY)
    {
    	if(this.gm == null)
    		return;
    	if(this.gm.itemCheat) {
	        boolean flag = Button.MOUSE_LEFT.isDown(); //TODO: remove?
	        int x1 = this.cheatX + this.cheatWidth * 18 + 2;
	        int y1 = this.cheatY;
	        int x2 = x1 + 8;
	        int y2 = y1 + this.cheatHeight * 18;
	
	        if (!this.wasClicking && flag && this.gm.mouseX >= x1 && this.gm.mouseY >= y1 && this.gm.mouseX < x2 && this.gm.mouseY < y2)
	        {
	            this.isScrolling = this.needsScrollBars();
	        }
	
	        if (!flag)
	        {
	            this.isScrolling = false;
	        }
	
	        this.wasClicking = flag;
	
	        if (this.isScrolling)
	        {
	            this.currentScroll = ((float)(this.gm.mouseY - y1) - 7.5F) / ((float)(y2 - y1) - 15.0F);
	            this.currentScroll = ExtMath.clampf(this.currentScroll, 0.0F, 1.0F);
	        }
    	}
        GlState.disableRescaleNormal();
        ItemRenderer.disableStandardItemLighting();
        GlState.disableLighting();
        GlState.disableDepth();
        ItemRenderer.enableGUIStandardItemLighting();
        GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
        GlState.enableRescaleNormal();
        this.theSlot = null;
        int k = 240;
        int l = 240;
        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, (float)k / 1.0F, (float)l / 1.0F);
        GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
        
        this.drawSlots(mouseX, mouseY);

        ItemRenderer.disableStandardItemLighting();
        ItemRenderer.enableGUIStandardItemLighting();
        ItemStack stack = this.gm.player.getMouseItem();
        if(this.gm.itemCheat)
        	stack = stack == null ? this.cheatStack : stack;
        if(Bind.CRAFT.isDown())
        	stack = stack == null && this.theSlot != null && this.theSlot.canCheatItem() && this.theSlot.getHasStack() ? this.gm.player.inventoryContainer.getSingleRecipe(this.theSlot.getStack()) : stack;

        if (stack != null)
        {
            int j2 = 8;
            int k2 = 8;
            String s = null;

            if (this.dragSplitting && this.dragSplittingSlots.size() > 1)
            {
                stack = stack.copy();
                stack.setSize(this.dragSplittingRemnant);

                if (stack.isEmpty())
                {
                    s = "" + Color.YELLOW + "0";
                }
            }
            else if(stack == this.cheatStack) {
            	s = Color.DARK_GREEN + "+" + Color.GREEN + formatAmount(stack.getSize());
            }

            this.drawItemStack(stack, mouseX - j2, mouseY - k2, s);
        }

        if (this.gm.player.getMouseItem() == null && this.cheatStack == null && stack != null)
        {
            this.renderToolTip(stack, mouseX, mouseY);
        }
        else if (this.gm.player.getMouseItem() == null && this.cheatStack == null && this.theSlot != null && this.theSlot.getHasStack())
        {
            ItemStack itemstack1 = this.theSlot.getStack();
            this.renderToolTip(itemstack1, mouseX, mouseY);
        }

        GlState.enableLighting();
        GlState.enableDepth();
        ItemRenderer.enableStandardItemLighting();

        if(this.gm.itemCheat) {
	        for (CheatTab tab : CheatTab.values())
	        {
	            if (this.renderInventoryHoveringText(tab, mouseX, mouseY))
	            {
	                break;
	            }
	        }
	
	        GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
	        GlState.disableLighting();
        }
    }

    private void drawItemStack(ItemStack stack, int x, int y, String altText)
    {
        GL11.glTranslatef(0.0F, 0.0F, 32.0F);
        this.itemRender.zLevel = 200.0F;
        this.itemRender.renderItemAndEffectIntoGUI(stack, x, y);
        this.drawnOverlays.add(new Overlay(stack, x, y, altText));
        this.itemRender.zLevel = 0.0F;
    }

	protected void renderToolTip(ItemStack stack, int x, int y) {
		List<String> list = this.getTooltip(stack);
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < list.size(); ++i) {
			if(i != 0)
				sb.append("\n" + Color.LIGHT_GRAY + (String)list.get(i));
			else
				sb.append((String)list.get(i));
		}
		this.hover(sb.toString(), x, y);
	}
    
    public void draw() {
    	super.draw();
    	if(this.gm.itemCheat) {
    		if(this.needsScrollBars()) {
	            int x1 = this.cheatX + this.cheatWidth * 18 + 2;
	            int y1 = this.cheatY;
	            int y2 = y1 + this.cheatHeight * 18;
	            Drawing.drawRect(x1, y1, 8, this.cheatHeight * 18, 0xff000000);
	            Drawing.drawGradient(x1, y1 + (int)((float)(y2 - y1 - 15) * this.currentScroll), 8, 15, this.gm.style.fill_top, this.gm.style.fill_btm, this.gm.style.brdr_top, this.gm.style.brdr_btm);
    		}
    		Drawing.drawRect(this.cheatX + 18 * (selectedTab.getIndex() % this.cheatWidth), this.cheatY + this.cheatHeight * 18 + 4 + 20 * (selectedTab.getIndex() / this.cheatWidth) - 2, 18, 2, 0xffffffff);
    	}
    	if(Bind.isWindowActive())
			super.drawOverlay();
    }

	public void drawOverlay() {
	}

	public void drawPost() {
    	GL11.glPushMatrix();
    	GL11.glTranslatef((float)((this.gm.fbX - this.xSize * this.container_scale) / 2), (float)((this.gm.fbY - this.ySize * this.container_scale) / 2), 0.0f);
    	if(this.container_scale != 1)
    		GL11.glScalef((float)this.container_scale, (float)this.container_scale, (float)this.container_scale);
		this.drawScreen((this.gm.mouseX - this.container_x) / this.container_scale, (this.gm.mouseY - this.container_y) / this.container_scale);
        GL11.glPopMatrix();
        ItemRenderer.disableStandardItemLighting();
	}

    private void drawSlot(Slot slotIn)
    {
        int i = slotIn.xDisplayPosition;
        int j = slotIn.yDisplayPosition;
        ItemStack itemstack = slotIn.getStack();
        boolean flag = false;
        ItemStack itemstack1 = this.gm.player.getMouseItem();
        String s = null;

        if (this.dragSplitting && this.dragSplittingSlots.contains(slotIn) && itemstack1 != null)
        {
            if (this.dragSplittingSlots.size() == 1)
            {
                return;
            }

            if (Container.canAddItemToSlot(slotIn, itemstack1, true) && this.inventorySlots.canDragIntoSlot(slotIn))
            {
                itemstack = itemstack1.copy();
                flag = true;
                Container.computeStackSize(this.dragSplittingSlots, this.dragSplittingLimit, itemstack, slotIn.getStack() == null ? 0 : slotIn.getStack().getSize());

                if (itemstack.getSize() > itemstack.getMaxStackSize())
                {
                    s = Color.YELLOW + formatAmount(itemstack.getMaxStackSize());
                    itemstack.setSize(itemstack.getMaxStackSize());
                }

                if (itemstack.getSize() > slotIn.getItemStackLimit(itemstack))
                {
                    s = Color.YELLOW + formatAmount(slotIn.getItemStackLimit(itemstack));
                    itemstack.setSize(slotIn.getItemStackLimit(itemstack));
                }
            }
            else
            {
                this.dragSplittingSlots.remove(slotIn);
                this.updateDragSplitting();
            }
        }

        this.itemRender.zLevel = 100.0F;

        GlState.enableDepth();
        this.itemRender.renderItemAndEffectIntoGUI(itemstack, i, j);
        this.drawnOverlays.add(new Overlay(itemstack, i, j, s));

        this.itemRender.zLevel = 0.0F;
    }

    private void updateDragSplitting()
    {
        ItemStack itemstack = this.gm.player.getMouseItem();

        if (itemstack != null && this.dragSplitting)
        {
            this.dragSplittingRemnant = itemstack.getSize();

            for (Slot slot : this.dragSplittingSlots)
            {
                ItemStack itemstack1 = itemstack.copy();
                int i = slot.getStack() == null ? 0 : slot.getStack().getSize();
                Container.computeStackSize(this.dragSplittingSlots, this.dragSplittingLimit, itemstack1, i);

                if (itemstack1.getSize() > itemstack1.getMaxStackSize())
                {
                    itemstack1.setSize(itemstack1.getMaxStackSize());
                }

                if (itemstack1.getSize() > slot.getItemStackLimit(itemstack1))
                {
                    itemstack1.setSize(slot.getItemStackLimit(itemstack1));
                }

                this.dragSplittingRemnant -= itemstack1.getSize() - i;
            }
        }
    }

    private Slot getSlotAtPosition(int x, int y)
    {
        for (int i = 0; i < this.inventorySlots.inventorySlots.size(); ++i)
        {
            Slot slot = (Slot)this.inventorySlots.inventorySlots.get(i);

            if (this.isMouseOverSlot(slot, x, y))
            {
                return slot;
            }
        }

        return null;
    }

    public void mouseClicked(int mouseX, int mouseY, int mouseButton)
    {
    	if(this.gm == null)
    		return;
    	if(this.cheatStack != null) {
    		Slot slot = this.getSlotAtPosition(mouseX, mouseY);
    		if((mouseButton == 0 || mouseButton == 1) && slot != null && this.gm.player != null && slot.canCheatItem())
    			this.gm.player.client.addToSendQueue(new CPacketCheat(this.cheatStack.getItem(), slot.slotNumber, mouseButton == 0 && this.cheatStack.isStacked()));
    		if(mouseButton != 1 && !this.gm.ctrl())
    			this.cheatStack = null;
    		return;
    	}
    	if((mouseButton == 0 || mouseButton == 1 || mouseButton == 2) && this.clickSide(this.gm.mouseX, this.gm.mouseY, -1, this.gm.shift() || mouseButton == 1, this.gm.ctrl() || mouseButton == 2))
    		return;
        if(mouseButton == 0) {
        	if(this.gm.itemCheat && this.gm.player != null && this.gm.player.getMouseItem() == null) {
	            for (CheatTab tab : CheatTab.values())
	            {
	                if (this.isInsideTab(tab, this.gm.mouseX, this.gm.mouseY))
	                {
	                    this.setCurrentTab(tab);
	                    return;
	                }
	            }
        	}
        }
        Slot slot = this.getSlotAtPosition(mouseX, mouseY);
        if(slot != null && Bind.CRAFT.isDown() && slot.canCheatItem() && slot.getHasStack() && this.gm.player.inventoryContainer.getSingleRecipe(slot.getStack()) != null) {
        	this.gm.player.client.addToSendQueue(new CPacketAction(CPacketAction.Action.CRAFT_ITEM, slot.slotNumber));
            this.ignoreMouseUp = true;
        	return;
    	}
        long i = System.currentTimeMillis();
        this.doubleClick = this.lastClickSlot == slot && i - this.lastClickTime < 250L && this.lastClickButton == mouseButton;
        this.ignoreMouseUp = false;

        if (mouseButton == 0 || mouseButton == 1)
        {
            boolean flag1 = mouseX < 0 || mouseY < 0 || mouseX >= this.xSize || mouseY >= this.ySize;
            int l = -1;

            if (slot != null)
            {
                l = slot.slotNumber;
            }

            if (flag1)
            {
                l = -999;
            }

            if (l != -1)
            {
                if (!this.dragSplitting)
                {
                    if (this.gm.player.getMouseItem() == null)
                    {
                        boolean flag2 = l != -999 && this.gm.shift();
                        int i1 = 0;

                        if (flag2)
                        {
                            this.shiftClickedSlot = slot != null && slot.getHasStack() ? slot.getStack() : null;
                            i1 = 1;
                        }
                        else if (l == -999)
                        {
                            i1 = 4;
                        }

                        this.handleMouseClick(slot, l, mouseButton, i1);

                        this.ignoreMouseUp = true;
                    }
                    else
                    {
                        this.dragSplitting = true;
                        this.dragSplittingButton = mouseButton;
                        this.dragSplittingSlots.clear();

                        if (mouseButton == 0)
                        {
                            this.dragSplittingLimit = 0;
                        }
                        else if (mouseButton == 1)
                        {
                            this.dragSplittingLimit = 1;
                        }
                    }
                }
            }
        }

        this.lastClickSlot = slot;
        this.lastClickTime = i;
        this.lastClickButton = mouseButton;
    }

    public void mouseDragged(int mouseX, int mouseY)
    {
    	if(this.gm == null || this.cheatStack != null)
    		return;
        Slot slot = this.getSlotAtPosition(mouseX, mouseY);
        ItemStack itemstack = this.gm.player.getMouseItem();

        if (this.dragSplitting && slot != null && itemstack != null && itemstack.getSize() > this.dragSplittingSlots.size() && Container.canAddItemToSlot(slot, itemstack, true) && slot.isItemValid(itemstack) && this.inventorySlots.canDragIntoSlot(slot))
        {
            this.dragSplittingSlots.add(slot);
            this.updateDragSplitting();
        }
    }

    public void mouseReleased(int mouseX, int mouseY, int state)
    {
    	if(this.gm == null || this.cheatStack != null)
    		return;
        Slot slot = this.getSlotAtPosition(mouseX, mouseY);
        boolean flag = mouseX < 0 || mouseY < 0 || mouseX >= this.xSize || mouseY >= this.ySize;
        int k = -1;

        if (slot != null)
        {
            k = slot.slotNumber;
        }

        if (flag)
        {
            k = -999;
        }

        if (this.doubleClick && slot != null && state == 0 && this.inventorySlots.canMergeSlot((ItemStack)null, slot))
        {
            if (this.gm.shift())
            {
                if (slot != null && slot.inventory != null && this.shiftClickedSlot != null)
                {
                    for (Slot slot2 : this.inventorySlots.inventorySlots)
                    {
                        if (slot2 != null && slot2.canTakeStack(this.gm.player) && slot2.getHasStack() && slot2.inventory == slot.inventory && Container.canAddItemToSlot(slot2, this.shiftClickedSlot, true))
                        {
                            this.handleMouseClick(slot2, slot2.slotNumber, state, 1);
                        }
                    }
                }
            }
            else
            {
                this.handleMouseClick(slot, k, state, 6);
            }

            this.doubleClick = false;
            this.lastClickTime = 0L;
        }
        else
        {
            if (this.dragSplitting && this.dragSplittingButton != state)
            {
                this.dragSplitting = false;
                this.dragSplittingSlots.clear();
                this.ignoreMouseUp = true;
                return;
            }

            if (this.ignoreMouseUp)
            {
                this.ignoreMouseUp = false;
                return;
            }

            if (this.dragSplitting && !this.dragSplittingSlots.isEmpty())
            {
                this.handleMouseClick((Slot)null, -999, Container.getDragCode(0, this.dragSplittingLimit), 5);

                for (Slot slot1 : this.dragSplittingSlots)
                {
                    this.handleMouseClick(slot1, slot1.slotNumber, Container.getDragCode(1, this.dragSplittingLimit), 5);
                }

                this.handleMouseClick((Slot)null, -999, Container.getDragCode(2, this.dragSplittingLimit), 5);
            }
            else if (this.gm.player.getMouseItem() != null)
            {
                boolean flag1 = k != -999 && this.gm.shift();

                if (flag1)
                {
                    this.shiftClickedSlot = slot != null && slot.getHasStack() ? slot.getStack() : null;
                }

                this.handleMouseClick(slot, k, state, flag1 ? 1 : 0);
            }
        }

        if (this.gm.player.getMouseItem() == null)
        {
            this.lastClickTime = 0L;
        }

        this.dragSplitting = false;
    }

    private boolean isMouseOverSlot(Slot slotIn, int mouseX, int mouseY)
    {
        return this.isPointInRegion(slotIn.xDisplayPosition, slotIn.yDisplayPosition, 16, 16, mouseX, mouseY);
    }

    protected boolean isPointInRegion(int left, int top, int right, int bottom, int pointX, int pointY)
    {
        return pointX >= left - 1 && pointX < left + right + 1 && pointY >= top - 1 && pointY < top + bottom + 1;
    }

    protected void handleMouseClick(Slot slotIn, int slotId, int clickedButton, int clickType)
    {
        if (slotIn != null)
        {
            slotId = slotIn.slotNumber;
        }

        this.gm.controller.windowClick(this.inventorySlots.windowId, slotId, clickedButton, clickType, this.gm.player);
    }
    
    public void dropItem() {
    	if (!this.clickSide(this.gm.mouseX, this.gm.mouseY, -100, this.gm.shift(), this.gm.ctrl()) && this.gm != null && this.gm.player != null && this.theSlot != null && this.theSlot.getHasStack())
        {
            this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, this.gm.ctrl() ? 1 : 0, 4);
        }
    }
    
    public void renameItem() {
    	if (this.gm != null && this.gm.player != null && this.theSlot != null && this.theSlot.canEditItem() && this.theSlot.getHasStack())
        {
    		this.gm.show(new GuiRename(this.theSlot.slotNumber, this.theSlot.getStack(), this));
        }
    }

    public void useHotbar(int slot)
    {
        if (this.gm != null && this.gm.player != null && slot < this.gm.player.getHotbarSize() && !this.clickSide(this.gm.mouseX, this.gm.mouseY, slot, this.gm.shift(), this.gm.ctrl()) && this.gm.player.getMouseItem() == null && this.cheatStack == null && this.theSlot != null)
        {
            this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, slot, 2);
        }
    }

    public void onGuiClosed()
    {
        if (this.gm != null && this.gm.player != null)
        {
            this.inventorySlots.onContainerClosed(this.gm.player);
        }
    }

    public void updateScreen()
    {
        if (this.gm != null && this.gm.player != null && (!this.gm.player.isEntityAlive() || this.gm.player.dead))
        {
        	this.gm.show(null);
        }
        else if(this.gm.itemCheat && !this.cheatSearch.getText().equals(this.cheatLast)) {
        	this.cheatLast = this.cheatSearch.getText();
        	this.setCurrentTab(selectedTab);
        }
    }
    
    public void renderItemOverlayIntoGUI(ItemStack stack, int xPosition, int yPosition, String text)
    {
        renderItemOverlay(stack, this.container_x + xPosition * this.container_scale, this.container_y + yPosition * this.container_scale, text, 0, 0, this.container_scale);
    }
    
    public static void renderItemOverlay(ItemStack stack, int xPosition, int yPosition, String text, int bar2, int bar2max, int scale)
    {
        if (stack != null)
        {
            if (stack.getSize() != 1 || text != null)
            {
                String s = text == null ? formatAmount(stack.getSize()) : text;

                if (text == null && stack.isEmpty())
                {
                    s = Color.RED + formatAmount(stack.getSize());
                }
                Font.set(scale > 1 ? Font.SMALL : Font.TINY);
        		Drawing.drawTextRight(s, xPosition + scale * 16, yPosition + scale * 16 + 1 - Font.HEIGHT, 0xffffffff);
        		Font.unset();
            }

            if (stack.isItemDamaged())
            {
                int j = (int)Math.round(14.0D * (double)scale - (double)stack.getItemDamage() * (14.0D * (double)scale) / (double)stack.getMaxDamage());
                int i = (int)Math.round(255.0D - (double)stack.getItemDamage() * 255.0D / (double)stack.getMaxDamage());
                draw(xPosition + scale, yPosition + 13 * scale, 14 * scale, scale * 2, 0, 0, 0);
                draw(xPosition + scale, yPosition + 13 * scale, 13 * scale, scale, (255 - i) / 4, 64, 0);
                draw(xPosition + scale, yPosition + 13 * scale, j, scale, 255 - i, i, 0);
            }

            if (bar2 > 0)
            {
                int j = (int)Math.round(14.0D * (double)scale - (double)bar2 * (14.0D * (double)scale) / (double)bar2max);
                int i = (int)Math.round(255.0D - (double)bar2 * 255.0D / (double)bar2max);
                draw(xPosition + scale, yPosition + scale * 2, 14 * scale, scale * 2, 0, 0, 0);
                draw(xPosition + scale, yPosition + scale * 2, 13 * scale, scale, (255 - i) / 8, (255 - i) / 16, i / 4);
                draw(xPosition + scale + 14 * scale - j, yPosition + scale * 2, j, scale, (255 - i) / 2, (255 - i) / 4, i);
            }
        }
    }
    
    private static void draw(int x, int y, int width, int height, int red, int green, int blue)
    {
    	Drawing.drawRect(x, y, width, height, 0xff000000 | (red << 16) | (green << 8) | blue);
    }
    
    public void drawBackground() {
		Drawing.drawRectBorder(this.container_x, this.container_y, this.container_w, this.container_h, Util.mulColor(Util.mixColor(this.gm.style.fill_top, this.gm.style.fill_btm), 0.8f), 0xff000000, Util.mulColor(this.gm.style.brdr_top, 0.8f), Util.mulColor(this.gm.style.brdr_btm, 0.8f));
    }
    
    
    protected boolean isInsideTab(CheatTab tab, int x, int y)
    {
        return x >= this.cheatX + 18 * (tab.getIndex() % this.cheatWidth) && x < this.cheatX + 18 * (tab.getIndex() % this.cheatWidth) + 18 && 
        	y >= this.cheatY + this.cheatHeight * 18 + 4 + 20 * (tab.getIndex() / this.cheatWidth) && y < this.cheatY + this.cheatHeight * 18 + 4 + 20 * (tab.getIndex() / this.cheatWidth) + 18;
    }
    
    private boolean renderInventoryHoveringText(CheatTab tab, int x, int z)
    {
        if (this.isPointInRegion(this.cheatX + 18 * (tab.getIndex() % this.cheatWidth), this.cheatY + this.cheatHeight * 18 + 4 + 20 * (tab.getIndex() / this.cheatWidth), 18, 18, this.gm.mouseX, this.gm.mouseY))
        {
        	this.hover(tab.getName(), x, z);
            return true;
        }
        else
        {
            return false;
        }
    }

    protected void drawTab(CheatTab tab)
    {
        this.itemRender.zLevel = 100.0F;
        ItemStack itemstack = tab.getIcon();
        GlState.enableDepth();
        this.itemRender.renderItemAndEffectIntoGUI(itemstack, this.cheatX + 18 * (tab.getIndex() % this.cheatWidth) + 1, this.cheatY + this.cheatHeight * 18 + 4 + 20 * (tab.getIndex() / this.cheatWidth) + 1);
        this.itemRender.zLevel = 0.0F;
    }

	public void scroll(int scr_x, int scr_y, int x, int y, boolean ctrl, boolean shift) {
		super.scroll(scr_x, scr_y, x, y, ctrl, shift);
		if(this.gm.itemCheat && scr_y != 0 && this.needsScrollBars()) {
            int len = ITEM_LIST.size() / this.cheatWidth - 5;
            this.currentScroll = (float)((double)this.currentScroll - (double)scr_y / (double)len);
            this.currentScroll = ExtMath.clampf(this.currentScroll, 0.0F, 1.0F);
        }
	}
    
    private boolean needsScrollBars()
    {
        return ITEM_LIST.size() > this.cheatWidth * this.cheatHeight;
    }

    private void setCurrentTab(CheatTab tab)
    {
        selectedTab = tab;
        this.dragSplittingSlots.clear();
        ITEM_LIST.clear();
        tab.filter(ITEM_LIST);
        if(!this.cheatLast.isBlank() && this.gm.player != null) {
        	for(Iterator<ItemStack> iter = ITEM_LIST.iterator(); iter.hasNext();) {
        		ItemStack stack = iter.next();
        		for(String line : this.getTooltip(stack)) {
        			if(line.toLowerCase().contains(this.cheatLast.toLowerCase())) {
        				stack = null;
        				break;
        			}
        		}
        		if(stack != null)
        			iter.remove();
        	}
        }
        this.currentScroll = 0.0F;
        this.cheatLabel.setText(tab.getName());
    }
    
    private boolean clickSide(int mouseX, int mouseY, int slot, boolean instant, boolean full) {
        if(this.gm.itemCheat && this.isPointInRegion(this.cheatX, this.cheatY, 18 * this.cheatWidth, 18 * this.cheatHeight, mouseX, mouseY) && this.gm.player != null && this.gm.player.getMouseItem() == null && this.cheatStack == null) {
            int size = (ITEM_LIST.size() + this.cheatWidth - 1) / this.cheatWidth - this.cheatHeight;
            int off = (int)((double)(this.currentScroll * (float)size) + 0.5D);

            if (off < 0)
            {
                off = 0;
            }
            
        	int sx = (mouseX - this.cheatX) / 18;
        	int sy = (mouseY - this.cheatY) / 18;
            int idx = sx + (sy + off) * this.cheatWidth;

            if(idx >= 0 && idx < ITEM_LIST.size()) {
            	if(slot != -1 || instant) {
            		this.gm.player.client.addToSendQueue(new CPacketCheat(ITEM_LIST.get(idx).getItem(), slot < 0 ? slot : -2 - slot, full));
            	}
            	else {
            		this.cheatStack = ITEM_LIST.get(idx).copy(full ? ITEM_LIST.get(idx).getMaxStackSize() : 1);
            	}
            	return true;
            }
        }
        return false;
    }
}
