import {
	measureDominionText,
	parse,
	renderDominionText,
} from "./dominiontext.ts";
import { DominionCardType, TYPE_ACTION } from "./types.ts";
import { DominionCard } from "./types.ts";

const imageCache: Record<string, HTMLImageElement> = {};
export const loadImage = (
	src: string,
	key?: string
): Promise<HTMLImageElement | null> => {
	return new Promise((resolve) => {
		if (key && key in imageCache && imageCache[key]) {
			resolve(imageCache[key]);
		}
		const img = new Image();
		img.onload = () => {
			if (key) {
				imageCache[key] = img;
			}
			resolve(img);
		};
		img.onerror = (e) => {
			console.log("err", e);
			resolve(null);
		};
		img.src = src;
	});
};

const imageList = [
	{
		key: "card-color-1",
		src: "/static/assets/CardColorOne.png",
	},
	{
		key: "card-brown",
		src: "/static/assets/CardBrown.png",
	},
	{
		key: "card-gray",
		src: "/static/assets/CardGray.png",
	},
	{
		key: "card-description-focus",
		src: "/static/assets/DescriptionFocus.png",
	},
	{
		key: "coin",
		src: "/static/assets/Coin.png",
	},
	{
		key: "debt",
		src: "/static/assets/Debt.png",
	},
	{
		key: "potion",
		src: "/static/assets/Potion.png",
	},
	{
		key: "vp",
		src: "/static/assets/VP.png",
	},
	{
		key: "vp-token",
		src: "/static/assets/VP-Token.png",
	},
];

export const loadImages = async () => {
	for (const imageInfo of imageList) {
		const { key, src } = imageInfo;
		await loadImage(src, key);
	}
};

export const getImage = (key: string) => {
	const image = imageCache[key];
	if (!image) {
		throw Error(`Tried to get an invalid image ${key}`);
	}
	return image;
};

export const colorImage = (
	image: HTMLImageElement,
	color?: string
): HTMLCanvasElement => {
	const canvas = document.createElement("canvas");
	canvas.width = image.width;
	canvas.height = image.height;
	const context = canvas.getContext("2d")!;
	context.save();
	context.drawImage(image, 0, 0);
	context.globalCompositeOperation = "multiply";
	context.fillStyle = color ?? "white";
	context.fillRect(0, 0, canvas.width, canvas.height);
	context.globalCompositeOperation = "destination-atop"; // restore transparency
	context.drawImage(image, 0, 0);
	context.restore();
	return canvas;
};

export const drawCard = (
	context: CanvasRenderingContext2D,
	card: DominionCard
): Promise<void> => {
	if (card.orientation === "card") {
		return drawStandardCard(context, card);
	} else {
		return drawLandscapeCard(context, card);
	}
};

const getColors = (types: DominionCardType[]): { primary: string } => {
	const byPriority = [...types]
		.filter((type) => type.color)
		.sort((a, b) => b.color!.priority - a.color!.priority);
	if (byPriority.length === 0) {
		return { primary: "white" };
	}
	const priority = byPriority[0]!;
	if (priority !== TYPE_ACTION) {
		return { primary: priority.color!.value };
	} else {
		const overriders = byPriority.filter((t) => t.color!.overridesAction);
		if (overriders.length) {
			return { primary: overriders[0]!.color!.value };
		} else {
			return { primary: priority.color!.value };
		}
	}
};

const drawStandardCard = async (
	context: CanvasRenderingContext2D,
	card: DominionCard & { orientation: "card" }
): Promise<void> => {
	const w = context.canvas.width;
	const h = context.canvas.height;
	let size;
	context.save();
	// Draw the image
	const image = await loadImage(card.image);
	if (image) {
		const cx = w / 2;
		const cy = 704;
		const windowHeight = 830;
		const windowWidth = 1194;
		const scale = Math.max(
			windowHeight / image.height,
			windowWidth / image.width
		);
		context.drawImage(
			image,
			cx - (scale * image.width) / 2,
			cy - (scale * image.height) / 2,
			scale * image.width,
			scale * image.height
		);
	}
	// Draw the card base
	const color = getColors(card.types).primary; // "#ffbc55";
	context.drawImage(colorImage(getImage("card-color-1"), color), 0, 0);
	context.drawImage(getImage("card-gray"), 0, 0);
	context.drawImage(colorImage(getImage("card-brown"), "#ff9911"), 0, 0);
	context.drawImage(getImage("card-description-focus"), 44, 1094);
	// Draw the name
	size = 78;
	context.font = `${size}pt DominionTitle`;
	while (
		(await measureDominionText(context, parse(card.title))).width > 1050
	) {
		size -= 1;
		context.font = `${size}pt DominionTitle`;
	}
	await renderDominionText(context, parse(card.title), w / 2, 220);
	// Draw the description
	context.font = "60pt DominionText";
	await renderDominionText(
		context,
		parse(card.description, { isDescription: true }),
		w / 2,
		1490,
		1000
	);
	console.log(card.title, parse(card.description));
	// Draw the types
	size = 65;
	context.font = `${size}pt DominionTitle`;
	while (
		(
			await measureDominionText(
				context,
				parse(card.types.map((t) => t.name).join(" - "))
			)
		).width > 800
	) {
		size -= 1;
		context.font = `${size}pt DominionTitle`;
	}
	await renderDominionText(
		context,
		parse(card.types.map((t) => t.name).join(" - ")),
		w / 2,
		1930,
		800
	);
	// Draw the cost
	context.font = "90pt DominionText";
	const costMeasure = await measureDominionText(context, parse(card.cost));
	await renderDominionText(
		context,
		parse(card.cost),
		130 + costMeasure.width / 2,
		1940
	);
	// Draw the preview
	if (card.preview) {
		context.font = "90pt DominionText";
		await renderDominionText(context, parse(card.preview), 200, 210);
		await renderDominionText(context, parse(card.preview), w - 200, 210);
	}
	// Draw the icon
	// Draw the author credit
	context.fillStyle = "white";
	context.font = "31pt DominionText";
	const authorMeasure = await measureDominionText(
		context,
		parse(card.author)
	);
	await renderDominionText(
		context,
		parse(card.author),
		w - 150 - authorMeasure.width / 2,
		2035
	);
	// Draw the artist credit
	const artistMeasure = await measureDominionText(
		context,
		parse(card.artist)
	);
	await renderDominionText(
		context,
		parse(card.artist),
		155 + artistMeasure.width / 2,
		2035
	);
	// Restore the context
	context.restore();
};

const drawLandscapeCard = async (
	context: CanvasRenderingContext2D,
	card: DominionCard & { orientation: "landscape" }
): Promise<void> => {
	// TODO: everything
};