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

import java.util.function.Predicate;

import org.lwjgl.glfw.GLFW;

import speiger.src.coreengine.math.vector.ints.Vec2i;
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.constrains.Constrains;
import speiger.src.coreengine.rendering.guiOld.renderer.UIRenderer;
import speiger.src.coreengine.rendering.guiOld.renderer.lexer.Line;
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 TextPanelComponent extends GuiComponent implements IButtonComponent, IKeyComponent
{
	public static final int FLAG_FOCUS = 1 << 20;
	public static final int FLAG_CAN_LOSE_FOCUS = 1 << 21;
	
	TextComponent text = new TextComponent().align(Align.LEFT_TOP, Align.LEFT_TOP).singleLine(true).special(false).cast();
	int color;
	int curserPosition = 0;
	Vec2i curserPos = Vec2i.mutable();
	int selectionPosition = 0;
	Vec2i selectionPos = Vec2i.mutable();
	long lastClickTime = 0;
	int maxTextLength = 64;
	int clickCount = 0;
	Predicate<String> validator = Functions.getAlwaysTrue();
	
	public TextPanelComponent(int color)
	{
		this(color, 1F);
	}
	
	public TextPanelComponent(int color, float textScale)
	{
		super(0F, 0F, 0F, 0F);
		this.color = color;
		text.setTextScale(textScale);
	}
	
	public TextPanelComponent(float x, float y, float width, float height, int color)
	{
		this(x, y, width, height, color, 1F);
	}
	
	public TextPanelComponent(float x, float y, float width, float height, int color, float textScale)
	{
		super(x, y, width, height);
		this.color = color;
		text.setTextScale(textScale);
	}

	@Override
	public void init()
	{
		addChild(text, Constrains.parent(1F));
	}
	
	public TextPanelComponent setValidator(Predicate<String> validator)
	{
		this.validator = validator;
		return this;
	}
	
	public TextPanelComponent setMaxTextLength(int charLimit)
	{
		maxTextLength = charLimit;
		return this;
	}
	
	public TextPanelComponent setColor(int color)
	{
		this.color = color;
		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 TextPanelComponent setText(String s)
	{
		if(s == null)
		{
			return this;
		}
		if(validator.test(s))
		{
			if(s.length() > maxTextLength)
			{
				s = s.substring(0, maxTextLength);
			}
			float height = text.getBox().getHeight();
			float scale = text.getTextScale();
			while(s.length() > 0 && text.getFont().height(s) * scale > height)
			{
				s = s.substring(0, s.length() - 1);
			}
			text.setText(s);
		}
		return this;
	}
	
	@Override
	protected boolean updateSelf(int mouseX, int mouseY, float particalTicks)
	{
		if(text.isMouseOver(mouseX, mouseY) && !text.isOverChild(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(box, 0.001F, color);
		if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0)
		{
			TextMetadata data = text.getMetadata();
			float height = text.getFont().height() * text.getTextScale();
			if(hasSelectedText())
			{
				if(selectionPos.y() == curserPos.y())
				{
					render.drawQuad(box.getMinX() + data.getWidth(Math.min(curserPos.x(), selectionPos.x()), curserPos.y()), box.getMinY() + (height * curserPos.y()), box.getMinX() + data.getWidth(Math.max(curserPos.x(), selectionPos.x()), curserPos.y()), box.getMinY() + (height * (curserPos.y()+1)), 0.02F, text.getTextColor());
				}
				else
				{
					Vec2i min = selectionPos.y() < curserPos.y() ? selectionPos : curserPos;
					Vec2i max = selectionPos.y() > curserPos.y() ? selectionPos : curserPos;
					for(int i = min.y(),m=max.y();i<=m;i++)
					{
						if(i == min.y())
						{
							render.drawQuad(box.getMinX() + data.getWidth(min.x()), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor());
						}
						else if(i == max.y())
						{
							render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getWidth(max.x()), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor());
						}
						else
						{
							render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor());
						}
					}
				}
				return true;
			}
			float width = data.getWidth(curserPos.x(), curserPos.y());
			render.drawQuad(box.getMinX()+width, box.getMinY() + (height * curserPos.y()), box.getMinX()+width+text.getTextScale(), box.getMinY() + (height * (curserPos.y()+1)), 0.02F, text.getTextColor());
		}
		return true;
	}
	
	@Override
	public boolean onClick(int button, int mouseX, int mouseY)
	{
		setFlag(FLAG_FOCUS);
		if(hasSelectedText() && clickCount <= 0)
		{
			setSelectionPosition(-1);
			setCurserPosition(getMousePosition(mouseX, mouseY));
			return true;
		}
		int pos = getMousePosition(mouseX, mouseY);
		if(pos == getCurserPosition())
		{
			if(System.currentTimeMillis() - lastClickTime < 500L)
			{
				if(clickCount == 1)
				{
					Line line = text.getMetadata().getLine(curserPos.y());
					setSelectionPosition(line.getStart());
					setCurserPosition(line.getEnd());
					clickCount = 2;
				}
				else if(clickCount == 2)
				{
					clickCount = 3;
					setSelectionPosition(0);
					setCurserToEnd();
				}
				else
				{
					clickCount = 1;
					handleDoubleClick(pos);
				}
			}
			else
			{
				clickCount = 0;
			}
			lastClickTime = System.currentTimeMillis();
			return true;
		}
        lastClickTime = System.currentTimeMillis();
        setCurserPosition(pos);
		setSelectionPosition(-1);
        clickCount = 0;
		return false;
	}

	@Override
	public boolean onDrag(int mouseX, int mouseY)
	{
		if(!hasSelectedText())
		{
			setSelectionPosition(getCurserPosition());
		}
		setCurserPosition(getMousePosition(mouseX, mouseY));
		return true;
	}

	@Override
	public void onFocusLost()
	{
		clearFlag(FLAG_FOCUS);
	}
	
	@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_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 == null)
					{
						return true;
					}
					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 == null)
					{
						return true;
					}
					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_UP)
		{
			if(Keyboard.isShiftDown() && !hasSelectedText())
			{
				setSelectionPosition(getCurserPosition());
			}
			else if(!Keyboard.isShiftDown() && hasSelectedText())
			{
				setSelectionPosition(-1);
			}
			setCurserPosition(text.getMetadata().moveDown(curserPos));
		}
		else if(key == GLFW.GLFW_KEY_DOWN)
		{
			if(Keyboard.isShiftDown() && !hasSelectedText())
			{
				setSelectionPosition(getCurserPosition());
			}
			else if(!Keyboard.isShiftDown() && hasSelectedText())
			{
				setSelectionPosition(-1);
			}
			setCurserPosition(text.getMetadata().moveUp(curserPos));
		}
		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(key == GLFW.GLFW_KEY_ENTER)
		{
			writeText(Character.toString('\n'));
			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()));
                    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);
			notifyListeners(LISTENER_USER_ACTION);
			return true;
		}
		return 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());
		}
	}
	
	public int getMousePosition(int mouseX, int mouseY)
	{
		return text.getMetadata().getIndex(mouseX - text.getBox().getMinX(), mouseY - text.getBox().getMinY());
	}
	
	public void setCurserToEnd()
	{
		setCurserPosition(curserPosition);
	}
	
	public void setCurserPosition(int curserPosition)
	{
		if(this.curserPosition == curserPosition)
		{
			return;
		}
		this.curserPosition = curserPosition;
		text.getMetadata().convert(curserPosition, curserPos);
	}
	
	public int getCurserPosition()
	{
		return curserPosition;
	}
	
	public void setSelectionPosition(int selectionPosition)
	{
		if(this.selectionPosition == selectionPosition)
		{
			return;
		}
		this.selectionPosition = selectionPosition;
		if(selectionPosition == -1)
		{
			selectionPos.set(Vec2i.MINUS_ONE);
			return;
		}
		text.getMetadata().convert(selectionPosition, selectionPos);
	}
	
	public int getSelectionPosition()
	{
		return selectionPosition;
	}
	
	public boolean hasSelectedText()
	{
		return selectionPosition != -1;
	}
}
