var KeyHandler: KeyHandler = {
	elements: {
		keys: {}
	},

	selected: [],
	imagecache: {},
	ghostImage: null,

	init() {
		KeyHandler.ghostImage = new Image();
		KeyHandler.ghostImage.src = '/stc/icon/ghost.png';
		KeyHandler.ghostImage.onload = () => {
			for (var y = 0; y < 4; y++) {
				if (KeyHandler.elements.keys[y] == undefined) KeyHandler.elements.keys[y] = {};
				for (var x = 0; x < 8; x++) {
					var keyCheck: HTMLDivElement = document.querySelector(`.key[y="${y}"][x="${x}"]`);
					if (keyCheck) {
						((keyX: number, keyY: number, key: HTMLDivElement) => {
							var canvas = key.querySelector('canvas');
							var context = canvas.getContext('2d');
							KeyHandler.elements.keys[keyY][keyX] = { key, canvas, context };

							context.textBaseline = 'middle';
							context.textAlign = 'center';

							key.onclick = () => {
								if (altDown == false) KeyHandler.select(String(keyX), String(keyY));
								else {
									socket.emit('page', 'executekey', PageHandler.currentPageID, keyX, keyY);
								}
							};
						})(x, y, keyCheck);
					}
				}
			}
		};
	},

	clear() {
		for (var y in KeyHandler.elements.keys) {
			for (var x in KeyHandler.elements.keys[y]) {
				var key = KeyHandler.elements.keys[y][x];

				key.context.clearRect(0, 0, 100, 100);
				key.key.classList.remove('selected');
			}
		}
		KeyHandler.selected = [];
		Editor.close();
	},

	render(x: string, y: string, data: Page_Key) {
		if (KeyHandler.elements.keys[String(y)]) {
			if (KeyHandler.elements.keys[String(y)][String(x)]) {
				var context = KeyHandler.elements.keys[String(y)][String(x)].context;

				context.clearRect(0, 0, renderQuality, renderQuality);

				if (data.state.type == 'custom') {
					var appearence = data.appearence;

					render(appearence);
				} else if (data.state.type == 'ghost') {
					var appearence = data.appearence;

					if (appearence == undefined) appearence = {};

					appearence.system = { ghost: true };

					render(appearence);
				} else if (data.state.type == 'pageup') {
					render({
						text: { value: 'Up', color: '#ffffff', size: 18, offsetX: 0, offsetY: 25 },
						background: { color: '#4676b7' },
						image: {
							size: 100,
							rotation: 0,
							offsetX: 0,
							offsetY: -15,
							iconid: 'keyboard_arrow_up',
							iconstyle: 'white'
						},
						system: {
							border: {
								color: '#253e5e',
								thickness: 8
							}
						}
					});
				} else if (data.state.type == 'pagedown') {
					render({
						text: { value: 'Down', color: '#ffffff', size: 18, offsetX: 0, offsetY: -25 },
						background: { color: '#4676b7' },
						image: {
							size: 100,
							rotation: 0,
							offsetX: 0,
							offsetY: 15,
							iconid: 'keyboard_arrow_down',
							iconstyle: 'white'
						},
						system: {
							border: {
								color: '#253e5e',
								thickness: 8
							}
						}
					});
				} else if (data.state.type == 'currentpage') {
					render({
						text: {
							value: `Page\\n\\n${PageHandler.currentIndex + 1}`,
							color: '#ffffff',
							size: 22,
							offsetX: 0,
							offsetY: 0
						},
						background: { color: '#4676b7' },
						system: {
							border: {
								color: '#253e5e',
								thickness: 8
							}
						}
					});
				}
			} else console.error(`Invalid x '${x}'`);
		} else console.error(`Invalid y '${y}'`);

		function render(appearence: Page_Key_Appearence) {
			if (appearence.background != undefined) {
				context.fillStyle = appearence.background.color;
				context.fillRect(0, 0, renderQuality, renderQuality);
				context.fill();
			}

			if (appearence.text != undefined) {
				context.fillStyle = appearence.text.color;
				context.font = `800 ${appearence.text.size * fontSizeRatio}px "Montserrat"`;

				var text = appearence.text.value;
				var lineHeight = appearence.text.size * fontSizeRatio;

				var centerX = renderQuality / 2 + appearence.text.offsetX / 100 * (renderQuality * 2);
				var centerY = renderQuality / 2 + appearence.text.offsetY / 100 * renderQuality;
				var canvasYCounter = centerY;

				var words = text.replace(/\\n/g, ' \\n ').split(' ');
				var line = '';

				var totalLineHeight = 0;
				for (var n = 0; n < words.length; n++) {
					if (words[n].length == 0) continue;

					var testLine = line + words[n] + ' ';
					var metrics = context.measureText(testLine);
					var testWidth = metrics.width;
					if (words[n] != '\\n')
						if (testWidth > renderQuality && n > 0) {
							line = words[n] + ' ';
							totalLineHeight += lineHeight;
						} else {
							line = testLine;
						}
					else {
						totalLineHeight += lineHeight;
						line = '';
					}
				}

				line = '';
				canvasYCounter = canvasYCounter - totalLineHeight / 2;

				var firstSkip = false;
				for (var n = 0; n < words.length; n++) {
					if (words[n].length == 0) continue;
					var testLine = line + words[n] + ' ';
					var metrics = context.measureText(testLine);
					var testWidth = metrics.width;
					if (words[n] != '\\n')
						if (testWidth > renderQuality && n > 0) {
							context.fillText(line, centerX, canvasYCounter);
							line = words[n] + ' ';
							canvasYCounter += lineHeight;
						} else {
							line = testLine;
						}
					else {
						context.fillText(line, centerX, canvasYCounter);
						line = '';

						canvasYCounter += firstSkip ? lineHeight * 2 : lineHeight;
						if (firstSkip) firstSkip = false;
					}
				}
				context.fillText(line, centerX, canvasYCounter);
			}

			if (appearence.image != undefined) {
				var imageAddress =
					appearence.image.address != undefined
						? appearence.image.address
						: appearence.image.iconid != undefined
							? `/stc/materialicons/white/${appearence.image.iconid}.png`
							: null;

				var imageSize =
					appearence.image.size != undefined ? appearence.image.size / 100 * renderQuality : renderQuality;
				if (imageAddress) {
					var centerX = renderQuality / 2 + appearence.image.offsetX / 100 * renderQuality;
					var centerY = renderQuality / 2 + appearence.image.offsetY / 100 * renderQuality;

					if (KeyHandler.imagecache[imageAddress] != undefined)
						renderImage(KeyHandler.imagecache[imageAddress]);
					else {
						KeyHandler.imagecache[imageAddress] = new Image();
						KeyHandler.imagecache[imageAddress].src = imageAddress;
						KeyHandler.imagecache[imageAddress].onload = () =>
							renderImage(KeyHandler.imagecache[imageAddress]);
					}

					function renderImage(image) {
						context.save();
						context.translate(centerX, centerY);
						context.rotate(appearence.image.rotation * Math.PI / 180);
						context.drawImage(
							image,
							imageSize / 2 - imageSize,
							imageSize / 2 - imageSize,
							imageSize,
							imageSize
						);
						context.restore();
					}
				}
			}

			if (appearence.system != undefined) {
				if (appearence.system.border != undefined) {
					var relativeThickness = appearence.system.border.thickness / 100 * renderQuality;
					context.fillStyle = appearence.system.border.color;
					context.fillRect(0, 0, renderQuality, relativeThickness);
					context.rect(0, renderQuality - relativeThickness, renderQuality, relativeThickness);
					context.rect(0, 0, relativeThickness, renderQuality);
					context.rect(renderQuality - relativeThickness, 0, relativeThickness, renderQuality);
					context.fill();
				}

				if (appearence.system.ghost == true) {
					var size = 50 / 100 * renderQuality;
					context.save();
					context.globalAlpha = 0.7;
					context.translate(renderQuality / 2, renderQuality / 2);
					context.drawImage(KeyHandler.ghostImage, size / 2 - size, size / 2 - size, size, size);
					context.restore();
				}
			}
		}
	},

	select(x: string, y: string, multi: boolean = false) {
		if (PageHandler.currentPage != null) {
			if (PageHandler.currentPage.keys[y] != undefined && PageHandler.currentPage.keys[y][x] != undefined) {
				// var keyConfig = PageHandler.currentPage.keys[y][x];

				var query = `${x},${y}`;
				if (multi) {
					if (!KeyHandler.selected.includes(query)) KeyHandler.selected.push(query);
				} else
					KeyHandler.selected = [
						query
					];

				document.querySelectorAll('.key').forEach((key: HTMLDivElement) => {
					var checkX = key.getAttribute('x');
					var checkY = key.getAttribute('y');
					var checkQuery = `${checkX},${checkY}`;
					if (KeyHandler.selected.includes(checkQuery)) key.classList.add('selected');
					else key.classList.remove('selected');
				});

				if (KeyHandler.selected.length > 1) Editor.close();
				else {
					socket.emit('page', 'getkey', PageHandler.currentPageID, x, y, (key: Page_Key) => {
						Editor.open(x, y, key);
					});
				}
			}
		}
	}
};
KeyHandler.init();

var altDown = false;

window.addEventListener('keydown', (e: KeyboardEvent) => {
	if (e.key == 'alt' || e.altKey == true) altDown = true;
});
window.addEventListener('keyup', (e: KeyboardEvent) => {
	if (e.key == 'alt' || e.altKey == false) altDown = false;
});

interface KeyHandler {
	elements: {
		keys: {
			[y: string]: {
				[x: string]: {
					key: HTMLDivElement;
					canvas: HTMLCanvasElement;
					context: CanvasRenderingContext2D;
				};
			};
		};
	};

	selected: string[];
	imagecache: { [iconID: string]: HTMLImageElement };
	ghostImage: any;

	init: () => void;
	clear: () => void;
	render: (x: string, y: string, data: Page_Key) => void;
	select: (x: string, y: string, multi?: boolean) => void;
}

interface Page_Key {
	id: string;
	state: {
		type: KeyTypes;
		toggle: boolean;
		confirm: boolean;
	};
	actions: Page_Key_ActionTypes;
	appearence: Page_Key_Appearence;
}
interface Page_Key_Appearence {
	text?: Page_Key_Text;
	image?: Page_Key_Image;
	background?: Page_Key_Background;
	system?: {
		border?: {
			color: string;
			thickness: number;
		};
		ghost?: boolean;
	};
}

interface Page_Key_Text {
	value: string;
	size: number;
	color: string;
	offsetX: number;
	offsetY: number;
}

interface Page_Key_Image {
	address?: string;
	iconid?: string;
	iconstyle?: 'black' | 'white';
	size: number;
	offsetX: number;
	offsetY: number;
	rotation: number;
}

interface Page_Key_Background {
	color: string;
}

type KeyTypes = 'empty' | 'custom' | 'pageup' | 'pagedown' | 'currentpage' | 'ghost';
