<script lang="ts">
	import { get_shifted_color } from '$lib/ts/stores/ui_behavior';
	import type { MenuOption } from '$lib/ts/types';
	import { fade } from 'svelte/transition';

	let {
		className = '',
		bg = 'bg-stone-700',
		hover_bg = get_shifted_color(bg, 100),
		active_bg = get_shifted_color(bg, 200),
		disabled = false,
		title = '',
		click_function = () => {},
		menu_options = [],
		menu_class = 'right-0',
		div_class = '',
		children
	}: {
		className?: string;
		bg?: string;
		hover_bg?: string;
		active_bg?: string;
		disabled?: boolean;
		title?: string;
		click_function?: (e: MouseEvent) => void;
		menu_options?: MenuOption[];
		menu_class?: string;
		div_class?: string;
		children?: any;
	} = $props();

	let menu_shown = $state(false);
	let button_element: HTMLButtonElement;
	let menu_element: HTMLDivElement | null = $state(null);
	let position_bottom = $state(true);

	function onclick(e: MouseEvent) {
		if (menu_options.length !== 0) {
			if (menu_shown) {
				close_menu();
			} else {
				open_menu();
			}
		}
		click_function(e);
	}

	function getPolygon(): [number, number][] | null {
		if (!button_element || !menu_element) return null;
		const b = button_element.getBoundingClientRect();
		const m = menu_element.getBoundingClientRect();

		// get polygon coords
		if (position_bottom) {
			return [
				[b.left, b.top],
				[b.right, b.top],
				[m.right, m.top],
				[m.right, m.bottom],
				[m.left, m.bottom],
				[m.left, m.top],
				[b.left, b.top]
			];
		} else {
			return [
				[b.left, b.bottom],
				[b.right, b.bottom],
				[m.right, m.bottom],
				[m.right, m.top],
				[m.left, m.top],
				[m.left, m.bottom],
				[b.left, b.bottom]
			];
		}
	}

	function handleMouseMove(e: MouseEvent) {
		if (!menu_shown) return;
		const polygon = getPolygon();
		if (!polygon) return;

		const inside = pointInPolygon([e.clientX, e.clientY], polygon);
		if (!inside) {
			close_menu();
		}
	}

	function pointInPolygon(point: [number, number], polygon: [number, number][]) {
		let [x, y] = point;
		let inside = false;
		for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
			let [xi, yi] = polygon[i];
			let [xj, yj] = polygon[j];
			let intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
			if (intersect) inside = !inside;
		}
		return inside;
	}

	function is_enough_space_below(): boolean {
		if (!button_element || !menu_element) return true;
		const button_rectangle = button_element.getBoundingClientRect();
		const menu_height = menu_element.offsetHeight;
		const space_below_available = window.innerHeight - button_rectangle.bottom;

		return space_below_available > menu_height;
	}

	function open_menu() {
		menu_shown = true;
		window.addEventListener('mousemove', handleMouseMove);
		setTimeout(() => {
			position_bottom = is_enough_space_below();
		}, 0);
	}

	function close_menu() {
		window.removeEventListener('mousemove', handleMouseMove);
		menu_shown = false;
	}

	function no_option_has_icon(): boolean {
		for (const option of menu_options) {
			if (option.icon) {
				return false;
			}
		}
		return true;
	}
</script>

<div class="relative min-w-0 flex {div_class}" {title}>
	<button
		bind:this={button_element}
		class="{className} {menu_shown ? hover_bg : bg} {disabled
			? 'text-stone-500 cursor-not-allowed'
			: 'hover:' +
				hover_bg +
				' active:' +
				active_bg +
				' cursor-pointer'} p-2 rounded-xl flex justify-center items-center transition-colors duration-200"
		{disabled}
		{onclick}
	>
		{@render children()}
	</button>

	{#if menu_shown}
		<!-- svelte-ignore a11y_no_static_element_interactions -->
		<!-- svelte-ignore a11y_click_events_have_key_events -->
		<div
			bind:this={menu_element}
			transition:fade={{ duration: 50 }}
			class="absolute {position_bottom
				? 'top-full'
				: 'bottom-full'} {menu_class} z-100 my-1.5 min-w-64 rounded-xl backdrop-blur bg-stone-800/45 border border-stone-400/10 shadow-xl/20 p-2 flex flex-col gap-2 text-stone-200 cursor-auto"
			onclick={(e) => {
				e.stopPropagation();
			}}
		>
			{#each menu_options as option}
				<button
					disabled={option.disabled ?? false}
					class="bg-white/15 {option.disabled
						? 'text-stone-500 cursor-not-allowed'
						: 'hover:bg-white/35 active:bg-white/60 cursor-pointer ' +
							option.class} rounded-lg p-2 transition-colors duration-200 select-none flex flex-row gap-2 items-center"
					onclick={async (e) => {
						if (option.on_select) await option.on_select();
						close_menu();
					}}
				>
					{#if !no_option_has_icon()}
						<div class="aspect-square h-[1.2rem]">
							{#if option.icon}
								{@const Icon = option.icon}
								<Icon class="size-full" />
							{/if}
						</div>
					{/if}
					<div class="truncate min-w-0" title={option.name}>
						{option.name}
					</div>
				</button>
			{/each}
		</div>
	{/if}
</div>
