import {roomTime, useRoomRunningAndReadyChanged, useRoomTime, useTimelineSongFileChanged} from "../lib/Connection";
import React, {createRef, SyntheticEvent, useRef, useState} from "react";

import '../css/player.sass'
import {Room} from "../types/types";
import {parse as parseQueryString} from "query-string";

const Player = () => {
    const room = useRoomRunningAndReadyChanged();
    const _ = useRoomTime()
    const timeline = useTimelineSongFileChanged();

    let player = useRef<HTMLAudioElement>(null)

    const [timesSeeked, setTimesSeeked] = useState(0);
    const [hadError, setHadError] = useState(false);

    // If our time synchronisation algorithm thing thinks the time is off by more
    // than this value, we seek the running player to correct it.
    const diffSecondsRequiredToSeekRunningPlayer = 0.20;

    // Hard cap we are allowed to seek this player. Some browsers are slow or inaccurate
    // and will always be off. To avoid endless skipping of the song this cap stops seeking the
    // player.
    const maxTimesSeekAllow = 25;

    const query = parseQueryString(window.location.search);
    if (query.nosound) {
        return null;
    }

    if (player.current && player.current.dataset.src != timeline!!.songFile) {
        player.current.dataset.src = timeline!!.songFile;
        player.current.src = timeline!!.songFile;
    }

    function handlePlayerOnPlay(e: SyntheticEvent) {
        e.preventDefault();

        // For when the user manually started the player for when autoplay is off.
        setHadError(false);
        if (shouldPlay()) {
            startPlaying(true)
        }
    }

    function shouldPlay() {
        return player.current && timeline && room && room.running && room.readyToParticipate
    }

    function startPlaying(manual: boolean) {
        if (!player.current) return;

        if (player.current.paused && !hadError) {
            player.current.play().then(() => {
                setHadError(false);
            }).catch(e => {
                console.error('Error playing', e);
                setHadError(true);
            })
        }

        if (!hadError) {
            setPlayerTime(room!!, manual);
        }
    }

    function setPlayerTime(room: Room, manualAdjustment: boolean) {
        if (!player.current) return;

        let targetTime = roomTime() / 1000;
        let diff = player.current.currentTime - targetTime;

        if (player.current && Math.abs(diff) > diffSecondsRequiredToSeekRunningPlayer) {
            if (room.speedFactor != 1 || manualAdjustment || timesSeeked < maxTimesSeekAllow) {
                player.current.currentTime = targetTime;
                player.current.playbackRate = Math.max(Math.min(4.0, room.speedFactor), 0.25);

                if (!manualAdjustment) {
                    setTimesSeeked(timesSeeked + 1);
                }
            } else {
                console.warn('The running player is off, but we\'ve changed the time ' +
                    'too often, skipping synchronizing the player.');
            }
        }
    }

    if (shouldPlay()) {
        startPlaying(false)
    } else {
        if (player.current) {
            player.current.pause();
        }
    }

    function render() {
        return (
            <audio ref={player} className='player' hidden={!hadError} controls={true} onPlay={handlePlayerOnPlay}/>
        )
    }

    return render();
}

export default Player;
