<?php

require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn;
if (!$isLoggedIn) header("Location: /login") and die();

$title = "Watch Together"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php';

global $WebSocketAddress;

if (!isset($WebSocketAddress)) {
    $WebSocketAddress = "wss://ponies.equestria.horse/_WatchTogether-WebSocket-EntryPoint/socket";
}

?>

<style>
    .list-group-item {
        color: #fff;
        background-color: #222;
        border: 1px solid rgba(255, 255, 255, .125);
    }

    .list-group-item.disabled {
        color: #fff;
        background-color: #222;
        border-color: rgba(255, 255, 255, .125);
        opacity: .75;
    }

    .list-group-item-action:hover {
        background-color: #252525;
        color: #ddd;
    }

    .list-group-item-action:active, .list-group-item-action:focus {
        background-color: #272727;
        color: #bbb;
    }

    .video-queue-item {
        display: grid;
        grid-template-columns: 1fr max-content;
        grid-gap: 10px;
        cursor: pointer;
    }

    .video-queue-item-status {
        filter: invert(1);
        display: block;
        height: 32px;
        width: 32px;
    }

    .video-queue-item-part {
        display: flex;
        align-items: center;
    }

    .video-queue-item-metadata {
        width: 100%;
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
    }

    .video-queue-item-title-outer, .video-queue-item-author-outer {
        display: flex;
        align-items: center;
    }

    .video-queue-item-title, .video-queue-item-title-outer, .video-queue-item-author, .video-queue-item-author-outer {
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
        width: 100%;
    }

    .modal-header {
        border-bottom: 1px solid #353738;
    }

    .modal-content {
        border: 1px solid rgba(255, 255, 255, .2);
        background-color: #111;
    }

    .btn-close {
        filter: invert(1);
    }

    .user-ping {
        float: right;
        display: inline-block;
        width: 16px;
        height: 16px;
        border-radius: 999px;
        position: relative;
        top: 5px;
    }

    .control-item {
        display: inline-block;
        cursor: pointer;
        border-radius: 999px;
        background-color: rgba(0, 0, 0, 0);
        border: 1px solid rgba(255, 255, 255, 0);
        transition: background-color 200ms, border-color 200ms;
    }

    .control-item:hover {
        background-color: rgba(0, 0, 0, .25);
        border-color: rgba(255, 255, 255, .1);
    }

    .control-item:active {
        background-color: rgba(0, 0, 0, .5);
        border-color: rgba(255, 255, 255, .25);
    }

    .control-icon {
        filter: invert(1);
        width: 24px;
        height: 24px;
        margin: 12px;
        pointer-events: none;
    }

    body.hide-controls * {
        cursor: none;
    }

    #controls-outer {
        opacity: 1;
        transition: opacity 500ms;
    }

    .navbar, #sidebar {
        opacity: 1;
        transition: opacity 500ms;
    }

    body.hide-controls #controls-outer {
        opacity: 0;
    }

    body.hide-controls .navbar, body.hide-controls #sidebar {
        opacity: .25;
    }

    body.fullscreen #app-container {
        grid-template-columns: 1fr !important;
    }

    body.fullscreen .navbar, body.fullscreen #sidebar {
        display: none;
    }

    body.fullscreen #app-container div, body.fullscreen #app-container video {
        height: 100vh !important;
    }

    body.fullscreen #controls-outer, body.fullscreen #notification {
        inset: 0 !important;
    }

    #notification {
        opacity: 0;
        pointer-events: none;
        transition: opacity 200ms;
    }

    #video-title {
        opacity: 1;
        transition: opacity 200ms;
    }

    body.notification #notification {
        opacity: 1;
    }

    body.notification #video-title {
        opacity: 0;
    }

    body.skipper #skipper {
        opacity: 1 !important;
        pointer-events: initial !important;
    }

    #skipper {
        opacity: 0;
        pointer-events: none;
        transition: opacity 200ms;
        position: fixed;
        bottom: 79px;
        z-index: 999;
        background: rgba(0, 0, 0, .5);
        border-radius: 10px;
        padding: 10px 20px;
        left: 20px;
        border: 1px solid rgba(255, 255, 255, .25);
        cursor: pointer;
    }

    #skipper:hover {
        background: rgba(0, 0, 0, .75) !important;
    }

    #skipper:active {
        background: rgba(0, 0, 0, 1) !important;
    }
</style>

<script src="/assets/editor/thing.js"></script>

<div class="modal fade" id="error" data-bs-backdrop="static" data-bs-keyboard="false" style="z-index: 99999;">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">An error occurred</h4>
            </div>

            <div class="modal-body">
                <div class="alert alert-danger" id="error-message"></div>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="add" data-bs-backdrop="static" data-bs-keyboard="false">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Add new video</h4>
            </div>

            <div class="modal-body">
                <p>Enter a YouTube video ID to add to the queue:</p>
                <input type="text" class="form-control" id="add-id" placeholder="e.g. K9tUKbOFots" style="color:white;background:#111;border-color:#222;">
                <p style="margin-top:10px;margin-bottom: 0;">
                <div class="btn-group">
                    <span onclick="doAdd();" id="add-btn-yes" class="btn btn-primary">Add</span>
                    <span onclick="closeAdd();" id="add-btn-no" class="btn btn-secondary">Cancel</span>
                </div>
                <a href="#" onclick="document.getElementById('add-id').value = 'K9tUKbOFots';">test</a>
                </p>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="welcome" data-bs-backdrop="static" data-bs-keyboard="false">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Welcome to Watch Together!</h4>
            </div>

            <div class="modal-body">
                <h5>Host a new session</h5>
                <p>You will be given an invite code to share with your partner·s.</p>
                <span class="btn btn-primary" id="host-start" onclick="hostSession();">Start</span>

                <hr>

                <h5>Join an existing session</h5>
                <p>Enter the invite code your partner has given you:</p>
                <div style="display:grid;grid-template-columns: repeat(8, 1fr);grid-gap:10px;">
                    <input type="text" class="form-control" id="invite-input-1" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-2" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-3" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-4" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-5" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-6" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-7" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                    <input type="text" class="form-control" id="invite-input-8" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1">
                </div>
            </div>
        </div>
    </div>
</div>
<script>
    document.getElementById("invite-input-1").disabled = true;
    document.getElementById("invite-input-2").disabled = true;
    document.getElementById("invite-input-3").disabled = true;
    document.getElementById("invite-input-4").disabled = true;
    document.getElementById("invite-input-5").disabled = true;
    document.getElementById("invite-input-6").disabled = true;
    document.getElementById("invite-input-7").disabled = true;
    document.getElementById("invite-input-8").disabled = true;
    document.getElementById("host-start").classList.add("disabled");

    let modal = new bootstrap.Modal(document.getElementById('welcome'));
    modal.show();
    document.getElementById("invite-input-1").focus();

    window.modalAdd = new bootstrap.Modal(document.getElementById('add'));

    function openAdd() {
        modalAdd.show();
    }

    function closeAdd() {
        document.getElementById("add-id").value = "";
        modalAdd.hide();
    }

    function doAdd() {
        document.getElementById("add-id").disabled = true;
        document.getElementById("add-btn-yes").classList.add("disabled");
        document.getElementById("add-btn-no").classList.add("disabled");

        socket.send(JSON.stringify({
            task: "UPDATE_QUEUE",
            payload: {
                operation: "+",
                video: document.getElementById("add-id").value
            }
        }))
    }

    function toPrettyTime(seconds) {
        let parts = new Date(seconds * 1000).toUTCString().split(" ")[4].split(":");
        parts[0] = parseInt(parts[0]).toString();

        if (parts[0] === "0") {
            parts[1] = parseInt(parts[1]).toString();
            parts.shift();
        }

        return parts.join(":");
    }

    function processInviteCode(event) {
        let i1 = document.getElementById("invite-input-1").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i2 = document.getElementById("invite-input-2").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i3 = document.getElementById("invite-input-3").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i4 = document.getElementById("invite-input-4").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i5 = document.getElementById("invite-input-5").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i6 = document.getElementById("invite-input-6").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i7 = document.getElementById("invite-input-7").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");
        let i8 = document.getElementById("invite-input-8").value.trim().toLowerCase().replace(/[^\da-z]+/gm, "");

        document.getElementById("invite-input-1").value = i1;
        document.getElementById("invite-input-2").value = i2;
        document.getElementById("invite-input-3").value = i3;
        document.getElementById("invite-input-4").value = i4;
        document.getElementById("invite-input-5").value = i5;
        document.getElementById("invite-input-6").value = i6;
        document.getElementById("invite-input-7").value = i7;
        document.getElementById("invite-input-8").value = i8;

        if (i8 === "") {
            document.getElementById("invite-input-8").focus();
        }
        if (i7 === "") {
            document.getElementById("invite-input-7").focus();
        }
        if (i6 === "") {
            document.getElementById("invite-input-6").focus();
        }
        if (i5 === "") {
            document.getElementById("invite-input-5").focus();
        }
        if (i4 === "") {
            document.getElementById("invite-input-4").focus();
        }
        if (i3 === "") {
            document.getElementById("invite-input-3").focus();
        }
        if (i2 === "") {
            document.getElementById("invite-input-2").focus();
        }
        if (i1 === "") {
            document.getElementById("invite-input-1").focus();
        }

        if (event instanceof KeyboardEvent) {
            if (event.code === "Backspace" && event.type === "keydown") {
                let el = event.target;
                let ep = document.getElementById("invite-input-" + (el['id'].split("-")[2] - 1));

                if (ep !== null) {
                    if (el.value.trim() === "") {
                        ep.value = "";
                        ep.focus();
                    } else {
                        el.value = "";
                        ep.focus();
                    }
                }
            }
        }

        if (i1.length === 1 && i2.length === 1 && i3.length === 1 && i4.length === 1 && i5.length === 1 && i6.length === 1 && i7.length === 1 && i8.length === 1 && ((event.type && event.type === "keyup") || !event.type)) {
            joinSession(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8);
        }
    }

    async function joinSession(code) {
        document.getElementById("invite-input-1").disabled = true;
        document.getElementById("invite-input-2").disabled = true;
        document.getElementById("invite-input-3").disabled = true;
        document.getElementById("invite-input-4").disabled = true;
        document.getElementById("invite-input-5").disabled = true;
        document.getElementById("invite-input-6").disabled = true;
        document.getElementById("invite-input-7").disabled = true;
        document.getElementById("invite-input-8").disabled = true;
        document.getElementById("host-start").classList.add("disabled");

        window.inviteCode = code;

        startSession();
    }

    async function hostSession() {
        document.getElementById("invite-input-1").disabled = true;
        document.getElementById("invite-input-2").disabled = true;
        document.getElementById("invite-input-3").disabled = true;
        document.getElementById("invite-input-4").disabled = true;
        document.getElementById("invite-input-5").disabled = true;
        document.getElementById("invite-input-6").disabled = true;
        document.getElementById("invite-input-7").disabled = true;
        document.getElementById("invite-input-8").disabled = true;
        document.getElementById("host-start").classList.add("disabled");

        window.inviteCode = null;

        startSession();
    }

    window.terminated = false;
    window.users = null;
    window.queue = null;
    window.segments = [];
    window.originalTitle = document.title;

    async function connect() {
        window.identity = JSON.parse(await (await window.fetch("/api/me")).text());
        window.sessionToken = await (await window.fetch("/api/token")).text();

        window.socket = new WebSocket("<?= $WebSocketAddress ?>");

        socket.onclose = (event) => {
            console.log("[ws:close] ",event);

            if (window.terminated) {
                modal.hide();
                return;
            }

            if (event.wasClean) {
                document.getElementById('error-message').innerText = "Connection closed";
            } else {
                if (event.reason) {
                    document.getElementById('error-message').innerText = "Connection closed unexpectedly with code " + event.code + ": " + event.reason;
                } else {
                    document.getElementById('error-message').innerText = "Connection closed unexpectedly with code " + event.code;
                }
            }

            let errorModal = new bootstrap.Modal(document.getElementById('error'));
            errorModal.show();
            modal.hide();
        }

        socket.onmessage = async (event) => {
            let data = JSON.parse(event.data);
            if (data.task !== "HEARTBEAT_ACK") console.log("[ws:message]", data);

            if (data.task === "CONFIG") {
                document.getElementById("invite-input-1").disabled = false;
                document.getElementById("invite-input-2").disabled = false;
                document.getElementById("invite-input-3").disabled = false;
                document.getElementById("invite-input-4").disabled = false;
                document.getElementById("invite-input-5").disabled = false;
                document.getElementById("invite-input-6").disabled = false;
                document.getElementById("invite-input-7").disabled = false;
                document.getElementById("invite-input-8").disabled = false;
                document.getElementById("host-start").classList.remove("disabled");

                setInterval(() => {
                    socket.send(JSON.stringify({
                        task: "HEARTBEAT",
                        payload: {
                            videoPositon: document.getElementById("video").currentTime
                        }
                    }));
                }, data.payload.heartbeatInterval);

                return;
            }

            if (data.task === "SESSION") {
                window.users = data.payload.users;
                window.queue = data.payload.queue;

                updateQueue();

                document.getElementById("participants").innerHTML = "";
                for (let user of data.payload.users) {
                    document.getElementById("participants").innerHTML += "<li class='list-group-item'>" + user.id + "<span id='user-" + user.id + "-ping' class='bg-primary user-ping'></span></li>";
                }

                document.getElementById("invite-code").innerText = data.payload.code;
            }

            if (data.task === "VIDEO_UPDATE") {
                if (data.payload.id !== null) {
                    try {
                        window.segments = JSON.parse(await (await window.fetch("https://sponsor.ajay.app/api/skipSegments/" + data.payload.sha.substring(0, 32) + '?categories=["sponsor","intro","outro","music_offtopic"]')).text()).filter(i => i['videoID'] === data.payload.id)[0].segments;
                        notification("This video is enhanced by smart playback", "success");
                    } catch (e) {
                        notification("This video is not compatible with smart playback", "warning");
                        window.segments = [];
                    }
                }

                if (data.payload.url) document.getElementById("video").src = data.payload.url;

                if (data.payload.state === 1) {
                    document.getElementById("video").play();
                } else if (!!data.payload.state) {
                    document.getElementById("video").pause();
                }

                if (data.payload.title && data.payload.title.trim() !== "") {
                    document.getElementById("video-title").innerText = data.payload.title;
                    document.title = data.payload.title + " · " + window.originalTitle;
                } else {
                    if (data.payload.title) {
                        document.getElementById("video-title").innerText = "";
                        document.title = window.originalTitle;
                    }
                }

                document.getElementById("video").currentTime = data.payload.position;
            }

            if (data.task === "UPDATE_USERS") {
                window.users = data.payload.users;

                document.getElementById("participants").innerHTML = "";
                for (let user of data.payload.users) {
                    document.getElementById("participants").innerHTML += "<li class='list-group-item'>" + user.id + "<span id='user-" + user.id + "-ping' class='bg-primary user-ping'></span></li>";
                }
            }

            if (data.task === "UPDATE_QUEUE") {
                window.queue = data.payload.queue;

                if (data.payload.poster === identity.id) {
                    document.getElementById("add-id").disabled = false;
                    document.getElementById("add-id").value = "";
                    document.getElementById("add-btn-yes").classList.remove("disabled");
                    document.getElementById("add-btn-no").classList.remove("disabled");

                    modalAdd.hide();
                }

                updateQueue();
            }

            if (data.task === "HEARTBEAT_ACK") {
                for (let user of Object.keys(data.payload.delays)) {
                    let abs = Math.abs(data.payload.delays[user]);
                    let color = "primary";

                    if (abs >= 500)  color = "warning";
                    if (abs >= 1000) color = "danger";
                    if (abs < 500)   color = "success";

                    let sign = "±";
                    if (data.payload.delays[user] < 0) sign = "-";
                    if (data.payload.delays[user] > 0) sign = "+";

                    document.getElementById("user-" + user + "-ping").title = sign + abs + " ms";
                    document.getElementById("user-" + user + "-ping").classList.remove("bg-primary");
                    document.getElementById("user-" + user + "-ping").classList.remove("bg-danger");
                    document.getElementById("user-" + user + "-ping").classList.remove("bg-warning");
                    document.getElementById("user-" + user + "-ping").classList.remove("bg-green");
                    document.getElementById("user-" + user + "-ping").classList.add("bg-" + color);
                }
            }

            if (data.task === "TERMINATE" || data.task === "FAILURE") {
                window.terminated = true;

                if (data.payload.code) {
                    if (data.payload.reason) {
                        document.getElementById('error-message').innerText = data.payload.code + ": " + data.payload.reason;
                        let errorModal = new bootstrap.Modal(document.getElementById('error'));
                        errorModal.show();
                    } else {
                        document.getElementById('error-message').innerText = data.payload.code;
                        let errorModal = new bootstrap.Modal(document.getElementById('error'));
                        errorModal.show();
                    }
                } else {
                    document.getElementById('error-message').innerText = "Error";
                    let errorModal = new bootstrap.Modal(document.getElementById('error'));
                    errorModal.show();
                }

                if (data.task === "FAILURE") socket.close();
            }
        }

        socket.onopen = (event) => {
            console.log("[ws:open]  ", event);

            socket.send(JSON.stringify({
                task: "IDENTIFY",
                payload: {
                    token: window.sessionToken
                }
            }))
        }
    }

    function updateQueue() {
        document.getElementById("queue").innerHTML = "";

        for (let video of window.queue) {
            document.getElementById("queue").innerHTML += `
<li class="list-group-item list-group-item-action video-queue-item">
    <div class="video-queue-item-part video-queue-item-metadata">
        <div>
            <div class="video-queue-item-title-outer">
                <span class="video-queue-item-title">${video.title}</span>
            </div>
            <div class="video-queue-item-author-outer">
                <span class="video-queue-item-author text-muted">${video.author}</span>
            </div>
        </div>
    </div>
    <div class="video-queue-item-part text-muted">${video['duration_pretty'] ?? toPrettyTime(video.duration)}</div>
</li>
`;
        }
    }

    function startSession() {
        socket.send(JSON.stringify({
            task: "SESSION",
            payload: {
                id: window.inviteCode
            }
        }))

        modal.hide();
    }

    connect();
</script>

<div style="height:calc(100vh - 60px);display:grid;grid-template-columns: 3fr 1.5fr;" id="app-container">
    <div style="height:calc(100vh - 60px);">
        <video id="video" style="width:100%;height:calc(100vh - 60px);"></video>
    </div>
    <script>

        document.getElementById("video").onplay = () => {
            console.log("play");

            if (socket) socket.send(JSON.stringify({
                task: "VIDEO_UPDATE",
                payload: {
                    state: 1,
                    position: document.getElementById("video").currentTime
                }
            }));
        }

        document.getElementById("video").onpause = () => {
            console.log("pause");

            if (socket) socket.send(JSON.stringify({
                task: "VIDEO_UPDATE",
                payload: {
                    state: 0,
                    position: document.getElementById("video").currentTime
                }
            }));
        }

        let controlsFadeInterval;
        document.body.classList.remove("hide-controls");

        document.body.onmousemove = document.body.onmouseup = () => {
            document.body.classList.remove("hide-controls");
            try { clearTimeout(controlsFadeInterval) } catch (e) {}
            controlsFadeInterval = setTimeout(() => {
                if (!document.getElementById("video").paused) document.body.classList.add("hide-controls");
            }, 5000);
        }

    </script>
    <div class="container" id="sidebar" style="border-left:1px solid rgba(255, 255, 255, .25);">
        <br>
        <h4>Participants</h4>
        <ul class="list-group" id="participants"></ul>
        <p style="margin-top:10px;margin-bottom:0;">Invite new participants with this invite code: <code id="invite-code">--------</code></p>

        <br>
        <h4>Queue</h4>
        <ul class="list-group" id="queue"></ul>
        <p style="margin-top:10px;margin-bottom:0;">
            <span class="btn btn-primary" onclick="openAdd()">Add video</span>
        </p>
    </div>
</div>

<div id="notification" style="z-index:999;padding: 15px 20px;font-size: 20px;position: fixed;top: 60px;left:0;text-shadow: 0 0 10px black;">
    Notification.
</div>
<a onclick="controls_skipIntroOutro();" id="skipper">Skip</a>
<div id="controls-outer" style="position:fixed;background: linear-gradient(180deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 35%, rgba(0,0,0,0) 65%, rgba(0,0,0,0.5) 100%);top: 60px;left: 0;right: 0;bottom: 0;">
    <div id="video-title" style="padding: 15px 20px;font-size: 20px;"></div>
    <div id="controls" style="position: fixed;bottom: 20px;height: 50px;left: 15px;display:grid;grid-template-columns: max-content 1fr max-content;">
        <div>
            <a id="control-play" onclick="controls_playPause();" class="control-item">
                <img alt="" src="/assets/icons/together/play.svg" id="control-play-icon" class="control-icon">
            </a>
            <a id="control-next" onclick="controls_next();" class="control-item">
                <img alt="" src="/assets/icons/together/next.svg" id="control-next-icon" class="control-icon">
            </a>
        </div>
        <div style="display: flex;align-items: center;padding-left:15px;padding-right:15px;">
            <div style="padding: 22px 0;width:100%;" id="seek-bar">
                <div style="height:6px;width:100%;background:rgba(255, 255, 255, .1);border-radius:999px;pointer-events: none;">
                    <div style="height:6px;width:0;background:rgba(255, 255, 255, .75);border-radius:999px;" id="control-progress"></div>
                </div>
            </div>
        </div>
        <div>
            <div style="display: inline-flex;align-items: center;">
                <span id="time-remaining" style="font-family:monospace;">-0:00</span>
            </div>
            <a id="control-full" onclick="controls_fullscreen();" class="control-item">
                <img alt="" src="/assets/icons/together/full-on.svg" id="control-full-icon" class="control-icon">
            </a>
        </div>
    </div>
    <div id="seek-bar-dot" style="position: fixed;bottom: 38px;width: 12px;height: 12px;background: white;border-radius: 999px;pointer-events: none;display:none;"></div>
</div>
<script>
    document.getElementById("controls").style.width = (document.getElementById("video").clientWidth - 30) + "px";
    document.getElementById("controls-outer").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px";
    document.getElementById("notification").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px";

    let notificationDecayTimeout;

    function notification(text, color) {
        document.getElementById("notification").innerText = text;
        document.getElementById("notification").classList.add("text-" + color);
        document.body.classList.add("notification");

        try { clearTimeout(notificationDecayTimeout) } catch (e) {}

        notificationDecayTimeout = setTimeout(() => {
            document.body.classList.remove("notification");
            setTimeout(() => {
                document.getElementById("notification").innerText = "Notification.";
                document.getElementById("notification").classList.remove("text-" + color);
            }, 200);
        }, 5000)
    }

    window.onresize = () => {
        document.getElementById("controls").style.width = (document.getElementById("video").clientWidth - 30) + "px";
        document.getElementById("controls-outer").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px";
        document.getElementById("notification").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px";
    }

    function controls_playPause() {
        if (document.getElementById("video").src.trim() === "") return;

        if (document.getElementById("video").paused) {
            document.getElementById("video").play();
        } else {
            document.getElementById("video").pause();
        }
    }

    function controls_fullscreen() {
        if (document.fullscreen) {
            document.exitFullscreen();
        } else {
            document.documentElement.requestFullscreen();
        }
    }

    function controls_next() {
        document.getElementById("video").play();
        document.getElementById("video").currentTime = document.getElementById("video").duration;
    }

    window.seeking = false;
    window.seekPosition = 0;

    document.getElementById("controls-outer").onclick = (event) => {
        if (event.target === document.getElementById("controls-outer")) controls_playPause();
    }

    document.getElementById("controls-outer").ondblclick = (event) => {
        if (event.target === document.getElementById("controls-outer")) controls_fullscreen();
    }

    document.getElementById("seek-bar").onmouseenter = () => {
        document.getElementById("seek-bar-dot").style.display = "";
        console.log("> ENTER <");
        window.seeking = true;
    }

    document.getElementById("seek-bar").onclick = (event) => {
        let percentage = (event.offsetX / document.getElementById("seek-bar").clientWidth) * 100;
        let multiplier = event.offsetX / document.getElementById("seek-bar").clientWidth;

        document.getElementById("seek-bar-dot").style.left = event.clientX + "px";

        console.log("  CLICK: ", event.offsetX, "(" + percentage.toFixed(3) + "%, " + toPrettyTime(document.getElementById("video").duration * multiplier) + ")");
        window.seekPosition = document.getElementById("video").duration * multiplier;

        document.getElementById("video").pause();
        document.getElementById("video").currentTime = window.seekPosition;
        document.getElementById("video").play();
    }

    document.getElementById("seek-bar").onmouseleave = () => {
        document.getElementById("seek-bar-dot").style.display = "none";
        console.log("< LEAVE >");
        window.seeking = false;
    }

    function controls_skipIntroOutro() {
        let skipper = null;

        for (let segment of window.segments.filter(i => i['category'] === "intro")) {
            if (segment.segment) {
                if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) {
                    skipper = segment.segment[1];
                }
            }
        }
        for (let segment of window.segments.filter(i => i['category'] === "outro")) {
            if (segment.segment) {
                if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) {
                    skipper = segment.segment[1];
                }
            }
        }

        if (skipper) {
            document.getElementById("video").pause();
            document.getElementById("video").currentTime = skipper;
            document.getElementById("video").play();
        }
    }

    document.getElementById("seek-bar").onmousemove = (event) => {
        let percentage = (event.offsetX / document.getElementById("seek-bar").clientWidth) * 100;
        let multiplier = event.offsetX / document.getElementById("seek-bar").clientWidth;

        document.getElementById("seek-bar-dot").style.left = event.clientX + "px";

        console.log("  POS:   ", event.offsetX, "(" + percentage.toFixed(3) + "%, " + toPrettyTime(document.getElementById("video").duration * multiplier) + ")");
        window.seekPosition = document.getElementById("video").duration * multiplier;
    }

    setInterval(() => {
        if (document.getElementById("video").src.trim() === "") {
            document.getElementById("controls").style.display = "none";
        } else {
            document.getElementById("controls").style.display = "grid";
        }

        document.getElementById("control-play-icon").src = document.getElementById("video").paused ? "/assets/icons/together/play.svg" : "/assets/icons/together/pause.svg";
        document.getElementById("control-full-icon").src = document.fullscreen ? "/assets/icons/together/full-off.svg" : "/assets/icons/together/full-on.svg";
        document.getElementById("control-progress").style.width = ((document.getElementById("video").currentTime / document.getElementById("video").duration) * 100) + "%";

        if (document.fullscreen) {
            document.body.classList.add("fullscreen");
        } else {
            document.body.classList.remove("fullscreen");
        }

        if (window.seeking) {
            document.getElementById("time-remaining").classList.add("text-warning");
            document.getElementById("time-remaining").innerText = toPrettyTime(window.seekPosition);
        } else {
            document.getElementById("time-remaining").classList.remove("text-warning");

            if (!isNaN(document.getElementById("video").duration)) {
                document.getElementById("time-remaining").innerText = "-" + toPrettyTime(Math.round(document.getElementById("video").duration) - Math.round(document.getElementById("video").currentTime));
            } else {
                document.getElementById("time-remaining").innerText = "-0:00";
            }
        }
    })

    setInterval(() => {
        if (!document.getElementById("video").paused) {
            for (let segment of window.segments.filter(i => i['category'] === "sponsor")) {
                if (segment.segment) {
                    if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) {
                        document.getElementById("video").pause();
                        document.getElementById("video").currentTime = segment.segment[1];
                        document.getElementById("video").play();
                        notification("Advert skipped by smart playback", "primary");
                    }
                }
            }

            let skipper = false;

            for (let segment of window.segments.filter(i => i['category'] === "intro")) {
                if (segment.segment) {
                    if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) {
                        skipper = true;
                        document.getElementById("skipper").innerText = "Skip intro";
                        document.body.classList.add("skipper");
                    }
                }
            }
            for (let segment of window.segments.filter(i => i['category'] === "outro")) {
                if (segment.segment) {
                    if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) {
                        skipper = true;
                        document.getElementById("skipper").innerText = "Skip to the end";
                        document.body.classList.add("skipper");
                    }
                }
            }

            if (!skipper) {
                document.body.classList.remove("skipper");
            }
        }
    })
</script>