<script context="module" lang="ts">
	import { writable } from "svelte/store";
	import { mount_css } from "@gradio/core";

	import type { Client as ClientType } from "@gradio/client";

	import type { ComponentMeta, Dependency, LayoutNode } from "@gradio/core";
	declare let GRADIO_VERSION: string;

	declare let BUILD_MODE: string;
	interface Config {
		auth_required?: true;
		auth_message: string;
		components: ComponentMeta[];
		css: string | null;
		js: string | null;
		head: string | null;
		dependencies: Dependency[];
		dev_mode: boolean;
		enable_queue: boolean;
		layout: LayoutNode;
		mode: "blocks" | "interface";
		root: string;
		theme: string;
		title: string;
		version: string;
		space_id: string | null;
		is_colab: boolean;
		show_api: boolean;
		stylesheets?: string[];
		path: string;
		app_id?: string;
		fill_height?: boolean;
		fill_width?: boolean;
		theme_hash?: number;
		username: string | null;
	}

	let id = -1;

	function create_intersection_store(): {
		register: (n: number, el: HTMLDivElement) => void;
		subscribe: (typeof intersecting)["subscribe"];
	} {
		const intersecting = writable<Record<string, boolean>>({});

		const els = new Map<HTMLDivElement, number>();

		const observer = new IntersectionObserver((entries) => {
			entries.forEach((entry) => {
				if (entry.isIntersecting) {
					let _el: number | undefined = els.get(entry.target as HTMLDivElement);
					if (_el !== undefined)
						intersecting.update((s) => ({ ...s, [_el as number]: true }));
				}
			});
		});

		function register(_id: number, el: HTMLDivElement): void {
			els.set(el, _id);
			observer.observe(el);
		}

		return { register, subscribe: intersecting.subscribe };
	}
</script>

<script lang="ts">
	import { onMount, createEventDispatcher, onDestroy } from "svelte";
	import type { SpaceStatus } from "@gradio/client";
	import { Embed } from "@gradio/core";
	import type { ThemeMode } from "@gradio/core";
	import { StatusTracker } from "@gradio/statustracker";
	import { _ } from "svelte-i18n";
	import { setupi18n } from "@gradio/core";
	import { Client } from "@gradio/client";
	import { page } from "$app/stores";

	import { init } from "@huggingface/space-header";
	import { browser } from "$app/environment";

	setupi18n();

	const dispatch = createEventDispatcher();
	export let data;

	export let autoscroll = false;
	export let version = GRADIO_VERSION;
	export let initial_height: string;
	export let app_mode = true;
	export let is_embed = false;
	export let theme_mode: ThemeMode | null = "system";
	export let control_page_title = true;
	export let container: boolean;
	let stream: EventSource;

	// These utilities are exported to be injectable for the Wasm version.

	// export let Client: typeof ClientType;

	export let space: string | null;
	// export let host: string | null;
	// export let src: string | null;

	let _id = id++;

	let loader_status: "pending" | "error" | "complete" | "generating" =
		"complete";
	let app_id: string | null = null;
	let wrapper: HTMLDivElement;
	let ready = false;
	let render_complete = false;
	$: config = data.config;
	let loading_text = $_("common.loading") + "...";
	let active_theme_mode: ThemeMode;
	let intersecting: ReturnType<typeof create_intersection_store> = {
		register: () => {},
		subscribe: writable({}).subscribe
	};

	$: if (config?.app_id) {
		app_id = config.app_id;
	}

	let status: SpaceStatus = {
		message: "",
		load_status: "pending",
		status: "sleeping",
		detail: "SLEEPING"
	};

	let app: ClientType = data.app;
	let css_ready = false;
	function handle_status(_status: SpaceStatus): void {
		status = _status;
	}
	//@ts-ignore

	let gradio_dev_mode = "";

	onMount(async () => {
		// active_theme_mode = handle_theme_mode(wrapper);

		//@ts-ignore
		config = data.config;
		window.gradio_config = config;
		window.gradio_config = data.config;
		config = data.config;

		if (!app.config) {
			throw new Error("Could not resolve app config");
		}

		window.__gradio_space__ = config.space_id;
		gradio_dev_mode = window?.__GRADIO_DEV__;

		status = {
			message: "",
			load_status: "complete",
			status: "running",
			detail: "RUNNING"
		};

		css_ready = true;
		window.__is_colab__ = config.is_colab;

		dispatch("loaded");

		if (config.dev_mode) {
			setTimeout(() => {
				const { host } = new URL(data.api_url);
				let url = new URL(`http://${host}${app.api_prefix}/dev/reload`);
				stream = new EventSource(url);
				stream.addEventListener("error", async (e) => {
					new_message_fn("Error reloading app", "error");
					// @ts-ignore
					console.error(JSON.parse(e.data));
				});
				stream.addEventListener("reload", async (event) => {
					app.close();
					app = await Client.connect(data.api_url, {
						status_callback: handle_status,
						with_null_state: true,
						events: ["data", "log", "status", "render"]
					});

					if (!app.config) {
						throw new Error("Could not resolve app config");
					}

					config = app.config;
					window.__gradio_space__ = config.space_id;
				});
			}, 200);
		}
	});

	let new_message_fn: (message: string, type: string) => void;

	onMount(async () => {
		intersecting = create_intersection_store();
		intersecting.register(_id, wrapper);
	});

	$: if (render_complete) {
		wrapper.dispatchEvent(
			new CustomEvent("render", {
				bubbles: true,
				cancelable: false,
				composed: true
			})
		);
	}

	$: app?.config &&
		browser &&
		mount_space_header(app?.config?.space_id, is_embed);
	let spaceheader: HTMLElement | undefined;

	async function mount_space_header(
		space_id: string | null | undefined,
		is_embed: boolean
	): Promise<void> {
		if (space_id && !is_embed && window.self === window.top) {
			if (spaceheader) {
				spaceheader.remove();
				spaceheader = undefined;
			}
			const header = await init(space_id);
			if (header) spaceheader = header.element;
		}
	}

	onDestroy(() => {
		spaceheader?.remove();
	});

	// function handle_theme_mode(target: HTMLDivElement): "light" | "dark" {
	// 	let new_theme_mode: ThemeMode;

	// 	const url = new URL(window.location.toString());
	// 	const url_color_mode: ThemeMode | null = url.searchParams.get(
	// 		"__theme"
	// 	) as ThemeMode | null;
	// 	new_theme_mode = theme_mode || url_color_mode || "system";

	// 	if (new_theme_mode === "dark" || new_theme_mode === "light") {
	// 		apply_theme(target, new_theme_mode);
	// 	} else {
	// 		new_theme_mode = sync_system_theme(target);
	// 	}
	// 	return new_theme_mode;
	// }

	// function sync_system_theme(target: HTMLDivElement): "light" | "dark" {
	// 	const theme = update_scheme();
	// 	window
	// 		?.matchMedia("(prefers-color-scheme: dark)")
	// 		?.addEventListener("change", update_scheme);

	// 	function update_scheme(): "light" | "dark" {
	// 		let _theme: "light" | "dark" = window?.matchMedia?.(
	// 			"(prefers-color-scheme: dark)"
	// 		).matches
	// 			? "dark"
	// 			: "light";

	// 		apply_theme(target, _theme);
	// 		return _theme;
	// 	}
	// 	return theme;
	// }

	// function apply_theme(target: HTMLDivElement, theme: "dark" | "light"): void {
	// 	const dark_class_element = is_embed ? target.parentElement! : document.body;
	// 	const bg_element = is_embed ? target : target.parentElement!;
	// 	bg_element.style.background = "var(--body-background-fill)";
	// 	if (theme === "dark") {
	// 		dark_class_element.classList.add("dark");
	// 	} else {
	// 		dark_class_element.classList.remove("dark");
	// 	}
	// }
</script>

<svelte:head>
	<link rel="stylesheet" href={"./theme.css?v=" + config?.theme_hash} />

	{#if config.head}
		{@html config.head}
	{/if}
</svelte:head>

<Embed
	display={container && is_embed}
	{is_embed}
	info={false}
	{version}
	{initial_height}
	{space}
	loaded={loader_status === "complete"}
	fill_width={config?.fill_width || false}
	bind:wrapper
>
	{#if config?.auth_required}
		<svelte:component
			this={data.Render}
			auth_message={config.auth_message}
			root={config.root}
			space_id={space}
			{app_mode}
		/>
	{:else if config && app}
		<svelte:component
			this={data.Render}
			{app}
			{...config}
			fill_height={!is_embed && config.fill_height}
			theme_mode={active_theme_mode}
			{control_page_title}
			target={wrapper}
			{autoscroll}
			bind:ready
			bind:render_complete
			bind:add_new_message={new_message_fn}
			show_footer={!is_embed}
			{app_mode}
			{version}
			search_params={$page.url.searchParams}
			initial_layout={data.layout}
		/>
	{/if}
</Embed>

<!-- <style>
	.error {
		position: relative;
		padding: var(--size-4);
		color: var(--body-text-color);
		text-align: center;
	}

	.error > * {
		margin-top: var(--size-4);
	}

	a {
		color: var(--link-text-color);
	}

	a:hover {
		color: var(--link-text-color-hover);
		text-decoration: underline;
	}

	a:visited {
		color: var(--link-text-color-visited);
	}

	a:active {
		color: var(--link-text-color-active);
	}
</style> -->
