package client.gui.element;

import client.gui.Font;
import client.gui.Formatter;
import client.gui.Gui;
import client.renderer.Drawing;
import client.window.Button;
import common.util.Displayable;
import common.util.ExtMath;
import common.util.Util;

public class Dropdown<T> extends Element {
	public class Handle extends Element {
		private Handle(boolean up) {
			super(Dropdown.this.pos_x, Dropdown.this.pos_y + (up ? -(Font.HEIGHT * Dropdown.this.values.length + 2) : Dropdown.this.size_y), Dropdown.this.size_x, Font.HEIGHT * Dropdown.this.values.length + 2, null);
			StringBuilder sb = new StringBuilder();
			for(T value : Dropdown.this.values) {
				if(sb.length() > 0)
					sb.append('\n');
				sb.append(value instanceof Displayable disp ? disp.getDisplay() : value.toString());
			}
			this.setText(sb.toString());
			this.visible = /* this.r_dirty = */ false;
		}

		public void updateText() {
		}
		
//		protected boolean isTextCenteredX() {
//			return false;
//		}
//		
//		protected boolean isTextCenteredY() {
//			return false;
//		}
		
		public boolean canClick() {
			return false;
		}
		
		public void deselect() {
			this.visible = false;
//			this.r_dirty = true;
		}
		
		public void mouse(Button btn, int x, int y, boolean ctrl, boolean shift) {
			Dropdown drop = Dropdown.this;
			int prev = drop.value;
			drop.value = (y - (this.pos_y + this.margin_y1)) * drop.values.length / (this.size_y - (this.margin_y1 + this.margin_y2));
			drop.value = ExtMath.clampi(drop.value, 0, drop.values.length - 1);
			if(drop.value != prev) {
				drop.func.use(drop, drop.getValue());
				drop.formatText();
				this.playSound();
			}
			this.visible = false;
//			this.r_dirty = true;
		}
		
		public void drawHover() {
			int m = ((this.gm.mouseY - (this.pos_y + this.margin_y1)) * Dropdown.this.values.length / (this.size_y - (this.margin_y1 + this.margin_y2)));
			// if((sys.mouse_y - this.pos_y) < (this.size_y - this.margin_y2))
			Drawing.drawRect(this.pos_x + this.margin_x1, this.pos_y + this.margin_y1 + ExtMath.clampi(m, 0, Dropdown.this.values.length - 1) * ((this.size_y - (this.margin_y1 + this.margin_y2)) / Dropdown.this.values.length), this.size_x - (this.margin_x1 + this.margin_x2), (this.size_y - (this.margin_y1 + this.margin_y2)) / Dropdown.this.values.length, Gui.HOVER_COLOR);
		}
	}
	
	private final DropdownCallback<T> func;
	private final T[] values;
	private final Handle handle;
	private final int def;

	private int value;

	public Dropdown(int x, int y, int w, int h, boolean up, T[] values, T def, T init, DropdownCallback<T> callback, Formatter<Dropdown<T>> formatter) {
		super(x, y, w, h, formatter);
		this.func = callback;
		this.values = values;
		this.def = Util.indexOfChecked(this.values, def);
		this.value = Util.indexOfChecked(this.values, init);
		this.handle = new Handle(up);
		this.formatText();
	}

	public Dropdown(int x, int y, int w, int h, boolean up, T[] values, T def, T init, DropdownCallback<T> callback, final String text) {
		this(x, y, w, h, up, values, def, init, callback, text == null ? new Formatter<Dropdown<T>>() {
			public String use(Dropdown<T> elem) {
				T value = elem.getValue();
				return value instanceof Displayable disp ? disp.getDisplay() : value.toString();
			}
		} : new Formatter<Dropdown<T>>() {
			public String use(Dropdown<T> elem) {
				T value = elem.getValue();
				return String.format("%s: %s", text, value instanceof Displayable disp ? disp.getDisplay() : value.toString());
			}
		});
	}

	public T getValue() {
		return this.values[this.value];
	}

	public void setValue(T value) {
		this.value = Util.indexOfChecked(this.values, value);
	}
	
	public void mouse(Button btn, int x, int y, boolean ctrl, boolean shift) {
		int prev = this.value;
		if(ctrl || (btn == Button.MOUSE_MIDDLE)) {
			if((this.value = this.def) != prev) {
				this.func.use(this, this.getValue());
				this.formatText();
				this.playSound();
			}
		}
		else if(this.gui != null) {
			Handle drop = this.handle;
			drop.visible = true;
//			drop.r_dirty = true;
			this.gui.selected = drop;
			this.playSound();
		}
	}
	
	public Handle getHandle() {
		return this.handle;
	}
}
