<script lang="ts">
	import Cursor from '$lib/Cursor.svelte';
	import Frame from '$lib/Frame.svelte';
	import PaintFrame from '$lib/PaintFrame.svelte';
	import PaintCanvas from '$lib/PaintCanvas.svelte';
	import Menu from '$lib/Menu.svelte';
	import PromptModal from '$lib/PromptModal.svelte';
	import { COLORS, FRAME_SIZE } from '$lib/constants';
	import { PUBLIC_WS_INPAINTING } from '$env/static/public';
	import type { PromptImgKey } from '$lib/types';
	import { Status } from '$lib/types';
	import { LiveObject } from '@liveblocks/client';

	import {
		loadingState,
		currZoomTransform,
		maskEl,
		selectedRoomID,
		isRenderingCanvas
	} from '$lib/store';
	import { useMyPresence, useObject, useOthers } from '$lib/liveblocks';

	const myPresence = useMyPresence();
	const others = useOthers();
	let showModal = false;
	function getKey(position: { x: number; y: number }): PromptImgKey {
		return `${position.x}_${position.y}`;
	}

	const promptImgStorage = useObject('promptImgStorage');

	$: isLoading = $myPresence?.status === Status.loading || $isRenderingCanvas || false;
	function onShowModal(e: CustomEvent) {
		if (isLoading) return;
		showModal = e.detail.showModal;
		if (showModal) {
			myPresence.update({
				status: Status.prompting
			});
		} else {
			myPresence.update({
				status: Status.ready
			});
		}
	}

	function onPaint() {
		showModal = false;
		generateImage();
	}
	function canPaint(position: { x: number; y: number }): boolean {
		if (!$others) return true;
		let canPaint = true;
		for (const { presence } of $others) {
			if (
				position.x < presence.frame.x + FRAME_SIZE &&
				position.x + FRAME_SIZE > presence.frame.x &&
				position.y < presence.frame.y + FRAME_SIZE &&
				position.y + FRAME_SIZE > presence.frame.y
			) {
				// can paint if presence is only  dragging
				if (presence.status === Status.ready || presence.status === Status.dragging) {
					canPaint = true;
					continue;
				}
				canPaint = false;
				break;
			}
		}
		return canPaint;
	}

	function clearStateMsg(t = 5000) {
		setTimeout(() => {
			$loadingState = '';
		}, t);
	}
	async function generateImage() {
		if (isLoading) return;
		const prompt = $myPresence.currentPrompt;
		const position = $myPresence.frame;
		$loadingState = 'Pending';
		if (!canPaint(position)) {
			$loadingState = 'Someone is already painting here';
			myPresence.update({
				status: Status.ready
			});
			clearStateMsg();
			return;
		}
		const imageKey = getKey(position);
		const room = $selectedRoomID || 'default';
		console.log('Generating...', prompt, position);
		myPresence.update({
			status: Status.loading
		});
		const sessionHash = crypto.randomUUID();
		const base64Crop = $maskEl.toDataURL('image/webp');

		const hashpayload = {
			fn_index: 0,
			session_hash: sessionHash
		};

		const datapayload = {
			data: [base64Crop, prompt, 0.75, 7.5, 25, 'patchmatch', room, imageKey]
		};

		const websocket = new WebSocket(PUBLIC_WS_INPAINTING);
		// websocket.onopen = async function (event) {
		// 	websocket.send(JSON.stringify({ hash: sessionHash }));
		// };
		websocket.onclose = (evt) => {
			if (!evt.wasClean) {
				$loadingState = 'Error';
				myPresence.update({
					status: Status.ready
				});
			}
		};
		websocket.onmessage = async function (event) {
			try {
				const data = JSON.parse(event.data);
				$loadingState = '';
				switch (data.msg) {
					case 'send_hash':
						websocket.send(JSON.stringify(hashpayload));
						break;
					case 'send_data':
						$loadingState = 'Sending Data';
						websocket.send(JSON.stringify({ ...hashpayload, ...datapayload }));
						break;
					case 'queue_full':
						$loadingState = 'Queue full';
						websocket.close();
						myPresence.update({
							status: Status.ready
						});
						return;
					case 'estimation':
						const { rank, queue_size } = data;
						$loadingState = `On queue ${rank}/${queue_size}`;
						break;
					case 'process_generating':
						$loadingState = data.success ? 'Generating' : 'Error';
						break;
					case 'process_completed':
						try {
							const params = data.output.data[0] as {
								is_nsfw: boolean;
								image: {
									url: string;
									filename: string;
								};
							};
							const isNSWF = params.is_nsfw;
							if (isNSWF) {
								throw new Error('NFSW');
							}
							// const imgBlob = await base64ToBlob(imgBase64);
							const promptImgParams = {
								imgURL: params.image.filename
							};
							// const imgURL = await uploadImage(imgBlob, promptImgParams);
							$promptImgStorage.set(imageKey, new LiveObject(promptImgParams));
							// $promptImgStorage.set(imageKey, promptImgParams);

							console.log(params.image.url);
							$loadingState = data.success ? 'Complete' : 'Error';
							clearStateMsg();
							myPresence.update({
								status: Status.ready,
								currentPrompt: ''
							});
						} catch (err) {
							const tError = err as Error;
							$loadingState = tError?.message;
							myPresence.update({
								status: Status.ready
							});
							clearStateMsg(10000);
						}
						websocket.close();
						return;
					case 'process_starts':
						$loadingState = 'Processing';
						break;
				}
			} catch (e) {
				console.error(e);
				$loadingState = 'Error';
			}
		};
	}
</script>

<!-- Show the current user's cursor location -->
<div class="text touch-none pointer-events-none">
	{$loadingState}
</div>
{#if showModal}
	<PromptModal
		on:paint={onPaint}
		initPrompt={$myPresence?.currentPrompt}
		on:showModal={onShowModal}
	/>
{/if}
<div class="fixed top-0 left-0 z-0 w-screen h-screen min-h-[600px]">
	<PaintCanvas />

	<main class="z-10 relative">
		<!-- When others connected, iterate through others and show their cursors -->
		{#if $others}
			{#each [...$others] as { connectionId, presence } (connectionId)}
				{#if (presence?.status === Status.loading || presence?.status === Status.prompting || presence?.status === Status.masking) && presence?.frame}
					<Frame
						status={presence.status}
						position={presence?.frame}
						prompt={presence?.currentPrompt}
						transform={$currZoomTransform}
					/>
				{/if}
				{#if presence?.cursor}
					<Cursor
						color={COLORS[1 + (connectionId % (COLORS.length - 1))]}
						position={presence?.cursor}
						transform={$currZoomTransform}
					/>
				{/if}
			{/each}
		{/if}
		<PaintFrame transform={$currZoomTransform} on:showModal={onShowModal} />
	</main>
</div>
<div class="fixed bottom-0 md:bottom-16 left-0 right-0 z-10 my-2">
	<Menu {isLoading} on:showModal={onShowModal} />
</div>

<style lang="postcss" scoped>
</style>
