<script lang="ts">
	import { statusStore } from '$lib/Types.svelte';
	import FlowArea from '../../../components/FlowArea.svelte';
	import type { PrevType } from './types';
	import { type EventsStat } from '../utils';
	import CoolGraphNode from './CoolGraphNode.svelte';
	import type { Node } from '../../flow/types';

	let {
		events
	}: {
		events: EventsStat[];
	} = $props();

	// Remove view events
	const internalEvs = $derived(events.filter((a) => a.t !== 2).toSorted((a, b) => a.c - b.c));

	let depth = $state(0);

	let applicationsTimelines = $derived.by(() => {
		let appState: Record<string, EventsStat[]> = {};
		for (const ev of internalEvs) {
			if (appState[ev.a] !== undefined) {
				if (ev.t == 0) continue;
				appState[ev.a].push(ev);
			} else {
				if (ev.t == 1) continue;
				appState[ev.a] = [ev];
			}
		}
		return Object.values(appState);
	});

	type Pos = {
		x: number;
		y: number;
		count: number;
		from: PrevType;
	};

	const posByDepth = $derived.by(() => {
		if (applicationsTimelines.length === 0) return [];

		const levels: Record<string, Pos>[] = [];

		let timelines = [...applicationsTimelines];
		while (timelines.length > 0) {
			const s: Record<string, Pos> = {};

			const nextTurnTimeLines: typeof timelines = [];

			const thisX = levels.length;

			// Deepclone prev
			if (thisX > 0) {
				for (const e of Object.keys(levels[thisX - 1])) {
					s[e] = {
						...levels[thisX - 1][e],
						from: {
							prev: levels[thisX - 1][e].from,
							cur: {}
						}
					};
				}
			}

			const dependencies: Record<string, string[]> = {};

			const thisLine = new Set<string>();

			for (const t of timelines) {
				const cur = t[thisX];
				// Only allow create as the first event of the timeline
				if ((cur.t === 0 && thisX !== 0) || (cur.t === 1 && thisX === 0)) {
					continue;
				}

				const st = cur.s ?? 'null';
				thisLine.add(st);

				let prev: string | undefined = undefined;
				if (thisX > 0) {
					prev = t[thisX - 1].s ?? 'null';
					if (dependencies[st]) {
						dependencies[st].push(prev);
					} else {
						dependencies[st] = [prev];
					}
				}

				if (s[st]) {
					s[st].count += 1;
					s[st].x = thisX;
					if (prev) {
						if (s[st].from.cur[prev]) {
							s[st].from.cur[prev] += 1;
						} else {
							s[st].from.cur[prev] = 1;
						}
					}
				} else {
					s[st] = {
						x: thisX,
						y: 0,
						from: { cur: {} },
						count: 1
					};
					if (prev) s[st].from.cur[prev] = 1;
				}

				if (t.length - 1 !== thisX) {
					nextTurnTimeLines.push(t);
				}
			}

			outer: for (let i = 0; i < thisX + 1; i++) {
				for (const v of Object.values(s)) {
					if (v.x === i) {
						continue outer;
					}
				}

				for (const v of Object.values(s)) {
					if (v.x > i) {
						v.x -= 1;
					}
				}
			}

			const max = Object.values(s).reduce((acc, a) => Math.max(acc, a.x), 0);

			for (const tName of Object.keys(s)) {
				const t = s[tName];
				if (t.x != max) continue;
				const thisLineDeps = dependencies[tName]?.filter((d) => thisLine.has(d)) ?? [];
				if (thisLineDeps.length == 0) continue;
				// If no cross dependencies
				if (thisLineDeps.every((d) => !dependencies[d].includes(tName))) {
					t.x += 1;
					thisLine.delete(tName);
				}
			}

			// TODO look for deps in the same line

			const findY: Record<number, number> = {};
			for (const target of Object.keys(s)) {
				if (findY[s[target].x] === undefined) {
					s[target].y = 0;
					findY[s[target].x] = 1;
				} else {
					s[target].y = findY[s[target].x];
					findY[s[target].x] += 1;
				}
			}

			levels.push(s);

			timelines = nextTurnTimeLines;
		}
		return levels;
	});

	let hovering: Node | undefined = $state();
</script>

{#if statusStore.nodesR && posByDepth.length > 0}
	<div>
		<div class="flex">
			<span>{depth}</span>
			<input
				class="flex-grow"
				min="0"
				max={posByDepth.length - 1}
				type="range"
				step="1"
				bind:value={depth}
			/>
		</div>
		<div class="flex flex-col h-[1200px]">
			<FlowArea grid_size={25 / 2} grid_scale_size={2}>
				{#snippet children(canvasX, canvasY)}
					{#each Object.keys(posByDepth[depth]) as nodeId}
						{@const cur = posByDepth[depth][nodeId]}
						{@const node = statusStore.nodesR[nodeId]}
						{#if node}
							<CoolGraphNode
								bind:hovering
								{canvasX}
								{canvasY}
								{node}
								from={cur.from}
								count={cur.count}
							/>
						{/if}
					{/each}
				{/snippet}
			</FlowArea>
		</div>
	</div>
{/if}
