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

import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntCollections;
import org.apache.commons.lang3.NotImplementedException;

import java.util.Collections;
import java.util.Objects;

public final class BlockPosition {
    private final BlockOrContainer pos;

	public BlockPosition(BlockOrContainer pos) {
		this.pos = pos;
	}

	public BlockPosition(Block block) {
		this.pos = block;
	}

	public BlockPosition(BlockContainer container) {
		this.pos = container;
	}

	public boolean isBlock() {
		return pos instanceof Block;
	}

	public boolean isContainer() {
		return pos instanceof BlockContainer;
	}

	public Block getBlock() {
		return (Block) pos;
	}

	public BlockContainer getContainer() {
		return (BlockContainer) pos;
	}

    public BlockOrContainer get() {
        return pos;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null || obj.getClass() != this.getClass()) return false;
        var that = (BlockPosition) obj;
        return Objects.equals(this.pos, that.pos);
    }

    @Override
    public int hashCode() {
        return Objects.hash(pos);
    }

    @Override
    public String toString() {
        return "BlockPosition[" +
                "pos=" + pos + ']';
    }

	public BlockPosition getNext() {
		if (pos instanceof BlockContainer) {
			var container = (BlockContainer) pos;
			var content = container.getContent();
			if (content.isEmpty()) {
				BlockContainer nextContainer;
				if (container.hasParent()) {
					var containers = container.getParentBlock().getInnerContainers();
					var it = containers.listIterator(containers.indexOf(container) + 1);
					nextContainer = it.hasNext() ? it.next() : null;
				} else {
					nextContainer = null;
				}
				if (nextContainer != null) {
					return new BlockPosition(nextContainer);
				} else {
					var parentBlock = container.getParentBlock();
					return parentBlock != null ? new BlockPosition(parentBlock) : null;
				}
			} else {
				var targetBlock = content.get(0);
				var innerContainers = targetBlock.getInnerContainers();
				if (innerContainers != null && !innerContainers.isEmpty()) {
					return new BlockPosition(innerContainers.get(0));
				} else {
					return new BlockPosition(targetBlock);
				}
			}
		} else if (pos instanceof Block) {
			var block = (Block) pos;
			var ref = block.getReference();
			var nextBlock = ref.getNextBlock();
			if (nextBlock == null) {
				var container = block.getParentContainer();
				BlockContainer nextContainer;
				if (container.hasParent()) {
					var containers = container.getParentBlock().getInnerContainers();
					var it = containers.listIterator(containers.indexOf(container) + 1);
					nextContainer = it.hasNext() ? it.next() : null;
				} else {
					nextContainer = null;
				}
				if (nextContainer != null) {
					return new BlockPosition(nextContainer);
				} else {
					var parentBlock = container.getParentBlock();
					return parentBlock != null ? new BlockPosition(parentBlock) : null;
				}
			} else {
				var targetBlock = nextBlock.get();
				var innerContainers = targetBlock.getInnerContainers();
				if (innerContainers != null && !innerContainers.isEmpty()) {
					return new BlockPosition(innerContainers.get(0));
				} else {
					return new BlockPosition(targetBlock);
				}
			}
		} else {
			throw new UnsupportedOperationException();
		}
	}

	public BlockPosition getPrevious() {
		if (pos instanceof BlockContainer) {
			var container = (BlockContainer) pos;
			if (container.hasParent()) {
				var parentBlock = container.getParentBlock();
				var innerContainers = parentBlock.getInnerContainers();
				if (innerContainers != null) {
					var it = innerContainers.listIterator(innerContainers.indexOf(container));
					if (it.hasPrevious()) {
						var prevContainer = it.previous();
						return prevContainer.getLastPosition();
					} else {
						var parentContainer = parentBlock.getParentContainer();
						var blocks = parentContainer.getContentUnsafe();
						var bIt = blocks.listIterator(blocks.indexOf(parentBlock));
						if (bIt.hasPrevious()) {
							return new BlockPosition(bIt.previous());
						} else {
							return new BlockPosition(parentContainer);
						}
					}
				} else {
					throw new UnsupportedOperationException();
				}
			} else {
				return null;
			}
		} else if (pos instanceof Block) {
			var block = (Block) pos;
			var innerContainers = block.getInnerContainers();
			if (innerContainers != null) {
				var lastContainer = innerContainers.get(innerContainers.size() - 1);
				return lastContainer.getLastPosition();
			}
			var ref = block.getReference();
			var prevBlock = ref.getPreviousBlock();
			if (prevBlock == null) {
				var container = block.getParentContainer();
				return new BlockPosition(container);
			} else {
				var targetBlock = prevBlock.get();
				return new BlockPosition(targetBlock);
			}
		} else {
			throw new UnsupportedOperationException();
		}
	}

	public IntArrayList getURL() {
		var url = new IntArrayList();
		var parent = pos;
		while (parent != null) {
			var index = parent.getIndex();
			if (index == -1) break;
			url.add(index);
			parent = parent.getParent();
		}
		Collections.reverse(url);
		return url;
	}
}
