package client.gui.element;

import java.util.List;

import client.gui.Gui;
import client.renderer.Drawing;
import client.window.Button;
import common.collect.Lists;
import common.util.ExtMath;

public abstract class GuiList<T extends ListEntry> extends Gui
{
	public static final int SCROLLBAR_WIDTH = 6;
	
    protected final List<T> elements = Lists.newArrayList();
    
    protected int width;
    protected int height;
    
    protected int top;
    protected int bottom;
    protected int right;
    protected int left;
    protected int mouseX;
    protected int mouseY;
    protected int initialClickY = -2;
    protected float scrollMultiplier;
    protected float amountScrolled;
    protected int selectedElement = -1;
    protected long lastClicked;

    public abstract int getSlotHeight();

    protected boolean isScrollbarRight()
    {
        return false;
    }

    public void setDimensions(int widthIn, int heightIn, int topIn, int bottomIn)
    {
        this.width = widthIn;
        this.height = heightIn;
        this.top = topIn;
        this.bottom = bottomIn;
        this.left = 0;
        this.right = widthIn;
    }

    public void init(int width, int height) {

		this.selectedElement = -1;
    }

    public void setSlotXBoundsFromLeft(int leftIn)
    {
        this.left = leftIn;
        this.right = leftIn + this.width;
    }

    public final T getListEntry(int index) {
    	return this.elements.get(index);
    }

    public final T getSelected() {
    	return this.selectedElement < 0 ? null : this.elements.get(this.selectedElement);
    }
    
    public final int getSize() {
    	return this.elements.size();
    }
    
    public final void setSelected(int index) {
        this.selectedElement = index;
    }

    protected int getContentHeight()
    {
        return this.getSize() * this.getSlotHeight();
    }
    
    public int getSlotIndexFromScreenCoords(int x, int y)
    {
        int x1 = this.left + (this.isScrollbarRight() ? 0 : SCROLLBAR_WIDTH);
        int x2 = this.left + (this.isScrollbarRight() ? this.width - SCROLLBAR_WIDTH : this.width);
        int y1 = y - this.top + (int)this.amountScrolled - 4;
        int idx = y1 / this.getSlotHeight();
        return this.isInList(x, y) && x >= x1 && x <= x2 && idx >= 0 && y1 >= 0 && idx < this.getSize() ? idx : -1;
    }

    protected boolean isInList(int x, int y)
    {
    	return this.isScrollbarRight() ? x < this.width - SCROLLBAR_WIDTH : x >= SCROLLBAR_WIDTH;
    }

    protected final boolean isSelected(int slotIndex)
    {
        return slotIndex == this.selectedElement;
    }

    protected void bindAmountScrolled()
    {
        this.amountScrolled = ExtMath.clampf(this.amountScrolled, 0.0F, (float)this.getMaxScroll());
    }

    public int getMaxScroll()
    {
        return Math.max(0, this.getContentHeight() - (this.bottom - this.top - 4));
    }

    public int getAmountScrolled()
    {
        return (int)this.amountScrolled;
    }

    public boolean isMouseYWithinSlotBounds(int y)
    {
        return y >= this.top && y <= this.bottom && this.mouseX >= this.left && this.mouseX <= this.right;
    }

    public void scrollBy(int amount)
    {
        this.amountScrolled += (float)amount;
        this.bindAmountScrolled();
        this.initialClickY = -2;
    }

	public void draw()
    {
    	int mouseXIn = this.gm.mouseX;
    	int mouseYIn = this.gm.mouseY;
        this.mouseX = mouseXIn;
        this.mouseY = mouseYIn;
        this.drawBackground();
        this.bindAmountScrolled();

        Drawing.drawScaled(this.gm, Gui.BACKGROUND, 0, this.top, this.gm.fbX, this.bottom - this.top, 0xff7f7f7f);
        
        int x = this.left + (this.isScrollbarRight() ? 0 : SCROLLBAR_WIDTH);
        int y = this.top + 4 - (int)this.amountScrolled;

        int size = this.getSize();
        boolean canHover = this.clicked(mouseXIn, mouseYIn) == null;

        for (int z = 0; z < size; z++)
        {
            int y1 = y + z * this.getSlotHeight();
            int h = this.getSlotHeight() - 4;

            Drawing.drawRectBorder(x, y1 - 2, this.width - SCROLLBAR_WIDTH, this.getSlotHeight(), this.isSelected(z) ? this.gm.style.fill_btm : this.gm.style.fill_top, this.isSelected(z) ? 0xffffffff : 0xff000000, this.gm.style.brdr_top, this.gm.style.brdr_btm);

            boolean hover = canHover && this.getSlotIndexFromScreenCoords(mouseXIn, mouseYIn) == z;
            this.getListEntry(z).draw(x + 2, y1, this.width - SCROLLBAR_WIDTH - 4, this.getSlotHeight() - 4, mouseXIn - x - 2, mouseYIn - y1, hover);
            if(hover)
				Drawing.drawRect(x, y1 - 2, this.width - SCROLLBAR_WIDTH, this.getSlotHeight(), Gui.HOVER_COLOR);
        }
        
        Drawing.drawScaled(this.gm, Gui.BACKGROUND, 0, 0, this.gm.fbX, this.top, 0xffffffff);
        Drawing.drawScaled(this.gm, Gui.BACKGROUND, 0, this.bottom, this.gm.fbX, this.height - this.bottom, 0xffffffff);
        Drawing.drawRect(-1, -1, this.gm.fbX + 2, this.top + 1, 0, this.gm.style.brdr_top, this.gm.style.brdr_btm);
        Drawing.drawRect(-1, this.bottom, this.gm.fbX + 2, this.height - this.bottom + 1, 0, this.gm.style.brdr_top, this.gm.style.brdr_btm);
        
        int edge = 2;
        Drawing.drawGradient(0, this.top, this.gm.fbX, edge, 0xff000000, 0x00000000);
        Drawing.drawGradient(0, this.bottom - edge, this.gm.fbX, edge, 0x00000000, 0xff000000);
        
        int max = this.getMaxScroll();

        if (max > 0)
        {
            int barHeight = (this.bottom - this.top) * (this.bottom - this.top) / this.getContentHeight();
            barHeight = ExtMath.clampi(barHeight, 32, this.bottom - this.top - 8);
            int scrollX = this.isScrollbarRight() ? this.width - SCROLLBAR_WIDTH : 0;
            int scrollY = (int)this.amountScrolled * (this.bottom - this.top - barHeight) / max + this.top;
            if(scrollY < this.top)
                scrollY = this.top;
            Drawing.drawRect(scrollX, this.top, SCROLLBAR_WIDTH, this.bottom - this.top, 0xff000000);
            Drawing.drawGradient(scrollX, scrollY, SCROLLBAR_WIDTH, barHeight, this.gm.style.fill_top, this.gm.style.fill_btm, this.gm.style.brdr_top, this.gm.style.brdr_btm);
        }
        
        super.draw();
    }

    public void handleMouseInput()
    {
        if (this.isMouseYWithinSlotBounds(this.mouseY))
        {
            if (this.initialClickY == -1)
            {
                boolean flag1 = true;

                if (this.mouseY >= this.top && this.mouseY <= this.bottom)
                {
                    int j2 = (this.isScrollbarRight() ? 0 : SCROLLBAR_WIDTH);
                    int k2 = (this.isScrollbarRight() ? this.width - SCROLLBAR_WIDTH : this.width);
                    int l2 = this.mouseY - this.top + (int)this.amountScrolled - 4;
                    int i1 = l2 / this.getSlotHeight();

                    if (i1 < this.getSize() && this.mouseX >= j2 && this.mouseX <= k2 && i1 >= 0 && l2 >= 0)
                    {
                    }
                    else if (this.mouseX >= j2 && this.mouseX <= k2 && l2 < 0)
                    {
                        flag1 = false;
                    }

                    int i3 = this.isScrollbarRight() ? this.width - SCROLLBAR_WIDTH : 0;
                    int j1 = i3 + SCROLLBAR_WIDTH;

                    if (this.mouseX >= i3 && this.mouseX <= j1)
                    {
                        this.scrollMultiplier = -1.0F;
                        int k1 = this.getMaxScroll();

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

                        int l1 = (int)((float)((this.bottom - this.top) * (this.bottom - this.top)) / (float)this.getContentHeight());
                        l1 = ExtMath.clampi(l1, 32, this.bottom - this.top - 8);
                        this.scrollMultiplier /= (float)(this.bottom - this.top - l1) / (float)k1;
                    }
                    else
                    {
                        this.scrollMultiplier = 1.0F;
                    }

                    if (flag1)
                    {
                        this.initialClickY = this.mouseY;
                    }
                    else
                    {
                        this.initialClickY = -2;
                    }
                }
                else
                {
                    this.initialClickY = -2;
                }
            }
            else if (this.initialClickY >= 0)
            {
                this.amountScrolled -= (float)(this.mouseY - this.initialClickY) * this.scrollMultiplier;
                this.initialClickY = this.mouseY;
            }
        }
    }
    
    public void mouse(Button btn, int x, int y, boolean ctrl, boolean shift)
    {
    	super.mouse(btn, x, y, ctrl, shift);
        if (this.isMouseYWithinSlotBounds(y) && this.clicked(x, y) == null)
        {
            int i = this.getSlotIndexFromScreenCoords(x, y);

            if (i >= 0)
            {
                int x1 = this.left + (this.isScrollbarRight() ? 0 : SCROLLBAR_WIDTH) + 2;
                int y1 = this.top + 4 - this.getAmountScrolled() + i * this.getSlotHeight();
                int mx = x - x1;
                int my = y - y1;

                boolean flag = i == this.selectedElement && System.currentTimeMillis() - this.lastClicked < (long)this.gm.dclickDelay;
                this.selectedElement = i;
                this.lastClicked = System.currentTimeMillis();

                this.getListEntry(i).select(flag, mx, my);
            }
        }
        if(btn == Button.MOUSE_LEFT && this.clicked(x, y) == null)
        	this.handleMouseInput();
    }

	public void mouserel(Button btn, int x, int y) {
		super.mouserel(btn, x, y);
		if(btn == Button.MOUSE_LEFT)
            this.initialClickY = -1;
	}
    
    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(scr_y != 0 && this.clicked(x, y) == null)
    		this.amountScrolled -= (float)(ExtMath.clampi(scr_y, -1, 1) * this.getSlotHeight() / 2);
    }

	public void drag(int x, int y) {
		super.drag(x, y);
		if(this.selected == null && Button.MOUSE_LEFT.isDown() && (this.initialClickY != -1 || this.clicked(x, y) == null))
			this.handleMouseInput();
	}
}
