import { Link, useParams, useSearchParams } from "react-router-dom";
import { MutableRefObject, useEffect, useRef, useState } from "react";
import { css } from "@emotion/css";
import { useWebsocket } from "./hooks/useWebsocket";
import { Pico8Player } from "@athingperday/react-pico-player";

type PicoPlayerHandle =
	NonNullable<
		Parameters<typeof Pico8Player>["0"]["consoleRef"]
	> extends MutableRefObject<infer T>
		? NonNullable<T>
		: never;

type Game = {
	carts: Parameters<typeof Pico8Player>["0"]["carts"];
};

const getPatch = (before: number[], after: number[]) => {
	const diff: Record<number, number> = {};
	for (const i in after) {
		if (after[i] !== before[i]) {
			diff[i] = after[i];
		}
	}
	return diff;
};

const applyPatch = (before: number[], patch: Record<number, number>) => {
	for (const i in patch) {
		before[i] = patch[i];
	}
};

export const GamePage = () => {
	const { author, slug } = useParams();
	// const [text, setText] = useState("");
	const [prevGpio, setPrevGpio] = useState<number[] | null>(null);
	const [searchParams, setSearchParams] = useSearchParams();
	const room = searchParams.get("room");
	const picoRef = useRef<PicoPlayerHandle>(null);
	const socket = useWebsocket({
		url: `/api/ws/room?room=${room}`,
		onMessage({ message }) {
			// console.log("message", message);
			const msg = message as any;
			if (msg.getGpio) {
				if (picoRef.current) {
					const handle = picoRef.current;
					if (handle) {
						socket.sendMessage({ gpio: handle.gpio });
					}
				}
			}
			if (msg.gpio) {
				if (picoRef.current) {
					const handle = picoRef.current;
					if (handle) {
						// console.log("updating pico gpio");
						handle.gpio.length = 0;
						handle.gpio.push(...msg.gpio);
						setPrevGpio([...handle.gpio]);
					}
				}
			}
			if (msg.gpioPatch) {
				if (picoRef.current) {
					const handle = picoRef.current;
					if (handle) {
						// console.log("updating pico gpio");
						applyPatch(handle.gpio, msg.gpioPatch);
						setPrevGpio([...handle.gpio]);
					}
				}
			}
		},
	});
	const [game, setGame] = useState<Game | null>(null);

	useEffect(() => {
		const fetchInfo = async () => {
			let url = `/api/game?author=${author}&name=${slug ?? ""}`;
			const information = await fetch(url);
			const json = await information.json();
			console.log(json);
			setGame(json);
		};
		fetchInfo();
	}, [setGame, slug]);

	useEffect(() => {
		const interval = setInterval(() => {
			const handle = picoRef.current;
			if (!handle) {
				return;
			}
			if (JSON.stringify(handle.gpio) !== JSON.stringify(prevGpio)) {
				if (prevGpio) {
					setPrevGpio([...handle.gpio]);
					socket.sendMessage({
						gpioPatch: getPatch(prevGpio, handle.gpio),
					});
				} else {
					socket.sendMessage({ getGpio: true });
					setPrevGpio([...handle.gpio]);
				}
			}
		}, 1000 / 60);

		return () => {
			clearInterval(interval);
		};
	});

	if (!game) {
		return <div>LOADING...</div>;
	}

	if (!game.carts) {
		return <div>NOT FOUND</div>;
	}

	return (
		<div
			className={css`
				margin: auto;
				width: max-content;
				max-inline-size: 66ch;
				padding: 1.5em;
				display: flex;
				flex-direction: column;
				gap: 1em;
			`}
		>
			<div>
				<h1>{slug}</h1>
				<h2>
					by <Link to={`/u/${author}`}>{author}</Link>
				</h2>
			</div>
			<div
				className={css`
					width: 512px;
					max-width: 100%;
					margin: auto;
				`}
			>
				<div
					className={css`
						border: 2px solid transparent;
						&:focus-within {
							border: 2px solid limegreen;
						}
					`}
				>
					<Pico8Player consoleRef={picoRef} carts={game.carts} />
				</div>
			</div>
			<div
				className={css`
					display: flex;
					flex-wrap: wrap;
				`}
			>
				{game.carts.map((cart) =>
					"src" in cart ? (
						<div
							key={cart.name}
							className={css`
								display: flex;
								flex-direction: column;
							`}
						>
							<img src={cart.src} />
							<a href={cart.src} download={`${cart.name}.p8.png`}>
								Download
							</a>
						</div>
					) : null,
				)}
			</div>
			{/* <div>
				<input onChange={(x) => setText(x.target.value)} />
				<button
					onClick={() => {
						socket.sendMessage({ text });
					}}
				>
					Send
				</button>
			</div> */}
			{/* <div>
				<p>This is a paragraph about this game. It is a cool game. And a cool website to play it on. It automagically connects from GitHub.</p>
			</div> */}
		</div>
	);
};
