<!-->
	 taken from https://github.com/Linkcube/svelte-audio-controls
	 ISC License
-->
<svelte:options />

<script module>
    let getAudio = null;

    export function jumpToTrack(s) {
        getAudio().currentTime = s;
        getAudio().play();
    }
</script>

<script lang="ts">
    import { run, createBubbler } from 'svelte/legacy';

    const bubble = createBubbler();
    import { onMount, onDestroy } from 'svelte';
    import { fade } from 'svelte/transition';
    import { getStreamContext } from '$lib/streamContext.svelte.ts';
    import type { Track } from '$lib/types';

    const ctx = getStreamContext();

    interface Props {
        src: any;
        audio?: any;
        paused?: boolean;
        duration?: number;
        muted?: boolean;
        volume?: number;
        preload?: string;
        iconColor?: string;
        textColor?: string;
        barPrimaryColor?: string;
        barSecondaryColor?: string;
        backgroundColor?: string;
        display?: boolean;
        inlineTooltip?: boolean;
        disableTooltip?: boolean;
    }

    let {
        src,
        audio = $bindable(null),
        paused = $bindable(true),
        duration = $bindable(0),
        muted = $bindable(false),
        volume = $bindable(0.67),
        preload = 'metadata',
        iconColor = 'gray',
        textColor = 'gray',
        barPrimaryColor = 'lightblue',
        barSecondaryColor = '#6f6f6f',
        backgroundColor = 'white',
        display = false,
        inlineTooltip = false,
        disableTooltip = false
    }: Props = $props();

    getAudio = () => {
        return audio;
    };

    let currentTime = $state(0);
    let tooltip = $state();
    let tooltipX = $state(0);
    let tooltipY = $state(0);
    let showTooltip = $state(false);
    let seekText = $state('');
    let seekTrack = $state('');
    let seeking = $state(false);
    let volumeSeeking = $state(false);
    let pendingSeekTime = $state<number | null>(null);
    let songBar = $state();
    let volumeBar = $state();
    let innerWidth = $state();
    let innerHeight = $state();
    let isSafari = $state(false);

    onMount(async () => {
        // work around coreaudio bug
        // see https://git.webbieweb.org/apt-get/strimserve/issues/1
        const ua = navigator.userAgent;
        const isIOS = /iPad|iPhone|iPod/.test(ua);
        const isDesktopSafari = /Safari/.test(ua) && !/Chrome/.test(ua) && !/Chromium/.test(ua);
        isSafari = isIOS || isDesktopSafari;

        // default volume
        const volumeData = localStorage.getItem('volume');
        volume = volumeData ? parseFloat(volumeData) : 0.67;
        setVolume(volume);
    });

    // update browser metadata on track changes
    $effect(() => {
        if ('mediaSession' in navigator && ctx.songIndex != null && !paused && ctx.current) {
            setMediaMetadata(ctx.current.tracks[ctx.songIndex]);
        }
    });

    function seek(event, bounds) {
        let x = event.clientX - bounds.left;
        return Math.min(Math.max(x / bounds.width, 0), 1);
    }

    // exponential volume bar
    // default is linear, which doesn't correspond to human hearing
    function setVolume(volume) {
        if (volume != 0) {
            audio.volume = Math.pow(10, 2.5 * (volume - 1));
        } else {
            audio.volume = volume;
        }
    }

    function setMediaMetadata(track: Track) {
        navigator.mediaSession.metadata = new MediaMetadata({
            artist: track[1],
            title: track[2]
        });
    }

    // browsers don't like if you update this while no media is playing
    function setMediaMetadataOnPlay() {
        if ('mediaSession' in navigator && ctx.current && ctx.songIndex != null) {
            setMediaMetadata(ctx.current.tracks[ctx.songIndex]);
        }
    }

    // workaround for bug https://github.com/sveltejs/svelte/issues/5347
    // need to init duration & volume after SSR first load
    function updateAudioAttributes(audio) {
        if (audio && audio.duration) {
            duration = audio.duration;
            setVolume(volume);

            // workaround for bug https://github.com/sveltejs/svelte/issues/5914
            audio.addEventListener('loadedmetadata', (event) => {
                paused = audio.paused;
            });
        }
    }

    function updateSeekVisual(event) {
        if (!songBar) return;
        pendingSeekTime = seek(event, songBar.getBoundingClientRect()) * duration;
    }

    function seekVolume(event) {
        if (!volumeBar) return;
        volume = seek(event, volumeBar.getBoundingClientRect());
        setVolume(volume);
        localStorage.setItem('volume', volume.toString());
        muted = false;
    }

    function formatSeconds(totalSeconds, forceHours = false) {
        if (isNaN(totalSeconds)) return 'No Data';
        totalSeconds = parseInt(totalSeconds, 10);
        var hours = Math.floor(totalSeconds / 3600);
        var minutes = Math.floor(totalSeconds / 60) % 60;
        var seconds = totalSeconds % 60;

        return [hours, minutes, seconds]
            .map((v) => (v < 10 ? '0' + v : v))
            .filter((v, i) => v !== '00' || i > 0 || forceHours)
            .join(':');
    }

    function seekTooltip(event) {
        if (!inlineTooltip) {
            let tooltipBounds = tooltip.getBoundingClientRect();
            tooltipX = Math.min(event.clientX + 10, innerWidth - tooltipBounds.width);
            tooltipY = Math.min(songBar.offsetTop - 30, innerHeight - tooltipBounds.height);
        }
        let bounds = songBar.getBoundingClientRect();
        let seekValue = ((event.clientX - bounds.left) * duration) / bounds.width;
        updateTooltipText(seekValue);
    }

    function updateTooltipText(seekValue: number) {
        if (!ctx.current) return;
        let trackArray = ctx.current.tracks[ctx.getSongAtTime(seekValue)];
        seekTrack = trackArray[1] + ' - ' + trackArray[2];
        seekText = formatSeconds(seekValue);
    }

    function trackMouse(event) {
        if (seeking) updateSeekVisual(event);
        if (showTooltip && !disableTooltip) seekTooltip(event);
        if (volumeSeeking) seekVolume(event);
    }
    run(() => {
        if (ctx.songIndex != null) {
            ctx.updateCurrentSong(currentTime, ctx.songIndex);
        }
    });
    run(() => {
        updateAudioAttributes(audio);
    });

    export {
        src,
        audio,
        paused,
        duration,
        muted,
        volume,
        preload,
        iconColor,
        textColor,
        barPrimaryColor,
        barSecondaryColor,
        backgroundColor,
        display,
        inlineTooltip,
        disableTooltip
    };
</script>

<svelte:window
    bind:innerWidth
    bind:innerHeight
    onmouseup={() => {
        if (seeking && pendingSeekTime != null) {
            audio.currentTime = pendingSeekTime;
            currentTime = pendingSeekTime;
            updateTooltipText(pendingSeekTime);
            pendingSeekTime = null;
        }
        seeking = volumeSeeking = false;
    }}
    onmousemove={trackMouse}
/>

{#if display}
    <div class="controls" style="--color:{textColor}; --background-color:{backgroundColor}">
        <button
            class="material-icons"
            style="--icon-color:{iconColor}"
            onclick={() => (audio.paused ? audio.play() : audio.pause())}
        >
            {#if paused}
                play_arrow
            {:else}
                pause
            {/if}
        </button>
        <progress
            bind:this={songBar}
            value={seeking && pendingSeekTime != null ? pendingSeekTime : (currentTime || 0)}
            max={duration}
            onmousedown={(e) => { seeking = true; updateSeekVisual(e); }}
            onmouseenter={() => (showTooltip = true)}
            onmouseleave={() => (showTooltip = false)}
            style="--primary-color:{barPrimaryColor}; --secondary-color:{barSecondaryColor}"
            class="song-progress"
        ></progress>
        <div class="control-times">{formatSeconds(currentTime, duration >= 3600)}/{formatSeconds(duration)}</div>
        <button
            style="--icon-color:{iconColor}"
            class="material-icons"
            onclick={() => (muted = !muted)}
        >
            {#if muted}
                volume_off
            {:else if volume < 0.01}
                volume_mute
            {:else if volume < 0.5}
                volume_down
            {:else}
                volume_up
            {/if}
        </button>
        <progress
            bind:this={volumeBar}
            value={volume}
            onmousedown={() => (volumeSeeking = true)}
            onclick={seekVolume}
            style="--primary-color:{barPrimaryColor}; --secondary-color:{barSecondaryColor}"
            class="volume-progress"
        ></progress>
        {#if !disableTooltip && (inlineTooltip || showTooltip)}
            <div
                class:hover-tooltip={!inlineTooltip}
                transition:fade
                bind:this={tooltip}
                class="tooltip"
                style="--left:{tooltipX}px;
                --top:{tooltipY}px;
                --background-color:{backgroundColor};
                --box-color:{barSecondaryColor};
                --text-color:{textColor}"
            >
                {#if showTooltip}
                    {seekText}
                    <br />
                    {seekTrack}
                {:else if duration > 3600}
                    --:--:--
                {:else}
                    --:--
                {/if}
            </div>
        {/if}
    </div>
{/if}

<audio
    bind:this={audio}
    bind:paused
    bind:duration={null, (d) => (duration = d || 0)}
    bind:currentTime
    {muted}
    onplay={setMediaMetadataOnPlay}
    onended={bubble('ended')}
    {preload}
>
    {#if isSafari}
        <source src="{src}.mp3" type="audio/mpeg" />
        <source {src} type="audio/ogg;codecs=opus" />
    {:else}
        <source {src} type="audio/ogg;codecs=opus" />
        <source src="{src}.mp3" type="audio/mpeg" />
    {/if}
</audio>

<style>
    .controls {
        display: flex;
        flex-flow: row;
        justify-content: space-around;
        color: var(--color);
        background-color: var(--background-color);
        padding-left: 10px;
        padding-right: 10px;
        -webkit-user-select: none; /* Safari */
        -ms-user-select: none; /* IE 10+ and Edge */
        user-select: none; /* Standard syntax */
        padding-top: 5px;
        padding-bottom: 5px;
        border: 3px double;
        border-radius: 5px;
        color: rgba(0, 0, 0, 0.6);
    }

    .control-times {
        margin: auto;
        margin-right: 5px;
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
        min-width: max-content;
    }

    .tooltip {
        background-color: var(--background-color);
        padding: 1px;
        border-radius: 5px;
        border-width: 3px;
        box-shadow: 6px 6px var(--box-color);
        color: var(--text-color);
        pointer-events: none;
        min-width: 50px;
        text-align: center;
        margin-bottom: 5px;
    }

    .hover-tooltip {
        position: absolute;
        top: var(--top);
        left: var(--left);
    }

    .material-icons {
        font-size: 16px;
        margin-bottom: 0px;
        color: var(--icon-color);
        background-color: rgba(0, 0, 0, 0);
        cursor: pointer;
        transition: 0.3s;
        border: none;
        border-radius: 25px;
    }

    .material-icons:hover {
        box-shadow: 0px 6px rgba(0, 0, 0, 0.6);
    }

    .material-icons::-moz-focus-inner {
        border: 0;
    }

    progress {
        display: block;
        color: var(--primary-color);
        background: var(--secondary-color);
        border: none;
        height: 15px;
        margin: auto;
        margin-left: 5px;
        margin-right: 5px;
    }

    progress::-webkit-progress-bar {
        background-color: var(--secondary-color);
        width: 100%;
    }

    progress::-moz-progress-bar {
        background: var(--primary-color);
    }

    progress::-webkit-progress-value {
        background: var(--primary-color);
    }

    .song-progress {
        width: 100%;
    }

    .volume-progress {
        width: 10%;
        max-width: 100px;
        min-width: 50px;
    }
</style>
