<script lang="ts">
	import { onMount, onDestroy } from "svelte";
	import { page } from "$app/stores";
	import {
		drawLandscape,
		type ParallaxState,
		type SceneType,
	} from "$lib/ts/parallax";

	interface SceneConfig {
		sceneType: SceneType;
		staticScene: boolean;
		progress?: number;
		scenes?: SceneType[];
	}

	const PATH_CONFIG: Record<string, SceneConfig> = {
		"/": { sceneType: "transition", staticScene: false },
		"/chat": { sceneType: "oilRig", staticScene: true },
		"/goal": { sceneType: "pollutedCity", staticScene: true },
		"/report": { sceneType: "deforestation", staticScene: true },
		"/catalogue": {
			sceneType: "transition",
			staticScene: false,
			scenes: ["eco", "ocean"],
		},
		"/news": { sceneType: "ocean", staticScene: true },
	};

	const DEFAULT_CONFIG: SceneConfig = {
		sceneType: "transition",
		staticScene: false,
	};

	interface Props {
		onProgressChange?: (progress: number) => void;
		staticScene?: boolean;
		sceneType?: SceneType;
		sceneProgress?: number;
		scenes?: SceneType[];
	}
	let {
		onProgressChange,
		staticScene,
		sceneType,
		sceneProgress,
		scenes,
	}: Props = $props();

	let scrollY = 0;
	let innerHeight = 1;
	let scrollHeight = 1;
	let mouseX = 0;
	let mouseY = 0;
	let canvas: HTMLCanvasElement;
	let ctx: CanvasRenderingContext2D | null = null;
	let animationId: number = 0;
	let scrollContainer: HTMLElement | null = null;
	let currentPath = $state("/");
	let currentProgress = $state(0);

	let activeConfig = $derived(() => {
		const pathConfig = PATH_CONFIG[currentPath] || DEFAULT_CONFIG;
		return {
			sceneType: sceneType ?? pathConfig.sceneType,
			staticScene: staticScene ?? pathConfig.staticScene,
			progress: sceneProgress ?? pathConfig.progress ?? 0,
			scenes: scenes ?? pathConfig.scenes,
		};
	});

	interface SceneBlend {
		fromScene: SceneType;
		toScene: SceneType;
		blendProgress: number;
	}

	function getSceneBlend(
		scrollProgress: number,
		scenesList: SceneType[],
	): SceneBlend {
		if (scenesList.length === 0) {
			return { fromScene: "eco", toScene: "eco", blendProgress: 0 };
		}
		if (scenesList.length === 1) {
			return {
				fromScene: scenesList[0],
				toScene: scenesList[0],
				blendProgress: 0,
			};
		}

		const numTransitions = scenesList.length - 1;
		const segmentSize = 1 / numTransitions;
		const segmentIndex = Math.min(
			Math.floor(scrollProgress / segmentSize),
			numTransitions - 1,
		);

		const segmentProgress =
			(scrollProgress - segmentIndex * segmentSize) / segmentSize;

		return {
			fromScene: scenesList[segmentIndex],
			toScene:
				scenesList[Math.min(segmentIndex + 1, scenesList.length - 1)],
			blendProgress: Math.min(Math.max(segmentProgress, 0), 1),
		};
	}

	function calculateProgress(): number {
		const config = activeConfig();

		if (config.staticScene && !config.scenes) {
			return Math.min(Math.max(config.progress, 0), 1);
		}

		const scrollableHeight = scrollHeight - innerHeight;
		if (scrollableHeight <= 0) return config.progress;
		return Math.min(Math.max(scrollY / scrollableHeight, 0), 1);
	}

	function updateMeasurements() {
		const config = activeConfig();
		if (config.staticScene && !config.scenes) return;

		if (scrollContainer) {
			scrollY = scrollContainer.scrollTop;
			innerHeight = scrollContainer.clientHeight;
			scrollHeight = scrollContainer.scrollHeight;
		} else {
			scrollY = window.scrollY;
			innerHeight = window.innerHeight;
			scrollHeight = Math.max(
				document.body.scrollHeight,
				document.documentElement.scrollHeight,
			);
		}
	}

	function animate() {
		if (!ctx || !canvas) return;

		updateMeasurements();

		const rect = canvas.getBoundingClientRect();
		const dpr = window.devicePixelRatio || 1;

		const canvasWidth = Math.floor(rect.width * dpr);
		const canvasHeight = Math.floor(rect.height * dpr);

		if (canvas.width !== canvasWidth || canvas.height !== canvasHeight) {
			canvas.width = canvasWidth;
			canvas.height = canvasHeight;
			ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
		}

		const progress = calculateProgress();
		const config = activeConfig();

		if (currentProgress !== progress) {
			currentProgress = progress;
			onProgressChange?.(progress);
		}

		let sceneBlend: SceneBlend | undefined;
		if (config.scenes && config.scenes.length > 1) {
			sceneBlend = getSceneBlend(progress, config.scenes);
		}

		const state: ParallaxState = {
			scrollY: config.staticScene && !config.scenes ? 0 : scrollY,
			innerHeight,
			mouseX,
			mouseY,
			progress: sceneBlend ? sceneBlend.blendProgress : progress,
			sceneType: sceneBlend ? sceneBlend.fromScene : config.sceneType,
			blendToScene: sceneBlend?.toScene,
			blendProgress: sceneBlend?.blendProgress,
		};

		drawLandscape(ctx, rect.width, rect.height, state);
		animationId = requestAnimationFrame(animate);
	}

	onMount(() => {
		const unsubscribe = page.subscribe((p) => {
			currentPath = p.url.pathname;
		});

		const handleMouseMove = (e: MouseEvent) => {
			mouseX = (e.clientX - window.innerWidth / 2) * 0.05;
			mouseY = (e.clientY - window.innerHeight / 2) * 0.05;
		};

		window.addEventListener("mousemove", handleMouseMove, {
			passive: true,
		});

		setTimeout(() => {
			const config = activeConfig();

			if (!config.staticScene || config.scenes) {
				
				scrollContainer = null;
				updateMeasurements();
			}

			if (canvas) {
				ctx = canvas.getContext("2d");
				if (ctx) {
					animate();
				}
			}
		}, 100);

		return () => {
			unsubscribe();
			window.removeEventListener("mousemove", handleMouseMove);
			if (animationId) {
				cancelAnimationFrame(animationId);
			}
		};
	});

	onDestroy(() => {
		if (animationId) {
			cancelAnimationFrame(animationId);
		}
	});
</script>

<div class="parallax-landscape">
	<canvas bind:this={canvas} class="parallax-canvas"></canvas>
</div>

<style>
	.parallax-landscape {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		width: 100%;
		height: 100vh;
		overflow: hidden;
		pointer-events: none;
		z-index: 0;
	}

	.parallax-canvas {
		width: 100%;
		height: 100%;
		display: block;
	}
</style>
