<svelte:options runes={true} />

<script lang="ts">
    import {OpenSubsonic} from "$lib/opensubsonic";
    import {onMount} from "svelte";
    import QueueFrame from "$lib/components/custom/QueueFrame.svelte";
    //import PlayerFrame from "$lib/components/custom/PlayerFrame.svelte";
    import {Button} from "$lib/components/ui/button";

    function toFixedNumber(num, digits, base){
        const pow = Math.pow(base ?? 10, digits);
        return Math.round(num*pow) / pow;
    }

    enum PlaybackMode {
        Linear,
        LoopOne,
        LoopQueue,
    }

    class PlaybackState {
        values = {
            [PlaybackMode.Linear]: "1",
            [PlaybackMode.LoopOne]: "0",
            [PlaybackMode.LoopQueue]: "00",
        }

        current: PlaybackMode = $state(PlaybackMode.Linear);

        next = function() {
            console.log(this.current)
            if (this.current == PlaybackMode.LoopQueue) {
                this.current = -1;
            }
            return this.values[++this.current];
        }

        prev = function() {
            if (this.current == PlaybackMode.Linear) {
                this.current = this.values.length;
            }
            return this.values[--this.current];
        }

        get = function() {
            return this.values[this.current];
        }
    }

    let source: HTMLAudioElement = $state();
    let mode = $state(new PlaybackState());
    let title = $state("");
    let artist = $state("");
    let queueIndex = $state(0);
    let currentSong = $state({
        data: {
            title,
            artist
        },
        source,
        queueIndex
    });
    let isPaused = $state(true);
    let volume = $state(0.2);
    let progress = $state(0);
    let duration = $state(0);

    let queue: Array<unknown> = $state([]);

    async function fetchQueue() {
        const data = await OpenSubsonic.get("getPlayQueue");
        if (data) {
            queue = [];
            queue = queue.concat(data.playQueue.entry);
        }
    }

    async function fetchNowPlaying() {
        const data = await OpenSubsonic.get("getNowPlaying");
        let foundInNowPlaying = false;
        if (data && data.nowPlaying.entry) {
            data.nowPlaying.entry.forEach((entry) => {
                if (entry.username == OpenSubsonic.username) {
                    newSong(entry);
                    foundInNowPlaying = true;
                }
            })
        }
        if (!foundInNowPlaying && queue.length != 0) {
            newSong(queue[0])
            currentSong.queueIndex = 0;
        }
    }

    async function saveQueue() {
        let songs = [];

        queue.forEach((song, idx) => {
            if (idx === 0) {
                songs.push({parameter: "current", value: song.id})
                songs.push({parameter: "id", value: song.id})
                // Add Progress within current song
            }
            else {
                songs.push({parameter: "id", value: song.id})
            }
        })
        const data = await OpenSubsonic.get("savePlayQueue", songs);
        if (data) {
            await fetchQueue();
        }
    }

    function removeSongFromQueue(idx: number) {
        if (idx > -1) {
            queue.splice(idx, 1);
        }
    }

    function playSong(song: unknown, songIndex: number) {
        //const chosenSong = queue[songIndex];
        pause();
        newSong(song);
        currentSong.queueIndex = songIndex;
        play();
    }

    function newSong(song: unknown) {
        let parameters = [
            { parameter: "id", value: song.id },
            //{ parameter: "maxBitRate", value: } // TODO
            //{ parameter: "format", value: } // TODO
            //{ parameter: "timeOffset", value: } // TODO? Only Video related
            //{ parameter: "size", value: } // TODO? Only Video related
            { parameter: "estimateContentLength", value: "true" },
            //{ parameter: "converted", value: } // TODO? Only Video related
        ];
        let url = OpenSubsonic.getApiUrl("stream", parameters);
        currentSong.source = new Audio(url); // Assign new URL
        currentSong.data = song;

        // Reassign Event Handlers
        currentSong.source.onloadedmetadata = () => {
            duration = currentSong.source.duration;
        };

        currentSong.source.onplay = () => {
            currentSong.source.volume = volume;
            isPaused = currentSong.source.paused;
            let time: number = Date.now();
            let parameters = [
                { parameter: "id", value: song.id },
                { parameter: "time", value: time},
                { parameter: "submission", value: false}
            ];
            console.warn("Scrobble is disabled");
            OpenSubsonic.get("scrobble", parameters);
        }

        currentSong.source.onpause = () => {
            isPaused = currentSong.source.paused;
        }

        currentSong.source.ontimeupdate = () => {
            progress = currentSong.source.currentTime;
        }

        currentSong.source.onended = () => {
            if (mode.current == PlaybackMode.LoopOne) {
                currentSong.source.play();
            }
        }

        currentSong.source.load();
    }

    function play() {
        currentSong.source.play().catch(() => {});
    }
    function pause() {
        currentSong.source.pause();
    }

    function changeVolume(change: number) {
        if (volume + change > 1) {
            volume = 1;
        }
        else if (volume + change < 0) {
            volume = 0;
        }
        else {
            volume = toFixedNumber(volume + change, 2, 10) ;
        }

        if (currentSong.source) {
            currentSong.source.volume = volume;
        }
    }

    function progressPercent() {
        return progress / duration * 100 || 0;
    }

    function displayTime(rawSeconds: number) {
        const intSeconds = rawSeconds.toFixed(0);
        const seconds = intSeconds % 60;
        const minutes = Math.floor((intSeconds / 60)) % 60;
        const hours = Math.floor((intSeconds / 3600));

        if (hours == 0) {
            return `${minutes.toString()}:${seconds.toString().padStart(2, 0)}`
        }
        else {
            return `${hours}:${minutes.toString().padStart(2, 0)}:${seconds.toString().padStart(2, 0)}`
        }
    }

    onMount(() => {
        fetchQueue().then(() => {
            fetchNowPlaying();
        });
        currentSong.source = new Audio();
    })
</script>

<svelte:head>
    <title>Lytter - {currentSong.data.title}({currentSong.data.artist})</title>
    <meta name="robots" content="noindex nofollow" />
</svelte:head>

<div class="border border-2 flex-1 grid grid-cols-5">
    <div class="border border-2 col-span-1">
        <h1>Left Sidebar</h1>
    </div>
    <div class="border border-2 col-span-3">
        <h1>Center</h1>
    </div>
    <QueueFrame {queue} {fetchQueue} {saveQueue} {removeSongFromQueue} {playSong} currentIndex={currentSong.queueIndex} />
</div>
<div class="border border-2 min-h-24 h-24">
    <div class="flex flex-row gap-2">
        <p class="border p-2">Song: {currentSong.data.title}</p>
        <p class="border p-2">Volume: {volume}</p>
        <p class="border p-2">{displayTime(progress)}/{displayTime(duration)}</p>
        <p class="border p-2">{progressPercent().toFixed(2)}%</p>
        <Button class="border p-2" onclick={() => changeVolume(-0.05)}>-</Button>
        <Button class="border p-2" onclick={() => changeVolume(0.05)}>+</Button>
        <Button class="border p-2" onclick={() => currentSong.source.currentTime -= 5}>{"<<"}</Button>
        {#if isPaused}
            <Button onclick={play}>Play</Button>
        {:else}
            <Button onclick={pause}>Pause</Button>
        {/if}
        <Button class="border p-2" onclick={() => currentSong.source.currentTime += 5}>{">>"}</Button>
        <Button class="border p-2" onclick={() => mode.next()}>{mode.get()}</Button>
    </div>
</div>

