package client.gui.element;

import org.lwjgl.opengl.GL11;

import client.gui.Font;
import client.renderer.Drawing;
import client.window.Button;
import client.window.Keysym;
import client.window.Window;
import common.util.CharValidator;
import common.util.Util;

abstract class Textbox extends Element {
	protected final TextCallback func;
	protected final CharValidator validator;
	protected final int capacity;
	protected final boolean editable;
	
	protected long tmr_scroll;
	protected long tmr_leftmb;
	protected int sel_start = -1;
	protected int sel_end = -1;
	protected int sel_drag = -1;

	protected Textbox(int x, int y, int w, int h, int cap, boolean editable, TextCallback callback, CharValidator validator) {
		super(x, y, w, h, null);
		this.func = callback;
		this.validator = validator;
		this.capacity = cap;
		this.editable = editable;
	}
	
	public abstract void updateText();
	protected abstract void onReturn(boolean shift);
	protected abstract void onSelection(boolean up);
	public abstract void scroll(int scr_x, int scr_y, int x, int y, boolean ctrl, boolean shift);
	public abstract void update();
	protected abstract void gui_text_clamp_scroll();
	protected abstract void updateCursor(int offset, boolean shift, int x1, int y1, int x2, int y2);
	protected abstract char getNewline();
	protected abstract int getCursorX(int x1, int x2);
	protected abstract int getCursorY(int y1, int y2);
	protected abstract int onCursorOffset(int x, int y, int x1, int y1, int x2, int y2);
	protected abstract void drawForeground(int x1, int y1, int x2, int y2);
	
	public boolean canHover() {
		return false;
	}
	
	protected int getMarginX() {
		return 4;
	}
	
	protected boolean canCopy() {
		return true;
	}
	
	public void setText(String str) {
		if(this.validator != null)
			str = this.validator.filter(str);
		this.text = str.length() > this.capacity ? str.substring(0, this.capacity) : str;
		this.updateText();
		this.sel_start = this.sel_end = this.sel_drag = this.text.length();
		gui_text_update_cur(this.sel_start, true);
	}
	
	public void mouse(Button btn, int x, int y, boolean ctrl, boolean shift) {
		if(btn == Button.MOUSE_LEFT) {
			if(!shift && ((System.currentTimeMillis() - this.tmr_leftmb) <= (long)this.gm.dclickDelay)) {
				this.onDoubleClick();
			}
			else {
				this.gui_text_select(x, y, shift);
			}
			this.tmr_leftmb = System.currentTimeMillis();
		}
		else if((btn == Button.MOUSE_MIDDLE) && this.func != null) {
			this.func.use(this, FieldAction.FUNCTION);
		}
	}
	
	public void drag(int x, int y) {
		this.gui_text_select(x, y, true);
	}
	
	public void character(char code) {
		if(this.editable)
			insertText(Character.toString(code));
	}
	
	public void key(Keysym key, boolean ctrl, boolean shift) {
		if(ctrl && key == Keysym.A) {
			this.sel_start = this.sel_drag = 0;
			this.sel_end = this.text.length();
		}
		else if(ctrl && (key == Keysym.C) || (this.editable && (key == Keysym.X))) {
			if(this.canCopy() && this.sel_start >= 0 && this.sel_start != this.sel_end) { // fix empty
				String str = Util.strip(this.text, this.sel_start, this.sel_end - this.sel_start, '\n', (char)0, '?');
				Window.setClipboard(str);
				if(key == Keysym.X)
					insertText("");
			}
		}
		else if(this.editable && ctrl && key == Keysym.V) {
			insertText(Window.getClipboard());
		}
		else if(this.editable && !ctrl && key == Keysym.RETURN) {
			this.onReturn(shift);
		}
		else if(this.editable && (!ctrl) && (key == Keysym.BACKSPACE || key == Keysym.DELETE)) {
			if(this.sel_start != this.sel_end) {
				insertText("");
			}
			else if(key == Keysym.DELETE && this.sel_start >= 0) {
				if(this.sel_end < this.text.length()) {
					this.sel_end += 1;
					insertText("");
				}
				else {
					this.sel_end = this.sel_start;
				}
			}
			else if(key == Keysym.BACKSPACE && this.sel_start > 0) {
				this.sel_start -= 1;
				insertText("");
			}
		}
		else if(!ctrl && (key == Keysym.RIGHT || key == Keysym.LEFT)) {
			if(key == Keysym.RIGHT && this.sel_start != this.sel_end) {
				this.sel_start = this.sel_drag = this.sel_end;
			}
			else if(key == Keysym.LEFT && this.sel_start != this.sel_end) {
				this.sel_end = this.sel_drag = this.sel_start;
			}
			if(key == Keysym.RIGHT && this.sel_start >= 0) {
				if(this.sel_end < this.text.length()) {
					this.sel_start = this.sel_drag = this.sel_end += 1;
				}
				else {
					this.sel_end = this.sel_drag = this.sel_start;
				}
				gui_text_update_cur(this.sel_end, true);
			}
			else if(key == Keysym.LEFT && this.sel_start >= 0) {
				if(this.sel_start > 0)
					this.sel_start -= 1;
				this.sel_end = this.sel_drag = this.sel_start;
				gui_text_update_cur(this.sel_end, true);
			}
		}
		else if(!ctrl && (key == Keysym.DOWN || key == Keysym.UP)) {
			this.onSelection(key == Keysym.UP);
		}
		else if((!ctrl) && key == Keysym.TAB) {
			if(this.func != null) {
				this.func.use(this, shift ? FieldAction.BACKWARD : FieldAction.FORWARD);
			}
		}
	}
	
	public void select() {
		if(this.func != null) {
			this.func.use(this, FieldAction.FOCUS);
		}
	}
	
	public void deselect() {
		this.sel_start = this.sel_end = this.sel_drag = -1;
		this.tmr_leftmb = 0L;
		if(this.func != null) {
			this.func.use(this, FieldAction.UNFOCUS);
		}
	}
	
	protected int updateScroll(int value) {
		if(value != 0) {
			int n = (int)((float)((float)this.tmr_scroll) / 1000000.0f * 4.0f * ((float)value));
			if((((long)n) * 1000000L) <= this.tmr_scroll)
				this.tmr_scroll -= ((long)n) * 1000000L;
			else
				this.tmr_scroll = 0L;
			this.tmr_scroll += this.gm.getPassedTime();
			return n;
		}
		return 0;
	}
	
	protected void gui_text_update_cur(int offset, boolean shift) {
		int x1 = this.pos_x + this.margin_x1;
		int y1 = this.pos_y + this.margin_y1;
		int x2 = this.size_x - (this.margin_x1 + this.margin_x2);
		int y2 = this.size_y - (this.margin_y1 + this.margin_y2);
		gui_text_clamp_scroll();
		this.updateCursor(offset, shift, x1, y1, x2, y2);
	}
	
	public void insertText(String str) {
		if(str == null || (this.sel_start == -1))
			return;
		str = Util.strip(str, 0, str.length(), this.getNewline(), ' ', (char)0);
		if(this.validator != null)
			str = this.validator.filter(str);
		if((str.length() + this.text.length() - (this.sel_end - this.sel_start)) > this.capacity)
			return;
		this.text = this.text.substring(0, this.sel_start) + str + this.text.substring(this.sel_end);
		this.sel_start += str.length();
		this.sel_end = this.sel_drag = this.sel_start;
		this.updateText();
		gui_text_update_cur(this.sel_end, true);
	}
	
	private void gui_text_select(int x, int y, boolean drag) {
		int x1 = this.pos_x + this.margin_x1;
		int y1 = this.pos_y + this.margin_y1;
		int x2 = this.size_x - (this.margin_x1 + this.margin_x2);
		int y2 = this.size_y - (this.margin_y1 + this.margin_y2);
		int offset = this.onCursorOffset(x, y, x1, y1, x2, y2);
		if(!drag) {
			this.sel_drag = this.sel_start = this.sel_end = offset;
		}
		else if(drag && this.sel_drag >= 0 && offset >= this.sel_drag) {
			this.sel_start = this.sel_drag;
			this.sel_end = offset;
		}
		else if(drag && this.sel_drag >= 0 && offset < this.sel_drag) {
			this.sel_start = offset;
			this.sel_end = this.sel_drag;
		}
	}
	
	protected void drawBackground() {
		if(this.enabled)
			Drawing.drawGradientBorder(this.pos_x, this.pos_y, this.size_x, this.size_y, this.gm.style.field_top, this.gm.style.field_btm, 0xff000000, this.gm.style.brdr_top, this.gm.style.brdr_btm);
		else
			Drawing.drawGradientBorder(this.pos_x, this.pos_y, this.size_x, this.size_y, Util.mulColor(this.gm.style.field_top, 0.5f), Util.mulColor(this.gm.style.field_btm, 0.5f), 0xff000000, Util.mulColor(this.gm.style.brdr_top, 0.5f), Util.mulColor(this.gm.style.brdr_btm, 0.5f));
	}
	
	public void drawOverlay() {
		if(this.editable && this.sel_start >= 0 && this.sel_end == this.sel_start && Util.ftime() % 1.0f < 0.5f) {
			int x1 = this.pos_x + this.margin_x1;
			int y1 = this.pos_y + this.margin_y1;
			int x2 = this.size_x - (this.margin_x1 + this.margin_x2);
			int y2 = this.size_y - (this.margin_y1 + this.margin_y2);
			this.gm.scissor(x1 < 0 ? 0 : x1, (this.gm.fbY - (y1 + y2)) < 0 ? 0 : (this.gm.fbY - (y1 + y2)), x2 < 0 ? 0 : x2, y2 < 0 ? 0 : y2);
			GL11.glEnable(GL11.GL_SCISSOR_TEST);
			Drawing.drawRect(this.getCursorX(x1, x2), this.getCursorY(y1, y2), 1, Font.HEIGHT, 0xff000000 | (~Util.mixColor(this.gm.style.field_top, this.gm.style.field_btm)));
			GL11.glDisable(GL11.GL_SCISSOR_TEST);
		}
	}
	
	protected void onDoubleClick() {
		this.sel_start = this.sel_drag = 0;
		this.sel_end = this.text.length();
	}
}
