import { css } from "@emotion/css";
import { Mathuscript, parseMathuscript } from "./parse";
import { useCallback, useMemo, useRef, useState } from "react";
import { MathText } from "./MathText";
import { characterData } from "./data";
import { DialogText, DialogTextImperatives } from "./DialogText";

type VisualState = {
	turn: number,
	characters: {name: string, emotion: string, side: "left" | "right", lastTurn: number}[],
	board: {text: string},
	dialog: {name: string | null, emotion: string, text: string},
}

const otherSide = (side: "left" | "right"): "left" | "right" => {
	return ({
		left: "right",
		right: "left",
	} as const)[side];
}

const afterStep = (state: VisualState, step: Mathuscript[number]): VisualState => {
	const newState = structuredClone(state);
	const {characters, board, dialog} = newState;
	newState.turn += 1;
	if (step.name === "board") {
		board.text = step.text;
	} else {
		characters.sort((a, b) => a.lastTurn - b.lastTurn);
		let char = characters.find(c => c.name === step.name);
		if (!char) {
			char = {name: step.name, emotion: "default", side: "left", lastTurn: -1};
			if (characters.length > 1) {
				characters.splice(0, characters.length-1);
			}
			const otherChar = characters[0];
			if (otherChar) {
				char.side = otherSide(otherChar.side);
			}
			characters.push(char);
		}
		char.lastTurn = newState.turn;
		char.emotion = step.emotion ?? char.emotion;
		dialog.name = step.name;
		dialog.emotion = char.emotion;
		dialog.text = step.text;
	}
	return newState;
}

const getCharData = (name: string | null) => {
	if (name && name in characterData) {
		return characterData[name as keyof typeof characterData];
	}
	return null;
}

const allChars = Object.keys(characterData) as (keyof typeof characterData)[];

export const MathuscriptPlayer = (props: { script: string }) => {
	const {script} = props;
	const parsedScript = useMemo(() => parseMathuscript(script), [script]);
	const goAgain = useRef(false);
	const [index, setIndex] = useState(0);
	const [visualState, setVisualState] = useState<VisualState>({turn: 0, characters: [], board: {text: ""}, dialog: {name: null, emotion: "", text: ""}});
	const dialogGoing = useRef(false);
	const dialogTextRef = useRef<DialogTextImperatives>(null);

	const doStep = useCallback(() => {
		goAgain.current = false;
		if (dialogGoing.current) {
			if (dialogTextRef.current) {
				dialogTextRef.current.skip();
			}
			return;
		}
		const step = parsedScript[index];
		if (step) {
			setIndex(i => i+1);
			setVisualState(state => afterStep(state, step));
			if (step.name === "board") {
				goAgain.current = true;
			} else {
				dialogGoing.current = true;
			}
		} else {
			console.log("the end");
		}
	}, [index, parsedScript, setIndex, setVisualState]);

	const onDialogFinish = useCallback(() => {
		dialogGoing.current = false;
	}, []);

	if (goAgain.current) {
		doStep();
	}

	const speakingChar = getCharData(visualState.dialog.name);

	return (
		<>
			<div className={css`
				width: 800px;
				height: 480px;
				border: 1px solid black;
				font-size: 16px;
				margin: auto;
				position: relative;
				background-image: url("/assets/background.png");
				background-size: 118%;
				background-position-x: center;
				background-position-y: bottom;
				overflow: hidden;
			`}>
				<div className={css`
					position: absolute;
					width: 58%;
					top: 7%;
					left: 21%;
					height: 64%;
					display: flex;
					align-items: center;
					justify-content: center;
					color: hsla(70, 60%, 95%, 0.9);
					padding: 1em;
				`}>
					<div>
						<MathText>{visualState.board.text}</MathText>
					</div>
				</div>
				<div className={css`
					position: absolute;
					width: 100%;
					height: 100%;
					top: 0;
					left: 0;
					--char-pos: ${visualState.board.text ? '-20%' : '0'};
				`}>
					{
						allChars.map(name => {
							const visChar = visualState.characters.find(ch => ch.name === name);
							// TODO: make it change with emotion
							const charDat = getCharData(name);
							if (!charDat) {
								return null;
							}
							const visualInactive = visChar && visualState.dialog.name !== name;
							return [
								<img
									className={css`
										position: absolute;
										transition: all 0.35s;
										left: -60%;
										${
											visChar?.side === "left" && css`left: var(--char-pos);`
										}
										height: 90%;
										bottom: 0;
										${
											visualInactive && css`bottom: -3%;`
										}
										--outline-color: white;
										/* --outline-color: hsl(300, 45%, 40%); */
										filter: drop-shadow(1px 1px 0 var(--outline-color)) drop-shadow(-1px 1px 0 var(--outline-color)) drop-shadow(1px -1px 0 var(--outline-color)) drop-shadow(-1px -1px 0 var(--outline-color));
									`}
									key={`${name}-left`}
									src={charDat.assets["default"]}
								/>,
								<img
									className={css`
										position: absolute;
										transition: all 0.35s;
										transform: scaleX(-1);
										right: -60%;
										${
											visChar?.side === "right" && css`right: var(--char-pos);`
										}
										height: 90%;
										bottom: 0;
										${
											visualInactive && css`bottom: -3%;`
										}
										--outline-color: white;
										/* --outline-color: hsl(300, 45%, 40%); */
										filter: drop-shadow(1px 1px 0 var(--outline-color)) drop-shadow(-1px 1px 0 var(--outline-color)) drop-shadow(1px -1px 0 var(--outline-color)) drop-shadow(-1px -1px 0 var(--outline-color));
									`}
									key={`${name}-right`}
									src={charDat.assets["default"]}
								/>
							]
						})
					}
				</div>
				<div className={css`
					padding: 1em;
					display: flex;
					position: absolute;
					width: 100%;
					height: 25%;
					bottom: 0;
				`}>
					<div className={css`
						flex-basis: 0;
						flex-grow: 1;
						position: relative;
						background-color: hsla(220, 50%, 40%, 0.85);
						border: 2px solid hsla(220, 30%, 60%, 0.85);
						border-radius: 0.25em;
						padding: 0.5em;
						color: white;
					`}>
						{
							speakingChar && <>
								<div className={css`
									position: absolute;
									bottom: 100%;
									left: 0;
									background-color: hsla(220, 50%, 40%, 0.85);
									border: 2px solid hsla(220, 30%, 60%, 0.85);
									border-radius: 0.25em;
									padding: 0.25em 0.75em;
								`}>
									<strong>{speakingChar.displayName}</strong> {visualState.dialog.emotion && <em>({visualState.dialog.emotion})</em>}
								</div>
								<DialogText onFinish={onDialogFinish} ref={dialogTextRef}>
									{visualState.dialog.text}
								</DialogText>
							</>
						}
					</div>
				</div>
			</div>
			<button onClick={doStep}>Step</button>
		</>
	);
};