package client.gui;

import java.util.List;

import org.lwjgl.opengl.GL13;

import client.Client;
import client.gui.element.Dropdown;
import client.gui.element.Dropdown.Handle;
import client.gui.element.Element;
import client.renderer.Drawing;
import client.renderer.GlState;
import client.vars.CVar;
import client.vars.ColorVar;
import client.window.Bind;
import client.window.Button;
import client.window.Keysym;
import common.collect.Lists;

public abstract class Gui {
    public static final String BACKGROUND = "textures/background.png";
    public static final int HOVER_COLOR = 0x288080ff;
    public static final int PRESS_COLOR = 0x30afafff;
    
	protected final Client gm = Client.CLIENT;
	
	public Element selected;
	private int min_x;
	private int min_y;
	private int max_x;
	private int max_y;
	public final List<Element> elems = Lists.newArrayList();
	
	public abstract void init(int width, int height);
	public abstract String getTitle();

	public void updateScreen() {
	}
	
    public void onGuiClosed() {
    }

	public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
	}

	public void mouseReleased(int mouseX, int mouseY, int state) {
	}
	
    public void mouseDragged(int mouseX, int mouseY) {
    }

	public void drawPost() {
	}
    
    public void drawOverlays() {
    }
    
    public void useHotbar(int slot) {
    }
    
    public void dropItem() {
    }
    
    public void renameItem() {
    }
    
    public void drawBackground() {
    	
    }
	
	public Element clicked(int x, int y) {
		if(this.selected != null && this.selected.visible && (this.selected instanceof Handle) && this.selected.inside(x, y))
			return this.selected; // fix (?)
		for(Element elem : this.elems) {
			if(elem.visible && elem.enabled && elem.inside(x, y))
				return elem;
		}
		return null;
	}
	
	public void reformat() {
		for(Element elem : this.elems) {
			elem.reformat();
		}
	}
	
	public void deselect() {
		if(this.selected != null && !(this.selected instanceof Handle)) {
			this.selected.deselect();
			this.selected = null;
		}
	}
	
	public void select(Element elem) {
		if(this.selected != elem && !(this.selected instanceof Handle) && !(elem instanceof Handle)) {
			if(this.selected != null)
				this.selected.deselect();
			if(elem != null)
				elem.select();
			this.selected = elem;
		}
	}
	
	public void mouse(Button btn, int x, int y, boolean ctrl, boolean shift) {
		int prev;
		Element elem = this.clicked(x, y);
		if(this.selected != null && this.selected != elem && this.selected instanceof Handle) {
			this.selected.deselect();
			return;
		}
		else if(this.selected != elem) {
			if(this.selected != null)
				this.selected.deselect();
			if(elem != null)
				elem.select();
			this.selected = elem;
		}
		if(elem != null)
			elem.mouse(btn, x, y, ctrl, shift);
	}
	
	public void mouserel(Button btn, int x, int y) {
		if(this.selected != null)
			this.selected.mouserel();
	}
	
	public void drag(int x, int y) {
		if(this.selected != null && (Button.MOUSE_LEFT.isDown() || Button.MOUSE_RIGHT.isDown()))
			this.selected.drag(x, y);
	}
	
	public void scroll(int scr_x, int scr_y, int x, int y, boolean ctrl, boolean shift) {
		Element elem = this.clicked(x, y);
		if(elem != null)
			elem.scroll(scr_x, scr_y, x, y, ctrl, shift);
	}
	
	public void key(Keysym key, boolean ctrl, boolean shift) {
		if(this.selected != null)
			this.selected.key(key, ctrl, shift);
	}
	
	public void character(char code) {
		if(this.selected != null)
			this.selected.character(code);
	}	
	
	protected void shift() {
		if(this.gm.fbX != 0 && this.gm.fbY != 0) {
			int shift_x = (this.gm.fbX - (this.max_x - this.min_x)) / 2 - this.min_x;
			int shift_y = (this.gm.fbY - (this.max_y - this.min_y)) / 2 - this.min_y;
			for(Element elem : this.elems) {
				elem.shift(shift_x, shift_y);
			}
		}
	}
	
	public void init() {
		this.selected = null;
		this.min_x = this.min_y = Integer.MAX_VALUE;
		this.max_x = this.max_y = Integer.MIN_VALUE;
		this.elems.clear();
		this.init(this.gm.fbX, this.gm.fbY);
	}
	
	public void update() {
		if(this.selected != null)
			this.selected.update();
	}
	
	protected <T extends Element> T add(T elem) {
		this.elems.add(elem);
		elem.setGui(this);
		this.min_x = Math.min(this.min_x, elem.getX());
		this.min_y = Math.min(this.min_y, elem.getY());
		this.max_x = Math.max(this.max_x, elem.getX() + elem.getWidth());
		this.max_y = Math.max(this.max_y, elem.getY() + elem.getHeight());
		if(elem instanceof Dropdown drop)
			this.add(drop.getHandle());
		return elem;
	}
	
	protected Element addSelector(String cvar, int x, int y, int w, int h) {
		CVar cv = this.gm.getVar(cvar);
		if(cv instanceof ColorVar color) {
			this.add(color.label(x, y, w));
			return this.add(color.editor(x, y, w, h));
		}
		return this.add(cv.selector(x, y, w, h));
	}
	
	public void draw() {
		if(this.selected != null && this.selected instanceof Handle && !this.selected.visible) {
			this.selected = null;
		}
		for(Element elem : this.elems) {
			if(!(elem instanceof Handle))
				elem.draw();
		}
		if(this.selected != null && this.selected instanceof Handle && this.selected.visible) {
			this.selected.draw();
		}
	}
	
	public void drawOverlay() {
		Element elem = this.selected;
		if(Button.isMouseDown() && elem != null && elem.enabled && elem.visible && elem.canClick()) {
			elem.drawPress();
			return;
		}
		if(elem != null && elem.enabled && elem.visible)
			elem.drawOverlay();
		elem = this.clicked(this.gm.mouseX, this.gm.mouseY);
		if(elem != null && elem.enabled && elem.visible && elem.canHover())
			elem.drawHover();
	}
	
	public void drawMainBackground() {
		if(this.gm.world != null && !this.gm.charEditor) {
			Drawing.drawGradient(0, 0, this.gm.fbX, this.gm.fbY, 0xc0101010, 0xd0101010);
		}
		else {
			Drawing.drawScaled(this.gm, BACKGROUND);
		}
	}
	
	public void render() {
		this.drawMainBackground();
		this.drawBackground();
		if(this.gm.fbX != 0 && this.gm.fbY != 0)
			this.draw();
		GlState.bindTexture(0);
    	GlState.setActiveTexture(GL13.GL_TEXTURE0);
    	GlState.enableTexture2D();
    	GlState.disableDepth();
        this.drawPost();
    	GlState.disableDepth();
        this.drawOverlays();
		if(Bind.isWindowActive())
			this.drawOverlay();
	}
}
