import { makeMessage, Resolver } from './websocketResolver.js';
import { ListOperationTypes, MediaAction, MessageTypes, PlayerState, YoutubeResourceType } from "./enums.js";

window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady

const youtubeApiScript = document.createElement('script');

youtubeApiScript.src = "https://www.youtube.com/iframe_api";
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(youtubeApiScript, firstScriptTag);


const RTT_ESTIMATE = 1
const ALLOWED_AHEAD = 5

const PLAYER_WIDTH = 640
const PLAYER_HEIGHT = 360

let videos = null;
let videosPlayed = null;

let videoPlaying = null;
let state = null;

let isLeader = null;

let repeat = false;

let player = null;
let playerActive = false;
document.getElementById('player').append(mockPlayer('360', '640'));

const codeInfoMap = new Map()

function onYouTubeIframeAPIReady() {
    onYTDone();
}


function onPlayerReady(event) {
    event.target.seekTo(0);
    if (state === PlayerState.PLAYING) {
        event.target.playVideo();
    } else if (state === PlayerState.PAUSED) {
        event.target.pauseVideo();
    } else if (state === PlayerState.LIST_END) {
        event.target.stopVideo();
    }
}

function onPlayerStateChange(event) {
    console.log(event.data, YT.PlayerState, state, videoPlaying, videos);
    if (event.data === 0 /*Ended*/) {
        if (state !== PlayerState.PLAYING) {
            console.warn("Player ended up in state ENDED while it is was not playing.")
            return
        }
        if (isLeader && videoPlaying !== null) {
            socket.ws.send(makeMessage(MessageTypes.SONG_END, { id: videoPlaying.id }))
        }
        const vid = popVideo()
        if (vid !== undefined) {
            playVideo(vid);
        } else {
            videoPlaying = null;
            state = PlayerState.LIST_END;
        }
    }
}

function onAddVideo() {
    console.log(state, videoPlaying, videos)
    switch (state) {
        case PlayerState.LIST_END:
            if (videoPlaying === null) {
                playVideo(popVideo());
            } else {
                console.error(`Invalid state: state=${state}; videoPlaying=${videoPlaying}`)
                return
            }
            break
        case PlayerState.PAUSED:
            if (videoPlaying === null) {
                loadVideo(popVideo())
            }
            break
        case PlayerState.PLAYING:
            if (videoPlaying === null) {
                playVideo(popVideo())
            }
            break
        default:
            console.error("Unknown state", state)
            break
    }
}

function addVideo(code, id) {
    videos.push({ code, id });
    makeQueueLine(code, id);
    onAddVideo();
}

function popVideo() {
    console.log("pop", videos)
    if (videos.length <= 0) {
        if (videosPlayed) {
            videos = []
            for (const { code, id } of videosPlayed) {
                videos.push({code, id});
                makeQueueLine(code, id);
            }
            videosPlayed = []
        } else {
            state = PlayerState.LIST_END
            return undefined;
        }
    }
    const vid = videos.shift();
    queueElement.removeChild(queueElement.querySelector(".videoListCard"));
    if (videosPlayed) {
        videosPlayed.push(vid);
    }
    return vid;
}

function findVideoIndex(id) {
    return videos.findIndex(function (vid) {
        return vid.id === id
    })
}

function delVideo(id) {
    videos.splice(findVideoIndex(id), 1)
    removeQueueLine(id)
}

function moveVideo(id, displacement) {
    const i = findVideoIndex(id)
    const [vid] = videos.splice(i, 1)

    const new_i = i + displacement
    videos.splice(new_i, 0, vid)
    removeQueueLine(id)

    if (new_i + 1 === videos.length) {
        makeQueueLine(vid.code, id)
    } else {
        console.log(videos, new_i)
        makeQueueLine(vid.code, id, videos[new_i + 1].id)
    }

}

function playVideo(vid) {
    videoPlaying = vid;
    state = PlayerState.PLAYING

    if (!playerActive) {
        return;
    }

    if (player === null) {
        buildPlayer(PLAYER_HEIGHT, PLAYER_WIDTH, vid.code);
    } else {
        player.loadVideoById(vid.code, 0);
    }
}

function loadVideo(vid) {
    videoPlaying = vid;

    if (!playerActive) {
        return
    }

    if (player === null) {
        buildPlayer(PLAYER_HEIGHT, PLAYER_WIDTH, vid.code);
    } else {
        player.cueVideoById(vid.code, 0);
    }
}

function setLeader(b) {
    console.log(isLeader, b)
    if (isLeader !== b) {
        let btn = document.getElementById("leader-button");
        btn.innerText = b ? "Leader" : "Follower";
        btn.classList.remove(b ? "btn-outline-success" : "btn-success")
        btn.classList.add(b ? "btn-success" : "btn-outline-success")
        if (isLeader === null) {
            btn.classList.remove("disabled", "btn-outline-secondary")
        }
        isLeader = b;
    }
}

window.setLeader = setLeader

function mockPlayer(height, width) {
    const rect = document.createElement('div');
    rect.setAttribute('style', `height:${height}px;width:${width}px;background:black`);
    return rect
}

function buildPlayer(height, width, id) {
    player = new YT.Player('player', {
        height: height + 48,
        width: width,
        videoId: id,
        playerVars: {},
        events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange,
        }
    });
    window.player = player
}

const queueElement = document.getElementById('videoList')
const queueLine = document.getElementById('videoListCardTemplate')

function makeQueueLine(code, id, before_id) {
    let newQueueLine = queueLine.cloneNode(true);
    newQueueLine.id = "";
    newQueueLine.hidden = false;
    newQueueLine.setAttribute("data-id", id)
    if (codeInfoMap.has(code)) {
        const { thumbnails, title, channelTitle, publishTime, description } = codeInfoMap.get(code)

        const thumbnail = newQueueLine.getElementsByClassName("videoListCardThumbnail")[0]
        const img = document.createElement('img')
        thumbnail.appendChild(img)
        img.setAttribute('src', thumbnails.default.url)
        img.setAttribute('width', thumbnails.default.width)
        img.setAttribute('height', thumbnails.default.height)
        img.setAttribute('alt', "")

        newQueueLine.getElementsByClassName("videoListCardTitle")[0].innerText = title
        newQueueLine.getElementsByClassName("videoListCardChannel")[0].innerText = channelTitle
        newQueueLine.getElementsByClassName("videoListCardDescription")[0].innerText = description.replace(/\n/g, " ")
    }

    newQueueLine.setAttribute("data-youtubeId", code)

    function delHandler(event) {
        onDeleteClick(event, id)
    }

    const delButton = newQueueLine.querySelector('.videoListCardDelete')
    delButton.addEventListener("click", delHandler)
    delButton.addEventListener("keydown", delHandler)

    function moveUpHandler(event) {
        onMoveClick(event, id, -1)
    }

    const upButton = newQueueLine.querySelector('.videoListCardMoveUp')
    upButton.addEventListener("click", moveUpHandler)
    upButton.addEventListener("keydown", moveUpHandler)

    function moveDownHandler(event) {
        onMoveClick(event, id, 1)
    }

    const downButton = newQueueLine.querySelector('.videoListCardMoveDown')
    downButton.addEventListener("click", moveDownHandler)
    downButton.addEventListener("keydown", moveDownHandler)

    if (before_id == null) {
        queueLine.before(newQueueLine);
    } else {
        queueElement.querySelector(`[data-id='${before_id}']`).before(newQueueLine)
    }
}

function removeQueueLine(id) {
    const card = queueElement.querySelector(`div[data-id='${id}']`);
    queueElement.removeChild(card);
}

function onSubmit(event) {
    event.preventDefault();
    socket.ws.send(makeMessage(MessageTypes.LIST_OPERATION, {
        op: ListOperationTypes.ADD,
        code: event.target[0].value
    }))
}

function onSearch(event) {
    event.preventDefault();
    const q = event.target[0].value
    if (q !== "") {
        socket.ws.send(makeMessage(MessageTypes.SEARCH, { q }))
    }
}

function onDeleteClick(event, id) {
    event.preventDefault();
    socket.ws.send(makeMessage(MessageTypes.LIST_OPERATION, { op: ListOperationTypes.DEL, id }))
}

function onMoveClick(event, id, displacement) {
    event.preventDefault();
    socket.ws.send(makeMessage(MessageTypes.LIST_OPERATION, { op: ListOperationTypes.MOVE, id, displacement }))
}

function onLeaderbutton(event) {
    if (isLeader) {
        socket.ws.send(makeMessage(MessageTypes.RELEASE_CONTROL))
    } else {
        socket.ws.send(makeMessage(MessageTypes.OBTAIN_CONTROL))
    }
}

const playerPlaceholder = document.getElementById('playerPlaceholder');
const playerPlaceholderParent = playerPlaceholder.parentElement;

const playerContainer = document.getElementById('playerContainer')

const showPlaceholderButton = document.getElementById('showPlayerPlaceholder');

function onPlayerStart(event) {
    event.preventDefault();
    playerActive = true;
    playerPlaceholderParent.removeChild(playerPlaceholder)
    playerContainer.toggleAttribute("hidden")
    switch (state) {
        case PlayerState.LIST_END:
            break;
        case PlayerState.PAUSED:
            if (videoPlaying !== null) {
                loadVideo(videoPlaying);
            } else {
                console.error(`Invalid state: state=${state}; videoPlaying=${videoPlaying}`)
                return
            }
            break;
        case PlayerState.PLAYING:
            if (videoPlaying !== null) {
                playVideo(videoPlaying);
            } else {
                console.error(`Invalid state: state=${state}; videoPlaying=${videoPlaying}`)
                return
            }
            break;
    }
    socket.ws.send(makeMessage(MessageTypes.PLAYER_ENABLED, { enabled: true }))
}

function onPlayerClose(event) {
    event.preventDefault();
    playerActive = false;
    playerContainer.toggleAttribute("hidden")
    playerPlaceholderParent.appendChild(playerPlaceholder)
    if (player !== null) {
        player.pauseVideo();
    }
    socket.ws.send(makeMessage(MessageTypes.PLAYER_ENABLED, { enabled: false }))
}

function hidePlayerPlaceholder(event) {
    event.preventDefault();
    playerPlaceholderParent.removeChild(playerPlaceholder);
    showPlaceholderButton.toggleAttribute("hidden")
}

function showPlayerPlaceholder(event) {
    event.preventDefault();
    playerPlaceholderParent.appendChild(playerPlaceholder);
    showPlaceholderButton.toggleAttribute("hidden")
}

function onPlayButton(event) {
    event.preventDefault();
    socket.ws.send(makeMessage(MessageTypes.MEDIA_ACTION, { action: MediaAction.PLAY }))
}

function onPauseButton(event) {
    event.preventDefault();
    socket.ws.send(makeMessage(MessageTypes.MEDIA_ACTION, { action: MediaAction.PAUSE }))

}

function onNextButton(event) {
    event.preventDefault();
    if (videoPlaying !== null) {
        socket.ws.send(makeMessage(MessageTypes.MEDIA_ACTION, { action: MediaAction.NEXT, current_id: videoPlaying.id }))
    }
}

const repeatButton = document.getElementById('repeat-button');

function onRepeatButton(event) {
    event.preventDefault();
    socket.ws.send(makeMessage(MessageTypes.MEDIA_ACTION, { action: MediaAction.REPEAT, enable: !repeat }))
}

function stateProcessor(ws, data) {
    const { playing, state: newState, lists } = data;
    const { next, previous } = lists

    videos = []
    videosPlayed = previous
    repeat = !!previous
    state = newState
    videoPlaying = playing

    const codes = []
    for (const song of next) {
        const { code, id } = song
        addVideo(code, id)
        if (!(codes.includes(code))) {
            codes.push(code)
        }
    }

    for (const song of previous || []) {
        const { code, id } = song;
        if (!(codes.includes(code))) {
            codes.push(code)
        }
    }

    if (codes.length > 0) {
        socket.ws.send(makeMessage(MessageTypes.SEARCH_ID, { id: codes }))
    }

    if (videoPlaying !== null) {
        if (state === PlayerState.PLAYING) {
            playVideo(videoPlaying)
        } else {
            loadVideo(videoPlaying)
        }
    }
    if (isLeader === null) {
        setLeader(false)
    }
    if (repeat) {
        repeatButton.classList.toggle('btn-outline-secondary');
        repeatButton.classList.toggle('btn-secondary');
    }
    afterStateInit()
}

function listOperationProcessor(ws, data) {
    const { op, items } = data;
    if (op === ListOperationTypes.ADD) {
        const noCodeInfo = []
        for (const { code, id, snippet } of items) {
            if (snippet !== undefined) {
                codeInfoMap.set(code, snippet)
            } else if (!codeInfoMap.has(code)) {
                noCodeInfo.push(code)
            }
            addVideo(code, id);
        }
        if (noCodeInfo.length > 0) {
            socket.ws.send(makeMessage(MessageTypes.SEARCH_ID, { id: noCodeInfo.join(',') }))
        }
    } else if (op === ListOperationTypes.DEL) {
        for (const { id } of items) {
            delVideo(id);
        }
    } else if (op === ListOperationTypes.MOVE) {
        for (const { id, displacement } of items) {
            moveVideo(id, displacement)
        }
    }
}

function mediaActionProcessor(ws, data) {
    const { action, ended_id, current_id, enable } = data;
    if (action === MediaAction.PLAY && state === PlayerState.PAUSED) {
        state = PlayerState.PLAYING
        if (playerActive) {
            player.playVideo();
        }
    } else if (action === MediaAction.PAUSE && state === PlayerState.PLAYING) {
        state = PlayerState.PAUSED
        if (playerActive) {
            player.pauseVideo();
        }
    } else if (action === MediaAction.NEXT) {
        if (videoPlaying !== null && videoPlaying.id === ended_id) {
            const vid = popVideo()
            if (vid !== undefined) {
                playVideo(vid)
            } else {
                videoPlaying = null;
                state = PlayerState.LIST_END;
                if (player) {
                    player.stopVideo();
                }
            }
        }
    } else if (action === MediaAction.REPEAT) {
        if (enable !== repeat) {
            repeat = enable;
            repeatButton.classList.toggle('btn-outline-secondary');
            repeatButton.classList.toggle('btn-secondary');
            videosPlayed = repeat ? (videoPlaying ? [videoPlaying] : []) : null;
        }
    }
}

function songEndProcessor(ws, data) {
    const { ended_id, current_id } = data;
    console.log(ended_id, current_id)
    if (videoPlaying === null) {
        // Do nothing
    } else if (ended_id === videoPlaying.id) {
        const vid = popVideo()
        console.log(vid)
        if (vid !== undefined) {
            playVideo(vid);
        } else {
            videoPlaying = null;
            state = PlayerState.LIST_END
            // TODO SEEK TO END
        }
    } else if (current_id === videoPlaying.id) {
        if (!isLeader && player.getCurrentTime() - RTT_ESTIMATE - ALLOWED_AHEAD > 0) {
            player.seekTo(RTT_ESTIMATE + ALLOWED_AHEAD, true)
        }
    } else {
        console.error("Difficult state reached. Reset protocol not implemented. Either to far ahead, behind or state inconsistency", ended_id, current_id, videoPlaying)
    }
}


const searchResultTemplate = document.getElementById("searchResultTemplate")
const searchResultList = searchResultTemplate.parentElement
searchResultList.removeChild(searchResultTemplate)
searchResultTemplate.id = ""

function makeSearchResult(item) {
    const { id, snippet } = item
    const { kind, videoId, channelId, playlistId } = id
    const { thumbnails, title, channelTitle, publishTime, description } = snippet

    const code = kind === YoutubeResourceType.CHANNEL ? channelId :
        kind === YoutubeResourceType.PLAYLIST ? playlistId :
            kind === YoutubeResourceType.VIDEO ? videoId :
                console.error(`Unknown kind ${kind}`)

    if (!code) return;

    const searchResult = searchResultTemplate.cloneNode(true)
    searchResult.setAttribute('data-youtubeID', code)


    function onClickHandler() {
        socket.ws.send(makeMessage(MessageTypes.LIST_OPERATION, {
            op: ListOperationTypes.ADD,
            kind,
            code
        }));
        if (kind === YoutubeResourceType.VIDEO) {
            codeInfoMap.set(videoId, snippet)
        }
    }

    searchResult.addEventListener("click", onClickHandler)
    searchResult.addEventListener("keydown", onClickHandler)

    const thumbnailContainer = searchResult.getElementsByClassName("searchResultThumbnail")[0]
    const thumbnailImage = thumbnailContainer.getElementsByClassName("thumbnailImage")[0]
    const img = document.createElement('img')
    thumbnailImage.appendChild(img)

    const { url, width, height } = (thumbnails ? thumbnails["default"] : {
        url: "/img/no_thumbnail.png",
        width: 120,
        height: 90
    })

    img.setAttribute('src', url)
    img.setAttribute('width', width)
    img.setAttribute('height', height)
    img.setAttribute('alt', "")

    if (kind !== YoutubeResourceType.PLAYLIST) {
        const playlistOverlay = thumbnailContainer.getElementsByClassName("thumbnailPlaylistOverlay")[0]
        playlistOverlay.parentElement.removeChild(playlistOverlay);
    }

    searchResult.getElementsByClassName("searchResultTitle")[0].innerText = title
    searchResult.getElementsByClassName("searchResultChannel")[0].innerText = channelTitle
    searchResult.getElementsByClassName("searchResultDescription")[0].innerText = description
    searchResult.removeAttribute('hidden')

    searchResultList.appendChild(searchResult)
}

function searchResultProcessor(_, data) {
    const { items } = data;
    searchResultList.innerHTML = '';
    for (const item of items) {
        makeSearchResult(item)
    }
}

function searchIdResultProcessor(_, data) {
    const { items } = data;
    for (const { id: code, snippet } of items) {
        codeInfoMap.set(code, snippet);
        const lines = queueElement.querySelectorAll(`[data-youtubeID='${code}`)
        for (const line of lines) {
            if (line !== null) {
                const id = parseInt(line.getAttribute("data-id"))
                makeQueueLine(code, id, id)
                line.parentElement.removeChild(line)
            }
        }
    }
}

let socket;

function onYTDone() {
    const resolver = new Resolver()
    resolver.register(MessageTypes.STATE, stateProcessor)
    resolver.register(MessageTypes.LIST_OPERATION, listOperationProcessor)
    resolver.register(MessageTypes.MEDIA_ACTION, mediaActionProcessor)
    resolver.register(MessageTypes.OBTAIN_CONTROL, () => setLeader(true))
    resolver.register(MessageTypes.RELEASE_CONTROL, () => setLeader(false))
    resolver.register(MessageTypes.SONG_END, songEndProcessor)
    resolver.register(MessageTypes.SEARCH, searchResultProcessor)
    resolver.register(MessageTypes.SEARCH_ID, searchIdResultProcessor)
    socket = resolver.connectSocket()
    socket.addEventListener("open", function () {
        socket.ws.send(makeMessage(MessageTypes.STATE, null))
    })
}

function afterStateInit() {
    // document.getElementById('addVideoForm').addEventListener('submit', onSubmit);
    document.getElementById('searchVideoForm').addEventListener('submit', onSearch)
    document.getElementById('leader-button').addEventListener('click', onLeaderbutton)
    document.getElementById('startPlayerButton').addEventListener('click', onPlayerStart)
    document.getElementById('closePlayer').addEventListener('click', onPlayerClose)
    document.getElementById('hidePlayerPlaceholder').addEventListener('click', hidePlayerPlaceholder)
    document.getElementById('showPlayerPlaceholder').addEventListener('click', showPlayerPlaceholder)
    document.getElementById('play-button').addEventListener('click', onPlayButton)
    document.getElementById('pause-button').addEventListener('click', onPauseButton)
    document.getElementById('next-button').addEventListener('click', onNextButton)
    document.getElementById('repeat-button').addEventListener('click', onRepeatButton)
}
