package speiger.src.coreengine.rendering.guiOld.components;

import java.util.function.Predicate;

import org.lwjgl.glfw.GLFW;

import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.rendering.guiOld.GuiComponent;
import speiger.src.coreengine.rendering.guiOld.base.IButtonComponent;
import speiger.src.coreengine.rendering.guiOld.base.IKeyComponent;
import speiger.src.coreengine.rendering.guiOld.helper.Align;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;
import speiger.src.coreengine.rendering.guiOld.helper.box.ParentBox;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.Constrain.Target;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.Constrains;
import speiger.src.coreengine.rendering.guiOld.renderer.UIRenderer;
import speiger.src.coreengine.rendering.guiOld.renderer.lexer.TextMetadata;
import speiger.src.coreengine.rendering.guiOld.renderer.lexer.Word;
import speiger.src.coreengine.rendering.guiOld.renderer.lexer.WordType;
import speiger.src.coreengine.rendering.inputOld.Keyboard;
import speiger.src.coreengine.rendering.utils.Cursor;
import speiger.src.coreengine.utils.functions.Functions;

public class TextFieldComponent extends GuiComponent
		implements IButtonComponent, IKeyComponent
{
	public static final int FLAG_FOCUS = 1 << 20;
	public static final int FLAG_CAN_LOSE_FOCUS = 1 << 21;
	public static final int FLAG_DOUBLE_CLICK = 1 << 22;
	public static final int FLAG_INFINITE_WIDTH = 1 << 23;
	public static final int FLAG_AUTO_VALIDATE = 1 << 24;
	
	TextComponent text = new TextComponent().horizontal(Align.LEFT_TOP).singleLine(true).special(false).cast();
	int color;
	int curserPosition = 0;
	int selectionPosition = 0;
	long lastClickTime = 0;
	int maxTextLength = 32;
	Predicate<String> validator = Functions.getAlwaysTrue();
	int lineOffset = 0;
	boolean direction = false;
	int largestPos = 0;
	IGuiBox viewPort = new ParentBox(1F);
	
	public TextFieldComponent(int color)
	{
		this(color, 1F);
	}
	
	public TextFieldComponent(int color, float textScale)
	{
		super(0F, 0F, 0F, 0F);
		setFlag(FLAG_CAN_LOSE_FOCUS);
		this.color = color;
	}
	
	public TextFieldComponent(float x, float y, float width, float height, int color)
	{
		this(x, y, width, height, color, 1F);
	}
	
	public TextFieldComponent(float x, float y, float width, float height, int color, float textScale)
	{
		super(x, y, width, height);
		setFlag(FLAG_CAN_LOSE_FOCUS);
		this.color = color;
	}
	
	@Override
	public void init()
	{
		addBox(viewPort);
		addChild(text, createConstraints());
		if(text.getText().length() > 0 && isFlagNotSet(FLAG_INFINITE_WIDTH))
		{
			String s = text.getText();
			float scale = text.getTextScale();
			float width = text.getBox().getWidth();
			while(text.getFont().width(s) * scale > width && s.length() > 0)
			{
				s = s.substring(0, s.length() - 1);
			}
			text.setText(s);
		}
	}
	
	private Constrains createConstraints()
	{
		return isFlagNotSet(FLAG_INFINITE_WIDTH) ? Constrains.parent(1F) : Constrains.dynamic(this::getOffset, Target.X).parent(1F, Target.Y).parent(1F, Target.WIDTH).parent(1F, Target.HEIGHT).build();
	}
	
	public TextFieldComponent setValidator(Predicate<String> validator)
	{
		this.validator = validator;
		return this;
	}
	
	public TextFieldComponent setMaxTextLength(int charLimit)
	{
		maxTextLength = charLimit;
		return this;
	}
	
	public TextFieldComponent setCanLoseFocus(boolean value) 
	{
		setFlag(FLAG_CAN_LOSE_FOCUS, value);
		return this;
	}
	
	public TextFieldComponent setFocused(boolean value)
	{
		setFlag(FLAG_FOCUS, value);
		return this;
	}
	
	public TextFieldComponent setAutoUpdating(boolean value)
	{
		setFlag(FLAG_AUTO_VALIDATE, value);
		return this;
	}
	
	public TextFieldComponent setColor(int color)
	{
		this.color = color;
		return this;
	}
	
	public final TextFieldComponent setInfiniteText(boolean value)
	{
		if(setFlag(FLAG_INFINITE_WIDTH, value))
		{
			text.limit(!value);
			if(getGui() != null)
			{
				addConstrains(text, createConstraints());
			}
		}
		return this;
	}
	
	public TextComponent getRawText()
	{
		return text;
	}
	
	public String getText()
	{
		return text.getText();
	}
	
	public String getSelectedText()
	{
		return text.getText(Math.min(getCurserPosition(), getSelectionPosition()), Math.max(getCurserPosition(), getSelectionPosition()));
	}
	
	public TextFieldComponent setText(String s)
	{
		if(s == null)
		{
			return this;
		}
		if(validator.test(s))
		{
			if(s.length() > maxTextLength)
			{
				s = s.substring(0, maxTextLength);
			}
			if(isFlagNotSet(FLAG_INFINITE_WIDTH) && getGui() != null)
			{
				float scale = text.getTextScale();
				float width = text.getBox().getWidth();
				while(text.getFont().width(s) * scale > width)
				{
					s = s.substring(0, s.length() - 1);
				}
			}
			text.setText(s);
		}
		return this;
	}
	
	@Override
	protected boolean updateSelf(int mouseX, int mouseY, float particalTicks)
	{
		if(text.isTopHovered(mouseX, mouseY))
		{
			bindCursor(Cursor.CURSOR_IBEAM);
		}
		return true;
	}
	
	@Override
	protected boolean renderSelf(int mouseX, int mouseY, float particalTicks)
	{
		float brightness = getActiveBrightness();
		UIRenderer render = getRenderer();
		render.setBrightness(brightness * 0.7F).drawQuad(getBox(), color).setBrightness(brightness);
		IGuiBox box = text.getBox();
		render.drawQuad(viewPort, 0.001F, color);
		if(isFlagSet(FLAG_INFINITE_WIDTH))
		{
			enableScissorsBox(getBox().getMinX(1F), getBox().getMinY(1F), getBox().getMaxX(-1F), getBox().getMaxY(-1F));
		}
		if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0)
		{
			TextMetadata data = text.getMetadata();
			if(hasSelectedText())
			{
				float extra = text.getHorizontal().align(box.getWidth(), data.getMaxWidth());
				render.drawQuad(box.getMinX() + extra + data.getWidth(Math.min(getCurserPosition(), getSelectionPosition())), box.getMinY(), box.getMinX() + extra + data.getWidth(Math.max(getCurserPosition(), getSelectionPosition())), box.getMaxY(), 0.02F, text.getTextColor());
			}
			else
			{
				float width = data.getWidth(curserPosition) + text.getHorizontal().align(box.getWidth(), data.getMaxWidth());
				render.drawQuad(box.getMinX() + width, box.getMinY(), box.getMinX() + width + text.getTextScale(), box.getMaxY(), 0.02F, text.getTextColor());
			}
		}
		if(isFlagSet(FLAG_INFINITE_WIDTH))
		{
			renderChildren(mouseX, mouseY, particalTicks);
			disableScissors();
			return false;
		}
		return true;
	}
	
	@Override
	public boolean onClick(int button, int mouseX, int mouseY)
	{
		setFocused(true);
		if(hasSelectedText() && isFlagNotSet(FLAG_DOUBLE_CLICK))
		{
			setSelectionPosition(-1);
			setCurserPosition(getMousePosition(mouseX));
			return true;
		}
		int pos = getMousePosition(mouseX);
		if(pos == getCurserPosition() || isFlagSet(FLAG_DOUBLE_CLICK))
		{
			if(System.currentTimeMillis() - lastClickTime < 500L)
			{
				if(isFlagSet(FLAG_DOUBLE_CLICK))
				{
					clearFlag(FLAG_DOUBLE_CLICK);
					setSelectionPosition(0);
					setCurserPosition(text.length());
				}
				else
				{
					setFlag(FLAG_DOUBLE_CLICK);
					handleDoubleClick(pos);
				}
			}
			else
			{
				clearFlag(FLAG_DOUBLE_CLICK);
			}
			lastClickTime = System.currentTimeMillis();
			return true;
		}
		clearFlag(FLAG_DOUBLE_CLICK);
		setCurserPosition(pos);
		lastClickTime = System.currentTimeMillis();
		return true;
	}
	
	@Override
	public boolean onDrag(int mouseX, int mouseY)
	{
		if(!hasSelectedText())
		{
			setSelectionPosition(getCurserPosition());
		}
		setCurserPosition(getMousePosition(mouseX));
		return true;
	}
	
	@Override
	public boolean isAcceptingInput()
	{
		return isFlagSet(FLAG_FOCUS);
	}
	
	@Override
	public boolean isBlockingMovement()
	{
		return true;
	}
	
	@Override
	public boolean onKeyPressed(int key)
	{
		if(isFlagNotSet(FLAG_FOCUS))
		{
			return false;
		}
		if(key == GLFW.GLFW_KEY_ENTER)
		{
			notifyListeners(LISTENER_USER_ACTION);
			if(isFlagSet(FLAG_CAN_LOSE_FOCUS))
			{
				setFocused(false);
				return true;
			}
		}
		if(key == GLFW.GLFW_KEY_BACKSPACE)
		{
			if(Keyboard.isCtrlDown())
			{
				Word word = text.getMetadata().getWord(getCurserPosition());
				if(word == null)
				{
					return true;
				}
				if(getCurserPosition() == word.getStartIndex())
				{
					if(word.getPrev() != null)
					{
						deleteAtCurser(word.getPrev().getStartIndex() - word.getStartIndex());
					}
				}
				else
				{
					setCurserPosition(word.getStartIndex());
					deleteAtCurser(word.getEndIndex() - word.getStartIndex());
				}
				return true;
			}
			else if(deleteAtCurser(-1))
			{
				return true;
			}
		}
		else if(key == GLFW.GLFW_KEY_DELETE)
		{
			if(Keyboard.isCtrlDown())
			{
				Word word = text.getMetadata().getWord(getCurserPosition());
				if(word == null)
				{
					return true;
				}
				if(getCurserPosition() == word.getEndIndex())
				{
					if(word.getNext() != null)
					{
						deleteAtCurser(word.getNext().getEndIndex() - word.getEndIndex());
					}
				}
				else
				{
					setCurserPosition(word.getStartIndex());
					deleteAtCurser(word.getEndIndex() - word.getStartIndex());
				}
				return true;
			}
			else if(deleteAtCurser(1))
			{
				return true;
			}
		}
		else if(key == GLFW.GLFW_KEY_LEFT)
		{
			if(getCurserPosition() >= 0)
			{
				if(Keyboard.isShiftDown() && getSelectionPosition() == -1)
				{
					setSelectionPosition(getCurserPosition());
				}
				else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1)
				{
					setSelectionPosition(-1);
				}
				if(Keyboard.isCtrlDown())
				{
					Word word = text.getMetadata().getWord(getCurserPosition());
					if(word.getStartIndex() == getCurserPosition())
					{
						if(word.getPrev() != null)
						{
							setCurserPosition(word.getPrev().getStartIndex());
						}
					}
					else
					{
						setCurserPosition(word.getStartIndex());
					}
				}
				else
				{
					setCurserPosition(getCurserPosition() - 1);
				}
				return true;
			}
		}
		else if(key == GLFW.GLFW_KEY_RIGHT)
		{
			if(getCurserPosition() < text.length())
			{
				if(Keyboard.isShiftDown() && getSelectionPosition() == -1)
				{
					setSelectionPosition(getCurserPosition());
				}
				else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1)
				{
					setSelectionPosition(-1);
				}
				if(Keyboard.isCtrlDown())
				{
					Word word = text.getMetadata().getWord(getCurserPosition());
					if(word.getEndIndex() == getCurserPosition())
					{
						if(word.getNext() != null)
						{
							setCurserPosition(word.getNext().getEndIndex());
						}
					}
					else
					{
						setCurserPosition(word.getEndIndex());
					}
				}
				else
				{
					setCurserPosition(getCurserPosition() + 1);
				}
				return true;
			}
			else
			{
				if(!Keyboard.isShiftDown() && hasSelectedText())
				{
					setSelectionPosition(-1);
				}
				return true;
			}
		}
		else if(key == GLFW.GLFW_KEY_END)
		{
			if(Keyboard.isShiftDown())
			{
				if(!hasSelectedText())
				{
					setSelectionPosition(getCurserPosition());
				}
			}
			else
			{
				setSelectionPosition(-1);
			}
			setCurserToEnd();
			return true;
		}
		else if(key == GLFW.GLFW_KEY_HOME)
		{
			if(Keyboard.isShiftDown())
			{
				if(getSelectionPosition() == -1)
				{
					setSelectionPosition(getCurserPosition());
				}
			}
			else
			{
				setSelectionPosition(-1);
			}
			setCurserPosition(0);
			return true;
		}
		else if(key == GLFW.GLFW_KEY_TAB)
		{
			writeText(Character.toString('\t'));
			return true;
		}
		else if(isSelect(key))
		{
			setSelectionPosition(0);
			setCurserToEnd();
			return true;
		}
		else if(isCopy(key))
		{
			if(hasSelectedText())
			{
				getWindow().setClipboardString(getSelectedText());
			}
			return true;
		}
		else if(isPaste(key))
		{
			String text = getWindow().getClipboardString();
			if(text != null)
			{
				writeText(text);
			}
			return true;
		}
		else if(isCut(key))
		{
			if(hasSelectedText())
			{
				getWindow().setClipboardString(getSelectedText());
				writeText("");
			}
			return true;
		}
		else if(Keyboard.isPrintableKey(key))
		{
			return true;
		}
		return false;
	}
	
	@Override
	public boolean onKeyTyped(char letter, int codepoint)
	{
		return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(codepoint) && writeText(new String(Character.toChars(codepoint)));
	}
	
	public boolean deleteAtCurser(int amount)
	{
		if(text.length() > 0)
		{
			if(hasSelectedText())
			{
				int startPos = Math.min(getCurserPosition(), getSelectionPosition());
				writeText("");
				setCurserPosition(startPos);
			}
			else
			{
				int startPos = Math.min(getCurserPosition(), getCurserPosition() + amount);
				int endPos = Math.max(getCurserPosition(), getCurserPosition() + amount);
				StringBuilder builder = new StringBuilder();
				if(startPos >= 0)
				{
					builder.append(text.getText(0, startPos));
				}
				if(endPos < text.length())
				{
					builder.append(text.getText(endPos));
				}
				String s = builder.toString();
				if(validator.test(s))
				{
					setText(s);
					setCurserPosition(Math.min(startPos, text.length()));
					if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION);
				}
			}
			return true;
		}
		return false;
	}
	
	public boolean writeText(String toWrite)
	{
		toWrite = text.getFont().clearInvalidLetters(toWrite);
		StringBuilder builder = new StringBuilder();
		int startPos = hasSelectedText() ? Math.min(getSelectionPosition(), getCurserPosition()) : getCurserPosition();
		int endPos = hasSelectedText() ? Math.max(getSelectionPosition(), getCurserPosition()) : getCurserPosition();
		int room = maxTextLength - text.length() - (startPos - endPos);
		if(text.length() > 0) builder.append(text.getText(0, startPos));
		int moved = 0;
		if(room < toWrite.length())
		{
			builder.append(toWrite.substring(0, room));
			moved = room;
		}
		else
		{
			builder.append(toWrite);
			moved = toWrite.length();
		}
		if(text.length() > 0 && endPos < text.length()) builder.append(text.getText(endPos));
		String s = builder.toString();
		if(validator.test(s))
		{
			setText(s);
			setCurserPosition(Math.min(endPos + moved, text.length()));
			setSelectionPosition(-1);
			if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION);
			return true;
		}
		return false;
	}
	
	@Override
	public void onFocusLost()
	{
		if(isFlagSet(FLAG_CAN_LOSE_FOCUS))
		{
			if(isFlagSet(FLAG_FOCUS))
			{
				notifyListeners(LISTENER_USER_ACTION);
			}
			setFocused(false);
		}
	}
	
	protected void handleDoubleClick(int position)
	{
		Word word = text.getMetadata().getWord(position);
		if(word == null)
		{
			return;
		}
		if(!word.isSpecial())
		{
			setSelectionPosition(word.getStartIndex());
			setCurserPosition(word.getEndIndex());
			return;
		}
		WordType type = word.getType();
		if(type.isStartWord())
		{
			Word other = type.findEndWord(word);
			if(other != null)
			{
				setSelectionPosition(word.getStartIndex());
				setCurserPosition(other.getEndIndex());
			}
		}
		else if(type.isEndWord())
		{
			Word other = type.findStartWord(word);
			if(other != null)
			{
				setSelectionPosition(other.getStartIndex());
				setCurserPosition(word.getEndIndex());
			}
		}
		else if(type.isDualWord())
		{
			if(word.getStartIndex() == position && word.getPrev() != null)
			{
				Word other = type.findStartWord(word);
				if(other != null)
				{
					setSelectionPosition(other.getStartIndex());
					setCurserPosition(word.getEndIndex());
				}
			}
			else
			{
				Word other = type.findEndWord(word);
				if(other != null)
				{
					setSelectionPosition(word.getStartIndex());
					setCurserPosition(other.getEndIndex());
				}
			}
		}
		else
		{
			setSelectionPosition(word.getStartIndex());
			setCurserPosition(word.getEndIndex());
		}
	}
	
	protected int getMousePosition(int mouseX)
	{
		return text.getMetadata().getIndex(mouseX - (text.getBox().getMinX()+text.getHorizontal().align(text.getBox().getWidth(), text.getMetadata().getMaxWidth())));
	}
	
	public void setCurserToEnd()
	{
		setCurserPosition(text.length());
	}
	
	public void setCurserPosition(int curserPosition)
	{
		this.curserPosition = Math.max(0, curserPosition);
		if(isFlagSet(FLAG_INFINITE_WIDTH) && text.getFont() != null)
		{
			int lastLine = lineOffset;
			if(lineOffset > text.length())
			{
				lineOffset = text.length();
			}
			
			String s = text.getFont().trimToWidth(text.getText(lineOffset), text.getWidth());
			int k = s.length() + lineOffset;
			if(curserPosition > k)
			{
				lineOffset += curserPosition - k;
				direction = true;
				largestPos = curserPosition;
			}
			else if(curserPosition < largestPos)
			{
				int diff = largestPos - curserPosition;
				lineOffset -= diff;
				largestPos -= diff;
				if(lineOffset <= 0) direction = false;
			}
			lineOffset = MathUtils.clamp(0, text.length(), lineOffset);
			if(lastLine != lineOffset)
			{
				text.onChanged(false);
			}
		}
	}
	
	public int getCurserPosition()
	{
		return curserPosition;
	}
	
	public void setSelectionPosition(int selectionPosition)
	{
		this.selectionPosition = selectionPosition;
	}
	
	public int getSelectionPosition()
	{
		return selectionPosition;
	}
	
	public boolean hasSelectedText()
	{
		return selectionPosition != -1;
	}
	
	protected float getOffset()
	{
		if(direction && text.getFont() != null)
		{
			return -(text.getMetadata().getWidth(largestPos)) + text.getBox().getWidth() - 1F;
		}
		return 1F - (text.getMetadata().getWidth(lineOffset));
	}
}
