import { clearScreen, fillRect } from "./window.ts";
import { font, fontWidth, fontHeight } from "./font.ts";
import { drawText } from "./builtins.ts";
import { COLOR } from "./colors.ts";
import { getCart } from "./cart.ts";
import {getSheet, setSheet} from "./sheet.ts";
import { K, getKeyboardString, getKeysPressed, keyDown, keyPressed, shiftKeyDown } from "./keyboard.ts";

// deno-lint-ignore prefer-const
let tab: "code" | "sprite" | "map" | "sfx" | "music" = "code";

const codeTabState = {
	scrollX: 0,
	scrollY: 0,
	anchor: 0,
	focus: 0,
	get focusX() {return indexToGrid(this.code, this.focus).x;},
	get focusY() {return indexToGrid(this.code, this.focus).y;},
	get anchorX() {return indexToGrid(this.code, this.anchor).x;},
	get anchorY() {return indexToGrid(this.code, this.anchor).y;},
	isCollapsed() {
		return this.anchor === this.focus;
	},
	clampInRange(n: number) {
		return Math.max(0, Math.min(n, this.code.length))
	},
	setSelection(anchor: number | {x: number, y: number}, focus?: number | {x: number, y: number}) {
		if (typeof anchor !== "number") {
			anchor = gridToIndex(this.code, anchor.x, anchor.y);
		}
		focus = focus ?? anchor;
		if (typeof focus !== "number") {
			focus = gridToIndex(this.code, focus.x, focus.y);
		}
		this.anchor = this.clampInRange(anchor),
		this.focus = this.clampInRange(focus);
	},
	setFocus(focus: number | {x: number, y: number}) {
		if (typeof focus !== "number") {
			focus = gridToIndex(this.code, focus.x, focus.y);
		}
		this.focus = this.clampInRange(focus);
	},
	insertText(text: string) {
		const {code, anchor, focus} = this;
		this.code = code.slice(0, Math.min(anchor, focus)) + text + code.slice(Math.max(anchor, focus));
		this.setSelection(Math.min(anchor, focus) + text.length);
	},
	indent(indentString: string) {
		const lines = this.code.split("\n");
		const {focusY, anchorY} = this;
		const newLines = lines.map((line, i) => {
			console.log(i, Math.min(focusY, anchorY), Math.max(focusY, anchorY));
			if (i >= Math.min(focusY, anchorY) && i <= Math.max(focusY, anchorY)) {
				console.log(indentString+line);
				return indentString+line;
			} else {
				return line;
			}
		});
		this.code = newLines.join("\n");
	},
	outdent(outdentRegex: RegExp) {
		const lines = this.code.split("\n");
		const {focusY, anchorY} = this;
		const newLines = lines.map((line, i) => {
			const match = line.match(outdentRegex);
			if (i >= Math.min(focusY, anchorY) && i <= Math.max(focusY, anchorY) && match) {
				return line.slice(match[0].length);
			} else {
				return line;
			}
		});
		this.code = newLines.join("\n");
	},
	backspace() {
		const {code, focus} = this;
		if (this.isCollapsed()) {
			if (focus > 0) {
				this.code = code.slice(0, focus-1) + code.slice(focus);
				this.setSelection(focus-1);
			}
		} else {
			this.insertText("");
		}
	},
	get code() {
		return getSheet(0);
	},
	set code(val) {
		setSheet(0, "code", val);
	}
}

const indexToGrid = (str: string, index: number) => {
	const linesUpTo = str.slice(0,index).split("\n");
	return {
		x: linesUpTo[linesUpTo.length-1].length,
		y: linesUpTo.length - 1,
	}
}

const gridToIndex = (str: string, x: number, y: number) => {
	const lines = str.split("\n");
	if (y < 0) {
		return 0;
	}
	if (y >= lines.length) {
		return str.length;
	}
	return lines.slice(0, y).join("\n").length+Math.min(x, lines[y].length)+1;
}

const drawCodeField = (code: string, x: number, y: number, w: number, h: number) => {
	const {
		scrollX,
		scrollY,
		anchor,
		focus,
	} = codeTabState;
	const {
		x: focusX,
		y: focusY,
	} = indexToGrid(code, focus);
	const {
		x: anchorX,
		y: anchorY,
	} = indexToGrid(code, anchor);
	fillRect(x, y, w, h, COLOR.DARKBLUE);
	if (anchor === focus) {
		fillRect(x+focusX*fontWidth-scrollX, y+focusY*(fontHeight+1)-scrollY, fontWidth+1, fontHeight+1, COLOR.RED);
	} else {
		fillRect(x+anchorX*fontWidth-scrollX, y+anchorY*(fontHeight+1)-scrollY, fontWidth+1, fontHeight+1, COLOR.GREEN);
		fillRect(x+focusX*fontWidth-scrollX, y+focusY*(fontHeight+1)-scrollY, fontWidth+1, fontHeight+1, COLOR.YELLOW);
	}
	code.split("\n").forEach((line, i) => {
		drawText(x-scrollX, 1+y+i*(fontHeight+1)-scrollY, line);
	});
}

const update = () => {
	if (tab === "code") {
		const {code, anchor, focus, focusX, focusY} = codeTabState;
		const keyboardString = getKeyboardString();
		if (keyboardString) {
			codeTabState.insertText(keyboardString);
		}
		if (keyPressed(K.ENTER)) {
			codeTabState.insertText("\n");
		}
		if (keyPressed(K.TAB)) {
			if (!shiftKeyDown()) {
				if (codeTabState.isCollapsed()) {
					codeTabState.insertText("\t");
				} else {
					codeTabState.indent("\t");
				}
			} else {
				codeTabState.outdent(/^(\t| )/);
			}
		}
		if (keyPressed(K.BACKSPACE)) {
			codeTabState.backspace();
		}
		if (keyPressed(K.ARROW_RIGHT)) {
			if (shiftKeyDown()) {
				codeTabState.setFocus(focus+1);
			} else {
				codeTabState.setSelection(focus+1);
			}
		}
		if (keyPressed(K.ARROW_LEFT)) {
			if (shiftKeyDown()) {
				codeTabState.setFocus(focus-1);
			} else {
				codeTabState.setSelection(focus-1);
			}
		}
		if (keyPressed(K.ARROW_DOWN)) {
			if (shiftKeyDown()) {
				codeTabState.setFocus({x: focusX, y: focusY+1});
			} else {
				codeTabState.setSelection({x: focusX, y: focusY+1});
			}
		}
		if (keyPressed(K.ARROW_UP)) {
			if (shiftKeyDown()) {
				codeTabState.setFocus({x: focusX, y: focusY-1});
			} else {
				codeTabState.setSelection({x: focusX, y: focusY-1});
			}
		}
	}
}

const draw = () => {
	clearScreen();
	if (tab === "code") {
		drawCodeField(getSheet(0), 0, 8, 128, 112);
	}
}

export const editmode = {
	update,
	draw,
}