import Anim from "./animation/anim.js";
import Frame from "./animation/frame.js";
import Layer, { TAG } from "./animation/layer.js";
import { PALETTE } from "./animation/sprites.js";
import { getLayerPixels } from "./shared.js";

const HAT_WIDTH = 12;

export const HAT = {
	NONE: "none",
	TOP_HAT: "top-hat",
	FEZ: "fez",
	WIZARD_HAT: "wizard-hat",
	BASEBALL_CAP: "baseball-cap",
	FLOWER_HAT: "flower-hat",
	COWBOY_HAT: "cowboy-hat",
	BEANIE: "beanie",
	SUN_HAT: "sun-hat",
	VIKING_HELMET: "viking-helmet",
	STRAW_HAT: "straw-hat",
	CORDOVAN_HAT: "cordovan-hat"
};

/** @type {{ [hatId: string]: { name: string, description: string } }} */
export const HAT_METADATA = {
	[HAT.NONE]: {
		name: "Invisible Hat",
		description: "It's like you're wearing nothing at all!"
	},
	[HAT.TOP_HAT]: {
		name: "Top Hat",
		description: "The mark of a true gentlebird."
	},
	[HAT.VIKING_HELMET]: {
		name: "Viking Helmet",
		description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?"
	},
	[HAT.COWBOY_HAT]: {
		name: "Cowboy Hat",
		description: "You can't jam with the console cowboys without the appropriate attire."
	},
	[HAT.FEZ]: {
		name: "Fez",
		description: "It's a fez. Fezzes are cool."
	},
	[HAT.WIZARD_HAT]: {
		name: "Wizard Hat",
		description: "Grants the bearer terrifying mystical power, but luckily birds only use it to summon old ladies with bread crumbs."
	},
	[HAT.BASEBALL_CAP]: {
		name: "Baseball Cap",
		description: "Birds unfortunately only ever hit 'fowl' balls..."
	},
	[HAT.FLOWER_HAT]: {
		name: "Flower Hat",
		description: "To be fair, this is less of a hat and more of a dirt clod that your pet happened to pick up."
	},
	[HAT.BEANIE]: {
		name: "Beanie",
		description: "Keeps feathers warm on those long migrations south!"
	},
	[HAT.SUN_HAT]: {
		name: "Sun Hat",
		description: "Perfect for frolicking through enchanted flower fields."
	},
	[HAT.STRAW_HAT]: {
		name: "Straw Hat",
		description: "A classic design, though keep away from water as this particular hat is seemingly unable to float."
	},
	[HAT.CORDOVAN_HAT]: {
		name: "Cordovan Hat",
		description: "A traditional Spanish hat that stays put even in the wildest of sword fights."
	}
};

/**
 * @param {string[][]} spriteSheet 
 * @returns {{ base: Layer[], down: Layer[] }}
 */
export function createHatLayers(spriteSheet) {
	const hatLayers = {
		base: [],
		down: []
	};
	for (let i = 0; i < Object.keys(HAT).length; i++) {
		const hatName = Object.keys(HAT)[i];
		if (hatName === 'NONE') {
			continue;
		}
		const index = i - 1;
		const hatKey = HAT[hatName];
		const hatLayer = buildHatLayer(spriteSheet, hatKey, index);
		const downHatLayer = buildHatLayer(spriteSheet, hatKey, index, 1);
		hatLayers.base.push(hatLayer);
		hatLayers.down.push(downHatLayer);
	}
	return hatLayers;
}

/**
 * @param {string[][]} spriteSheet
 * @param {string} hatId 
 * @returns {Anim}
 */
export function createHatItemAnimation(hatId, spriteSheet) {
	const hatLayer = buildHatItemLayer(spriteSheet, hatId);
	const frames = [
		new Frame([hatLayer])
	];
	return new Anim(frames, [1000], true);
}

/**
 * @param {string[][]} spriteSheet 
 * @param {string} hatName
 * @param {number} hatIndex
 * @param {number} [yOffset=0]
 * @returns {Layer}
 */
function buildHatLayer(spriteSheet, hatName, hatIndex, yOffset = 0) {
	const LEFT_PADDING = 6;
	const RIGHT_PADDING = 14;
	const TOP_PADDING = 5 + yOffset;
	const BOTTOM_PADDING = Math.max(0, 15 - yOffset);

	let hatPixels = getLayerPixels(spriteSheet, hatIndex, HAT_WIDTH);
	hatPixels = pad(hatPixels, TOP_PADDING, BOTTOM_PADDING, LEFT_PADDING, RIGHT_PADDING);
	hatPixels = drawOutline(hatPixels, false);

	return new Layer(hatPixels, hatName);
}

/**
 * @param {string[][]} spriteSheet 
 * @param {string} hatId 
 * @returns {Layer}
 */
function buildHatItemLayer(spriteSheet, hatId) {
	if (hatId === HAT.NONE) {
		return new Layer([], TAG.DEFAULT);
	}
	const hatIndex = Object.values(HAT).indexOf(hatId) - 1;
	let hatPixels = getLayerPixels(spriteSheet, hatIndex, HAT_WIDTH);
	hatPixels = pad(hatPixels, 1, 1, 1, 1);
	hatPixels = drawOutline(hatPixels, true);
	hatPixels = pushToBottom(hatPixels);
	return new Layer(hatPixels, TAG.DEFAULT);
}

/**
 * Add transparent padding around the pixel array
 * @param {string[][]} pixels 
 * @param {number} top 
 * @param {number} bottom 
 * @param {number} left 
 * @param {number} right 
 * @returns {string[][]}
 */
function pad(pixels, top, bottom, left, right) {
	const paddedPixels = [];
	const rowLength = pixels[0].length + left + right;
	// Top padding
	for (let y = 0; y < top; y++) {
		paddedPixels.push(Array(rowLength).fill(PALETTE.TRANSPARENT));
	}
	// Left and right padding
	for (let y = 0; y < pixels.length; y++) {
		const row = [];
		for (let x = 0; x < left; x++) {
			row.push(PALETTE.TRANSPARENT);
		}
		for (let x = 0; x < pixels[y].length; x++) {
			row.push(pixels[y][x]);
		}
		for (let x = 0; x < right; x++) {
			row.push(PALETTE.TRANSPARENT);
		}
		paddedPixels.push(row);
	}
	// Bottom padding
	for (let y = 0; y < bottom; y++) {
		paddedPixels.push(Array(rowLength).fill(PALETTE.TRANSPARENT));
	}
	return paddedPixels;
}

/**
 * Draw an outline around non-transparent pixels
 * @param {string[][]} pixels 
 * @param {boolean} [outlineBottom=false]
 * @return {string[][]}
 */
function drawOutline(pixels, outlineBottom = false) {
	let neighborOffsets = [
		[-1, 0],
		[1, 0],
		[0, -1],
		[-1, -1],
		[1, -1],
	];
	if (outlineBottom) {
		neighborOffsets.push([0, 1], [-1, 1], [1, 1]);
	}
	for (let y = 0; y < pixels.length; y++) {
		for (let x = 0; x < pixels[y].length; x++) {
			const pixel = pixels[y][x];
			if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) {
				for (let [dx, dy] of neighborOffsets) {
					const newX = x + dx;
					const newY = y + dy;
					if (newY >= 0 && newY < pixels.length && newX >= 0 && newX < pixels[newY].length && pixels[newY][newX] === PALETTE.TRANSPARENT) {
						pixels[newY][newX] = PALETTE.BORDER;
					}
				}
			}
		}
	}
	return pixels;
}

/**
 * Trim transparent rows from the bottom and push them to the top
 * @param {string[][]} pixels
 * @returns {string[][]}
 */
function pushToBottom(pixels) {
	let trimmedPixels = pixels.slice();
	let trimCount = 0;
	while (trimmedPixels.length > 1) {
		const firstRow = trimmedPixels[trimmedPixels.length - 1];
		if (firstRow.every(pixel => pixel === PALETTE.TRANSPARENT)) {
			trimmedPixels.pop();
			trimCount++;
		} else {
			break;
		}
	}
	trimmedPixels = pad(trimmedPixels, trimCount, 0, 0, 0);
	return trimmedPixels;
}