import {
	setPixelsInRect,
	clearScreen,
	fillRect,
cameraPos,
fillCircle,
outlineCircle,
fillEllipse,
outlineEllipse,
setPixelColor,
} from "./window.ts";
import { CHAR, Font, font } from "./font.ts";
import { K, keyDown, keyPressed, keyReleased } from "./keyboard.ts";
import { addToContext, runCode } from "./runcode.ts";
import { resetRepl } from "./repl.ts";
import { COLOR } from "./colors.ts";
import { getSheet, getCodeSheet, getMapSheet } from "./sheet.ts";
import { saveCart, loadCart } from "./cart.ts";
import { outlineRect } from "./util.ts";

let spritesheet: number | null = null;

export const getSpritesheet = () => {
	return spritesheet;
}

export const useSpritesheet = (sheet: number) => {
	spritesheet = sheet;
}

export const drawSprite = (x: number, y: number, spr: number) => {
	if (!spritesheet) {
		return;
	}
	const {sheet_type, value: sprites} = getSheet(spritesheet);
	if (sheet_type !== "spritesheet") {
		throw "Trying to run a non-code sheet as code."
	}
	setPixelsInRect(x, y, 8, sprites[spr]);
}

export const drawIcon = (x: number, y: number, icon: Array<number>, color: number) => {
	setPixelsInRect(x, y, 8, icon.map(n => n*color));
}

export const measureCharFont = (char: string, fnt: Font) => {
	return (fnt.chars[char]?.length ?? 0)/fnt.height;
}

export const drawCharFont = (x: number, y: number, char: string, fnt: Font, color: number) => {
	const w = measureCharFont(char, fnt);
	if (!fnt.chars[char]) {
		return 0;
	}
	setPixelsInRect(x, y, w, fnt.chars[char].map(n => n*color));
	return w;
}

export const drawTextFont = (x: number, y: number, text: string, fnt: Font, color?: number) => {
	let dx = 0;
	[...text].forEach((char) => {
		dx += 1+drawCharFont(x+dx, y, char, fnt, color ?? COLOR.WHITE);
	});
	return dx-1;
}

export const measureTextFont = (text: string, fnt: Font) => {
	let w = 0;
	[...text].forEach((char) => {
		w += measureCharFont(char, fnt)+1;
	});
	return Math.max(0, w-1);
}

export const drawText = (x: number, y: number, text: string, color?: number) => {
	return drawTextFont(x, y, text, font, color);
}

export const measureText = (text: string) => {
	return measureTextFont(text, font);
}

export const camera = (x: number, y: number) => {
	cameraPos.x = x;
	cameraPos.y = y;
};

const faux = {
	// Graphics
	cls: () => {
		resetRepl();
		clearScreen();
	},
	camera,
	sprsht: useSpritesheet,
	spr: drawSprite,
	txt: drawText,
	rectfill: fillRect,
	rect: outlineRect,
	circfill: fillCircle,
	circ: outlineCircle,
	ovalfill: fillEllipse,
	oval: outlineEllipse,
	pset: setPixelColor,
	map: (mapSheet: number, tileX: number, tileY: number, screenX: number, screenY: number, tileW: number, tileH: number) => {
		const originalSpritesheet = getSpritesheet() ?? 0;
		getMapSheet(mapSheet).values.forEach(([sprSheet, spr], i) => {
			const x = i%64;
			const y = Math.floor(i/64);
			if (x >= tileX && y >= tileY && x < tileX + tileW && y < tileY + tileH) {
				useSpritesheet(sprSheet);
				drawSprite(screenX + (x-tileX)*8, screenY + (y-tileY)*8, spr);
			}
		});
		useSpritesheet(originalSpritesheet);
	},
	// Map
	mgetsht: (mapSheet: number, x: number, y: number) => {
		if (x < 0 || x >= 64 || y < 0 || y >= 64) {
			return undefined;
		}
		return getMapSheet(mapSheet).get(x, y)[0];
	},
	mgetspr: (mapSheet: number, x: number, y: number) => {
		if (x < 0 || x >= 64 || y < 0 || y >= 64) {
			return undefined;
		}
		return getMapSheet(mapSheet).get(x, y)[1];
	},
	// Temporarily removing mset, since it would overwrite static cart data
	// mset: (mapSheet: number, x: number, y: number, sprSheet: number, spr: number) => {
	// 	if (x < 0 || x >= 64 || y < 0 || y >= 64) {
	// 		return;
	// 	}
	// 	getMapSheet(mapSheet).set(x, y, [sprSheet, spr]);
	// },
	// Input
	[CHAR.UP]: K.ARROW_UP,
	[CHAR.DOWN]: K.ARROW_DOWN,
	[CHAR.LEFT]: K.ARROW_LEFT,
	[CHAR.RIGHT]: K.ARROW_RIGHT,
	btn: keyDown,
	btnp: keyPressed,
	btnr: keyReleased,
	// Cart
	save: saveCart,
	load: loadCart,
	// JS
	Array,
	BigInt: BigInt,
	Boolean,
	Date,
	Error,
	Function,
	Infinity: Infinity,
	JSON: JSON,
	Map,
	NaN: NaN,
	Number,
	Object,
	Promise,
	Proxy,
	Reflect: Reflect,
	RegExp,
	Set,
	String,
	Symbol: Symbol,
	WeakMap,
	WeakRef,
	WeakSet,
	isFinite,
	isNaN,
	// Math
	max: Math.max,
	min: Math.min,
	floor: Math.floor,
	ceil: Math.ceil,
	sin: Math.sin,
	cos: Math.cos,
	atan2: Math.atan2,
	sqrt: Math.sqrt,
	abs: Math.abs,
	rand: Math.random,
	[CHAR.PI]: Math.PI,
	// Other
	code: (n: number) => {
		return runCode(getCodeSheet(n));
	},
	log: console.log,
};

for (const key in faux) {
	addToContext(key, faux[key as keyof typeof faux]);
}

export default faux;