package it.cavallium.warppi.gui.expression.containers;

import it.cavallium.warppi.gui.expression.Caret;
import it.cavallium.warppi.gui.expression.CaretState;
import it.cavallium.warppi.gui.expression.InputContext;
import it.cavallium.warppi.gui.expression.blocks.*;
import it.cavallium.warppi.math.MathematicalSymbols;

import java.util.ArrayList;
import java.util.List;

public class NormalInputContainer extends InputContainer {

	@Deprecated()
	/**
	 * Use NormalInputContainer(InputContext) instead
	 */
	public NormalInputContainer() {
		super();
	}

	public NormalInputContainer(final InputContext ic) {
		super(ic);
	}

	public NormalInputContainer(final InputContext ic, final boolean small) {
		super(ic, small);
	}

	public NormalInputContainer(final InputContext ic, final boolean small, final int minWidth, final int minHeight) {
		super(ic, small, minWidth, minHeight);
	}

	/**
	 * Copy
	 * @param ic
	 */
	public NormalInputContainer(InputContainer old, InputContext ic) {
		super(old, ic);
	}

	@Override
	public Block parseChar(final char c) {
		switch (c) {
			case MathematicalSymbols.DIVISION:
				return new BlockDivision();
			case MathematicalSymbols.SQUARE_ROOT:
				return new BlockSquareRoot();
			case MathematicalSymbols.PARENTHESIS_OPEN:
				return new BlockParenthesis();
			case MathematicalSymbols.PARENTHESIS_CLOSE:
				return null;
			case MathematicalSymbols.POWER:
				return new BlockPower();
			case MathematicalSymbols.POWER_OF_TWO:
				return new BlockPower2();
			case MathematicalSymbols.MULTIPLICATION:
			case MathematicalSymbols.SUM:
			case MathematicalSymbols.SUM_SUBTRACTION:
			case MathematicalSymbols.SUBTRACTION:
				return new BlockChar(c);
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '.':
				return new BlockNumericChar(c);
			case MathematicalSymbols.SINE:
				return new BlockSine();
			case MathematicalSymbols.LOGARITHM:
				return new BlockLogarithm();
			case MathematicalSymbols.PI:
				return new BlockVariable(inputContext, c, true);
			case MathematicalSymbols.EULER_NUMBER:
				return new BlockVariable(inputContext, c, true);
			default:
				for (final char v : MathematicalSymbols.variables) {
					if (c == v) {
						return new BlockVariable(inputContext, c);
					}
				}
				return new BlockChar(c);
		}
	}

	@Override
	public void typeChar(final char c) {
		super.typeChar(c);
		switch (c) {
			case MathematicalSymbols.PARENTHESIS_CLOSE: {
				final BlockReference<?> ref = getSelectedBlock();
				BlockParenthesisAbstract parenthesis = findParenthesis(ref.get());
				if (parenthesis == null) {
					break;
				} else {
					moveTo(new BlockPosition(parenthesis));
				}
				break;
			}
			case MathematicalSymbols.POWER_OF_TWO:
				moveTo(new BlockPosition(getSelectedBlock().get()));
				break;
			case MathematicalSymbols.DIVISION: {
				@SuppressWarnings("unchecked")
				final BlockReference<BlockDivision> ref = (BlockReference<BlockDivision>) getSelectedBlock();
				@SuppressWarnings("unused")
				final BlockContainer parentContainer = ref.getContainer();
				BlockReference<?> currentBlock = ref;
				List<Block> blocksToMove = new ArrayList<>();
				boolean groupedBefore = false;
				int before = 0;
				while (true) {
					currentBlock = currentBlock.getPreviousBlock();
					if (currentBlock == null) {
						break;
					}
					final Block b = currentBlock.get();
					if (b instanceof BlockNumericChar || b instanceof BlockVariable) {
						if (!groupedBefore) {
							groupedBefore = true;
						}
						blocksToMove.add(b);
						before++;
					} else {
						break;
					}
				}
				if (groupedBefore) {
					var div = ref.get();
					var upperContainer = div.getUpperContainer();
					for (int i = blocksToMove.size() - 1; i >= 0; i--) {
						var b = blocksToMove.get(i);
						b.getParentContainer().removeBlockUnsafe(b);
						upperContainer.appendBlock(b);
					}
					div.recomputeDimensionsToRoot();
					moveTo(new BlockPosition(div.getLowerContainer()));
				}
				break;
			}
		}
	}

	private BlockParenthesisAbstract findParenthesis(Block o) {
		while (!(o instanceof BlockParenthesisAbstract)) {
			var pc = o.getParentContainer();
			if (!pc.hasParent()) {
				return null;
			}
			o = pc.getParentBlock();
			if (o == null) {
				return null;
			}
		}
		return (BlockParenthesisAbstract) o;
	}
}
