<script lang="ts">
	// @ts-nocheck
	import { createEventDispatcher, tick, onMount } from "svelte";
	import { BlockLabel } from "@gradio/atoms";
	import { Image, Sketch as SketchIcon } from "@gradio/icons";
	import type { SelectData, I18nFormatter } from "@gradio/utils";
	import { get_coordinates_of_clicked_image } from "./utils";

	import Cropper from "./Cropper.svelte";
	import Sketch from "./Sketch.svelte";
	import Webcam from "./Webcam.svelte";
	import ModifySketch from "./ModifySketch.svelte";
	import SketchSettings from "./SketchSettings.svelte";
	import {
		Upload,
		ModifyUpload,
		type FileData,
		normalise_file
	} from "@gradio/upload";

	export let value:
		| null
		| { image: string | null; mask: string | null }
		| FileData;
	export let label: string | undefined = undefined;
	export let show_label: boolean;

	export let source: "canvas" | "webcam" | "upload" = "upload";
	export let tool: "editor" | "select" | "sketch" | "color-sketch" = "editor";
	export let shape: [number, number];
	export let streaming = false;
	export let pending = false;
	export let mirror_webcam: boolean;
	export let brush_radius: number;
	export let brush_color = "#000000";
	export let mask_opacity;
	export let selectable = false;
	export let root: string;
	export let i18n: I18nFormatter;

	let sketch: Sketch;
	let cropper: Cropper;

	if (
		value &&
		(source === "upload" || source === "webcam") &&
		tool === "sketch"
	) {
		value = { image: value as string, mask: null };
	}

	function handle_upload({ detail }: CustomEvent<string>): void {
		if (tool === "color-sketch") {
			static_image = normalise_file(detail, root, null)?.data;
		} else {
			value =
				(source === "upload" || source === "webcam") && tool === "sketch"
					? { image: normalise_file(detail, root, null), mask: null }
					: normalise_file(detail, root, null);
		}

		dispatch("upload", normalise_file(detail, root, null));
	}

	function handle_clear({ detail }: CustomEvent<null>): void {
		value = null;
		static_image = undefined;
		dispatch("clear");
	}

	async function handle_save(
		{ detail }: { detail: string },
		initial
	): Promise<void> {
		if (mode === "mask") {
			if (source === "webcam" && initial) {
				value = {
					image: detail,
					mask: null
				};
			} else {
				value = {
					image: typeof value === "string" ? value : value?.image || null,
					mask: detail
				};
			}
		} else if (
			(source === "upload" || source === "webcam") &&
			tool === "sketch"
		) {
			value = { image: detail, mask: null };
		} else {
			value = detail;
		}

		await tick();

		dispatch(streaming ? "stream" : "edit");
	}

	const dispatch = createEventDispatcher<{
		change: string | null;
		stream: string | null;
		edit: undefined;
		clear: undefined;
		drag: boolean;
		upload: FileData;
		select: SelectData;
	}>();

	let dragging = false;

	$: dispatch("drag", dragging);

	let value_: null | FileData = null;

	$: if (value !== value_) {
		value_ = value;
		normalise_file(value_, root, null);
	}

	function handle_image_load(event: Event): void {
		const element = event.currentTarget as HTMLImageElement;
		img_width = element.naturalWidth;
		img_height = element.naturalHeight;
		container_height = element.getBoundingClientRect().height;
	}

	async function handle_sketch_clear(): Promise<void> {
		dispatch("clear");
		sketch.clear();
		await tick();
		value = null;
		static_image = undefined;
	}

	async function handle_mask_clear(): Promise<void> {
		sketch.clear_mask();
		await tick();
	}

	let img_height = 0;
	let img_width = 0;
	let container_height = 0;

	let mode;

	$: {
		if (source === "canvas" && tool === "sketch") {
			mode = "bw-sketch";
		} else if (tool === "color-sketch") {
			mode = "color-sketch";
		} else if (
			(source === "upload" || source === "webcam") &&
			tool === "sketch"
		) {
			mode = "mask";
		} else {
			mode = "editor";
		}
	}
	let value_img;
	let max_height;
	let max_width;

	let static_image = undefined;

	$: {
		if (value === null || (value.image === null && value.mask === null)) {
			static_image = undefined;
		}
	}

	$: {
		if (cropper) {
			if (value) {
				cropper.image = value;
				cropper.create();
			} else {
				cropper.destroy();
			}
		}
	}

	onMount(async () => {
		if (tool === "color-sketch" && value && typeof value === "string") {
			static_image = value;
			await tick();
			handle_image_load({ currentTarget: value_img });
		}
	});

	function handle_click(evt: MouseEvent): void {
		let coordinates = get_coordinates_of_clicked_image(evt);
		if (coordinates) {
			dispatch("select", { index: coordinates, value: null });
		}
	}
</script>

<BlockLabel
	{show_label}
	Icon={source === "canvas" ? SketchIcon : Image}
	label={label || (source === "canvas" ? "Sketch" : "Image")}
/>

<div
	data-testid="image"
	class="image-container"
	bind:offsetHeight={max_height}
	bind:offsetWidth={max_width}
>
	{#if source === "upload"}
		<Upload
			bind:dragging
			filetype="image/*"
			on:load={handle_upload}
			include_file_metadata={false}
			disable_click={!!value}
			{root}
		>
			{#if (value === null && !static_image) || streaming}
				<slot />
			{:else if tool === "select"}
				<Cropper
					bind:this={cropper}
					image={value_.data}
					on:crop={handle_save}
				/>
				<ModifyUpload
					{i18n}
					on:clear={(e) => (handle_clear(e), (tool = "editor"))}
				/>
			{:else if tool === "editor"}
				<ModifyUpload
					{i18n}
					on:edit={() => (tool = "select")}
					on:clear={handle_clear}
					editable
				/>

				<!-- TODO: fix-->
				<!-- svelte-ignore a11y-click-events-have-key-events -->
				<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
				<img
					src={value_.data}
					alt=""
					class:scale-x-[-1]={source === "webcam" && mirror_webcam}
					class:selectable
					on:click={handle_click}
					loading="lazy"
				/>
			{:else if (tool === "sketch" || tool === "color-sketch") && (value !== null || static_image)}
				{#key static_image}
					<img
						bind:this={value_img}
						class="absolute-img"
						src={static_image || value?.image?.data || value?.data}
						alt=""
						on:load={handle_image_load}
						class:webcam={source === "webcam" && mirror_webcam}
						loading="lazy"
						crossorigin="anonymous"
					/>
				{/key}
				{#if img_width > 0}
					<Sketch
						{value}
						bind:this={sketch}
						bind:brush_radius
						bind:brush_color
						{mask_opacity}
						on:change={handle_save}
						{mode}
						width={img_width || max_width}
						height={img_height || max_height}
						container_height={container_height || max_height}
						{value_img}
						{source}
						{shape}
					/>
					<ModifySketch
						show_eraser={value_img}
						on:undo={() => sketch.undo()}
						on:clear_mask={handle_mask_clear}
						on:remove_image={handle_sketch_clear}
					/>
					{#if tool === "color-sketch" || tool === "sketch"}
						<SketchSettings
							bind:brush_radius
							bind:brush_color
							container_height={container_height || max_height}
							img_width={img_width || max_width}
							img_height={img_height || max_height}
							{mode}
						/>
					{/if}
				{/if}
			{:else}
				<!-- TODO: fix-->
				<!-- svelte-ignore a11y-click-events-have-key-events -->
				<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
				<img
					src={value.image || value}
					alt=""
					class:webcam={source === "webcam" && mirror_webcam}
					class:selectable
					on:click={handle_click}
					loading="lazy"
				/>
			{/if}
		</Upload>
	{:else if source === "canvas"}
		<ModifySketch
			on:undo={() => sketch.undo()}
			on:remove_image={handle_sketch_clear}
		/>
		{#if tool === "color-sketch"}
			<SketchSettings
				bind:brush_radius
				bind:brush_color
				container_height={container_height || max_height}
				img_width={img_width || max_width}
				img_height={img_height || max_height}
			/>
		{/if}
		<Sketch
			{value}
			bind:brush_radius
			bind:brush_color
			bind:this={sketch}
			on:change={handle_save}
			on:clear={handle_sketch_clear}
			{mode}
			width={img_width || max_width}
			height={img_height || max_height}
			container_height={container_height || max_height}
			{shape}
		/>
	{:else if (value === null && !static_image) || streaming}
		{#if source === "webcam" && !static_image}
			<Webcam
				on:capture={(e) =>
					tool === "color-sketch" ? handle_upload(e) : handle_save(e, true)}
				on:stream={handle_save}
				on:error
				{streaming}
				{pending}
				{mirror_webcam}
			/>
		{/if}
	{:else if tool === "select"}
		<Cropper bind:this={cropper} image={value.data} on:crop={handle_save} />
		<ModifyUpload
			{i18n}
			on:clear={(e) => (handle_clear(e), (tool = "editor"))}
		/>
	{:else if tool === "editor"}
		<ModifyUpload
			{i18n}
			on:edit={() => (tool = "select")}
			on:clear={handle_clear}
			editable
		/>

		<!-- TODO: fix -->
		<!-- svelte-ignore a11y-click-events-have-key-events -->
		<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
		<img
			src={value_}
			alt=""
			class:selectable
			class:webcam={source === "webcam" && mirror_webcam}
			on:click={handle_click}
			loading="lazy"
		/>
	{:else if (tool === "sketch" || tool === "color-sketch") && (value !== null || static_image)}
		{#key static_image}
			<img
				bind:this={value_img}
				class="absolute-img"
				src={static_image || value_}
				alt=""
				on:load={handle_image_load}
				class:webcam={source === "webcam" && mirror_webcam}
				loading="lazy"
			/>
		{/key}
		{#if img_width > 0}
			<Sketch
				{value}
				bind:this={sketch}
				bind:brush_radius
				bind:brush_color
				on:change={handle_save}
				{mode}
				width={img_width || max_width}
				height={img_height || max_height}
				container_height={container_height || max_height}
				{value_img}
				{source}
			/>
			<ModifySketch
				on:undo={() => sketch.undo()}
				on:remove_image={handle_sketch_clear}
			/>
			{#if tool === "color-sketch" || tool === "sketch"}
				<SketchSettings
					bind:brush_radius
					bind:brush_color
					container_height={container_height || max_height}
					img_width={img_width || max_width}
					img_height={img_height || max_height}
					{mode}
				/>
			{/if}
		{/if}
	{:else}
		<!-- TODO: fix -->
		<!-- svelte-ignore a11y-click-events-have-key-events -->
		<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->

		<img
			src={value_.image || value_.data}
			alt=""
			class:webcam={source === "webcam" && mirror_webcam}
			class:selectable
			on:click={handle_click}
			loading="lazy"
		/>
	{/if}
</div>

<style>
	.image-container,
	img {
		width: var(--size-full);
		height: var(--size-full);
	}
	img {
		object-fit: contain;
	}

	.selectable {
		cursor: crosshair;
	}

	.absolute-img {
		position: absolute;
		opacity: 0;
	}

	.webcam {
		transform: scaleX(-1);
	}
</style>
