<script lang="ts">
    import { onMount } from "svelte";
    import { Play, Pause, Maximize2, Minimize2 } from "@lucide/svelte";

    let canvasElement: HTMLCanvasElement | undefined = undefined;
    let videoContainer: HTMLDivElement | undefined = undefined;
    let isFullscreen = $state(false);
    let isPlaying = $state(false);
    let isLoading = $state(false);
    let error = $state<string | null>(null);
    let streamId = $state("camera-1");
    let rtspUrl = $state("rtsp://Kamera_1:kamera_1@192.168.175.49/stream1");
    let frameCount = $state(0);
    let fps = $state(0);
    let frameCounterStart = 0;
    let streamWidth = 0;
    let streamHeight = 0;

    let abortController: AbortController | null = null;

    async function startStream() {
        if (!streamId || !rtspUrl) {
            error = "Please enter both Stream ID and RTSP URL";
            return;
        }

        isLoading = true;
        error = null;
        frameCount = 0;
        fps = 0;

        try {
            const response = await fetch("/api/stream", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    action: "start",
                    streamId,
                    rtspUrl,
                }),
            });

            const data = await response.json();

            if (!response.ok || data.error) {
                throw new Error(data.error || "Failed to start stream");
            }

            // Wait for FFmpeg to start
            await new Promise((resolve) => setTimeout(resolve, 2000));

            // Start streaming MJPEG frames
            streamMJPEG();
            isPlaying = true;
        } catch (err) {
            error =
                err instanceof Error ? err.message : "Unknown error occurred";
            console.error("Error starting stream:", err);
        } finally {
            isLoading = false;
        }
    }

    async function streamMJPEG() {
        if (!canvasElement) return;

        abortController = new AbortController();
        const signal = abortController.signal;

        let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;

        try {
            const response = await fetch(`/api/stream/mjpeg?id=${streamId}`, {
                signal,
            });

            if (!response.ok) {
                throw new Error(
                    `Failed to connect to stream: ${response.status}`,
                );
            }

            if (!response.body) {
                throw new Error("Response body is empty");
            }

            reader = response.body.getReader();
            let buffer = new Uint8Array();
            frameCounterStart = Date.now();

            while (true) {
                const { done, value } = await reader.read();

                if (done) break;

                // Append new data to buffer
                const newBuffer = new Uint8Array(buffer.length + value.length);
                newBuffer.set(buffer);
                newBuffer.set(value, buffer.length);
                buffer = newBuffer;

                // Find JPEG frames (they start with FFD8 and end with FFD9)
                const jpegStart = findJPEGStart(buffer);
                if (jpegStart !== -1) {
                    const jpegEnd = findJPEGEnd(buffer, jpegStart);
                    if (jpegEnd !== -1) {
                        const jpegData = buffer.slice(jpegStart, jpegEnd + 2);
                        displayFrame(jpegData);
                        buffer = buffer.slice(jpegEnd + 2);

                        // Update FPS counter
                        frameCount++;
                        const now = Date.now();
                        if (now - frameCounterStart >= 1000) {
                            fps = frameCount;
                            frameCount = 0;
                            frameCounterStart = now;
                        }
                    }
                }

                // Prevent buffer from growing too large
                if (buffer.length > 5000000) {
                    buffer = buffer.slice(-1000000);
                }
            }
        } catch (err) {
            if ((err as Error).name !== "AbortError") {
                error = err instanceof Error ? err.message : "Stream error";
                console.error("Error streaming MJPEG:", err);
            }
        } finally {
            if (reader) {
                reader.releaseLock();
            }
        }
    }

    function findJPEGStart(buffer: Uint8Array): number {
        for (let i = 0; i < buffer.length - 1; i++) {
            if (buffer[i] === 0xff && buffer[i + 1] === 0xd8) {
                return i;
            }
        }
        return -1;
    }

    function findJPEGEnd(buffer: Uint8Array, start: number): number {
        for (let i = start + 2; i < buffer.length - 1; i++) {
            if (buffer[i] === 0xff && buffer[i + 1] === 0xd9) {
                return i + 1;
            }
        }
        return -1;
    }

    function displayFrame(jpegData: Uint8Array) {
        if (!canvasElement) return;

        const blob = new Blob([jpegData as BlobPart], { type: "image/jpeg" });
        const url = URL.createObjectURL(blob);
        const img = new Image();

        img.onload = () => {
            if (!canvasElement) return;

            // Store original stream dimensions
            if (streamWidth === 0 || streamHeight === 0) {
                streamWidth = img.width;
                streamHeight = img.height;
            }

            // Get container dimensions
            const container = canvasElement.parentElement;
            if (!container) return;

            const containerWidth = container.clientWidth;
            const containerHeight = container.clientHeight;

            // Calculate scale to fit container while maintaining aspect ratio
            const scaleW = containerWidth / streamWidth;
            const scaleH = containerHeight / streamHeight;
            const scale = Math.min(scaleW, scaleH);

            const displayWidth = Math.round(streamWidth * scale);
            const displayHeight = Math.round(streamHeight * scale);

            canvasElement.width = displayWidth;
            canvasElement.height = displayHeight;

            // Get fresh context after resizing canvas
            const ctx = canvasElement.getContext("2d");
            if (!ctx) {
                console.error("Failed to get canvas context");
                URL.revokeObjectURL(url);
                return;
            }

            ctx.drawImage(img, 0, 0, displayWidth, displayHeight);
            URL.revokeObjectURL(url);
        };

        img.onerror = () => {
            console.error("Failed to load JPEG frame");
            URL.revokeObjectURL(url);
        };

        img.src = url;
    }

    async function stopStream() {
        isLoading = true;

        try {
            if (abortController) {
                abortController.abort();
                abortController = null;
            }

            const response = await fetch("/api/stream", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    action: "stop",
                    streamId,
                }),
            });

            if (!response.ok) {
                throw new Error("Failed to stop stream");
            }

            isPlaying = false;
            error = null;
            frameCount = 0;
            fps = 0;
        } catch (err) {
            error =
                err instanceof Error ? err.message : "Failed to stop stream";
            console.error("Error stopping stream:", err);
        } finally {
            isLoading = false;
        }
    }

    function toggleFullscreen() {
        if (!videoContainer) return;

        if (!document.fullscreenElement) {
            videoContainer.requestFullscreen().catch((err) => {
                console.error(
                    `Error attempting to enable fullscreen: ${err.message}`,
                );
            });
        } else {
            document.exitFullscreen();
        }
    }

    function togglePlayPause() {
        if (isPlaying) {
            stopStream();
        } else {
            startStream();
        }
    }

    onMount(() => {
        // Handle page unload/reload
        const handleBeforeUnload = () => {
            if (isPlaying) {
                stopStream();
            }
        };

        const handleFullscreenChange = () => {
            isFullscreen = !!document.fullscreenElement;
        };

        window.addEventListener("beforeunload", handleBeforeUnload);
        document.addEventListener("fullscreenchange", handleFullscreenChange);

        // Autoplay stream on mount
        startStream();

        return () => {
            window.removeEventListener("beforeunload", handleBeforeUnload);
            document.removeEventListener(
                "fullscreenchange",
                handleFullscreenChange,
            );
            if (abortController) {
                abortController.abort();
            }
        };
    });
</script>

<svelte:head>
    <title>Keimli der II.</title>
</svelte:head>

<div class="min-h-screen bg-black py-8 px-4">
    <div class="w-full max-w-7xl mx-auto">
        <h1 class="text-white text-3xl font-bold mb-6">Keimli der II.</h1>

        <div
            bind:this={videoContainer}
            class="relative bg-black rounded-lg overflow-hidden group"
            style="width: 100%; aspect-ratio: 16/9;"
        >
            <canvas
                bind:this={canvasElement}
                class="w-full h-full bg-black"
                style="display: block; object-fit: contain;"
            ></canvas>

            <!-- Play/Pause button - lower left -->
            <button
                onclick={togglePlayPause}
                class="absolute bottom-4 left-4 bg-black/50 hover:bg-black/80 text-white p-2 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200"
                title={isPlaying ? "Pause stream" : "Play stream"}
            >
                {#if isPlaying}
                    <Pause class="w-5 h-5" />
                {:else}
                    <Play class="w-5 h-5" />
                {/if}
            </button>

            <!-- Fullscreen button - lower right -->
            <button
                onclick={toggleFullscreen}
                class="absolute bottom-4 right-4 bg-black/50 hover:bg-black/80 text-white p-2 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200"
                title="Toggle fullscreen"
            >
                {#if isFullscreen}
                    <Minimize2 class="w-5 h-5" />
                {:else}
                    <Maximize2 class="w-5 h-5" />
                {/if}
            </button>
        </div>
    </div>
</div>

<style>
    :global(html, body) {
        height: 100%;
        margin: 0;
        padding: 0;
    }
</style>
