<!DOCTYPE html>
<html lang="fr">
<head>
    <title>Obscreen</title>
    <meta name="robots" content="noindex, nofollow">
    <meta name="google" content="notranslate">
    <link rel="shortcut icon" href="{{ STATIC_PREFIX }}/favicon.ico">
    {% if slide_animation_enabled.eval() %}
        <link rel="stylesheet" href="{{ STATIC_PREFIX }}css/lib/animate.min.css" />
    {% endif %}
    <style>
        html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; background-color: white; display: flex; flex-direction: row; justify-content: center; align-items: center; }
        .slide { display: flex; flex-direction: row; justify-content: center; align-items: center; background: black; }
        .slide, iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding-top: 0; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; }
        .slide iframe { background: white; }
        .slide img, .slide video { height: 100%; }
    </style>
    <script type="application/javascript" src="{{ STATIC_PREFIX }}js/utils.js"></script>
    <script type="application/javascript" src="{{ STATIC_PREFIX }}js/lib/is-cron-now.js"></script>
</head>
<body>
<div id="IntroSlide" class="slide" style="z-index: 10000;">
    {% if default_slide_duration.eval() > 0 %}
        <iframe src="/player/default"></iframe>
    {% endif %}
</div>
<div id="NotificationSlide" class="slide" style="z-index: 0;">

</div>
<div id="FirstSlide" class="slide slide-loop" style="z-index: 500;">

</div>
<div id="SecondSlide" class="slide slide-loop" style="z-index: 1000;">

</div>

<script type="text/javascript">
    // Backend config
    let items = {{items | safe}};
    const introDuration = {{ default_slide_duration.eval() * 1000 }};
    const playlistCheckResolutionMs = {{ polling_interval.eval() * 1000 }};

    // Backend flag updates
    let needHardRefresh = null;

    // Frontend config
    const syncWithTime = items['time_sync'];
    const tickRefreshResolutionMs = 100;

    // Frontend flag updates
    let hasMoveOnce = false;
    let forcePreload = false;

    // Player states infos
    const PLAY_STATE_PLAYING = 0, PLAY_STATE_PAUSE = 1;
    let playState = PLAY_STATE_PLAYING;
    const isPlaying = function() {return playState === PLAY_STATE_PLAYING;};
    const isPaused = function() {return playState === PLAY_STATE_PAUSE;};
    let pauseClockValue = null;

    // Animations
    const animate = {{ 'true' if slide_animation_enabled.eval() else 'false' }};
    const animate_speed = "animate__{{ slide_animation_speed.eval()|default("normal") }}";
    const animation_speed_duration = {{ animation_speed_duration[slide_animation_speed.eval()] if slide_animation_enabled.eval() else 0 }};
    const animate_transitions = [
        "animate__{{ slide_animation_entrance_effect.eval()|default("fadeIn") }}",
        "animate__{{ slide_animation_exit_effect.eval()|default("none") }}"
    ];

    // Slide flow management
    const SLIDE_TOP_Z = '1000';
    const SLIDE_BOTTOM_Z = '500';
    let clockValue = 0;
    let curItemIndex = -1;
    let secondsBeforeNext = 0;
    const introSlide = document.getElementById('IntroSlide');
    const notificationSlide = document.getElementById('NotificationSlide');
    const firstSlide = document.getElementById('FirstSlide');
    const secondSlide = document.getElementById('SecondSlide');
    let curSlide = secondSlide;
    let nextSlide = firstSlide;
    let notificationItemIndex = null;

    // Functions
    const itemCheck = setInterval(function() {
        fetch('/player/playlist' + (items['playlist_id'] ? '/use/'+items['playlist_id'] : '')).then(function(response) {
            if (response.ok) {
                return response.json();
            }
        }).then(function(data) {
            items = data;

            if (needHardRefresh === null) {
                needHardRefresh = items.hard_refresh_request;
            } else if (needHardRefresh != items.hard_refresh_request) {
                document.location.reload();
            }
        }).catch(function(err) {
            console.error(err);
        });
    }, playlistCheckResolutionMs);

    const getLoopDuration = function() {
        let totalDuration = 0;
        for (let i = 0; i < items.loop.length; i++) {
            const item = items.loop[i];
            totalDuration += safe_duration(item);
        }
        return totalDuration;
    };

    const resume = function() {
        playState = PLAY_STATE_PLAYING;
    };

    const play = function() {
        resume();
    };

    const pause = function() {
        pauseClockValue = clockValue;
        playState = PLAY_STATE_PAUSE;
    };

    const stop = function() {
        pause();
    };

    const seek = function(timeInSeconds) {
        if (forcePreload) {
            return;
        }

        if (syncWithTime) {
            return console.warn('You can\'t seek with synced playlists');
        }

        const maxDuration = getLoopDuration();

        if (timeInSeconds > maxDuration) {
            timeInSeconds = maxDuration - 1;
            console.warn('Max duration is ' + timeInSeconds + ' seconds');
        }

        if (timeInSeconds < 0) {
            timeInSeconds = 0;
        }

        clockValue = timeInSeconds * 1000;
        forcePreload = true;
        pause();
    };

    const lookupPreviousItem = function() {
        return (curItemIndex - 1 < 0) ? items.loop[items.loop.length - 1] : items.loop[curItemIndex - 1];
    };

    const lookupNextItem = function() {
        return (curItemIndex + 1 >= items.loop.length) ? items.loop[0] : items.loop[curItemIndex + 1];
    };

    const lookupCurrentItem = function() {
        if (curItemIndex === -1) {
            return {duration: introDuration/1000};
        }

        return items.loop[curItemIndex];
    }

    const getEmptySlide = function() {
        return Array.from(document.getElementsByClassName('slide-loop')).filter(slide => slide.innerHTML.replaceAll(/\s/g,'') === '')[0];
    };

    const refreshSlidesOrder = function() {
        curSlide = Array.from(document.getElementsByClassName('slide')).filter(function(slide) {
            return getComputedStyle(slide).zIndex === SLIDE_TOP_Z;
        })[0];
        nextSlide = Array.from(document.getElementsByClassName('slide')).filter(function(slide) {
            return getComputedStyle(slide).zIndex === SLIDE_BOTTOM_Z;
        })[0];
        //console.log("top is", SLIDE_TOP_Z, curSlide, "bottom is", SLIDE_BOTTOM_Z, nextSlide)
    };

    const safe_duration = function(item) {
        if (!item) {
            return tickRefreshResolutionMs/1000;
        }
        return item.duration + Math.ceil(animation_speed_duration/1000);
    };

    const main = function() {
        setInterval(checkAndMoveNotifications, 1000);
        setTimeout(function() {
            if (items.loop.length === 0) {
                return setTimeout(main, 5000);
            }
            introSlide.remove();
            setInterval(checkAndMoveSlide, tickRefreshResolutionMs);
        }, introDuration);
    };

    const preloadSlide = function(slide, item) {
        //console.log('Preload', slide, item.name)
        const element = document.getElementById(slide);
        const callbackReady = function() {};
        loadContent(element, callbackReady, item);
    };

    const tickClockValue = function() {
        if (isPaused()) {
            return pauseClockValue;
        }
        if (syncWithTime) {
            clockValue = Date.now();
        } else {
            clockValue += tickRefreshResolutionMs;
        }
    };

    const checkAndMoveSlide = function() {
        tickClockValue();
        const timeInCurrentLoop = (clockValue/1000) % getLoopDuration();
        let accumulatedTime = 0;

        for (let i = 0; i < items.loop.length; i++) {
            const item = items.loop[i];

            if (i === curItemIndex) {
                secondsBeforeNext = accumulatedTime + safe_duration(item) - timeInCurrentLoop;
                //console.log("remaining:", secondsBeforeNext, "clock:",clockValue, curItemIndex);
            }

            if (timeInCurrentLoop < accumulatedTime + safe_duration(item)) {
                if (curItemIndex !== i) {
                    //console.log('change to ', i , item.name)
                    curItemIndex = i;

                    const emptySlide = getEmptySlide();
                    if ((emptySlide && !hasMoveOnce) || forcePreload) {
                        //console.log('init preload');
                        if (!hasMoveOnce && syncWithTime) {
                            if (accumulatedTime + safe_duration(item) - timeInCurrentLoop < 1) {
                                // Prevent glitch when syncWithTime for first init
                                curItemIndex = -1;
                                continue;
                            }
                        }
                        const slide = emptySlide ? emptySlide : nextSlide;
                        preloadSlide(slide.attributes['id'].value, item);
                        hasMoveOnce = true;
                    }
                    moveToNextSlide();
                }
                break;
            }
            accumulatedTime += safe_duration(item);
        }
    }

    function moveToNextSlide() {
        refreshSlidesOrder();
        nextSlide.style.zIndex = SLIDE_TOP_Z; // first
        curSlide.style.zIndex = SLIDE_BOTTOM_Z; // second

        //console.log("curSlide", curSlide.attributes['id'].value, curSlide.style.zIndex, "to", "next", nextSlide.attributes['id'].value, nextSlide.style.zIndex);
        //console.log("###");

         const loadingNextSlide = function() {
            if (forcePreload) {
                forcePreload = false;
                play();
            }

            if (isPaused() && !syncWithTime) {
                return setTimeout(loadingNextSlide, 500);
            }

            refreshSlidesOrder();
            preloadSlide(nextSlide.attributes['id'].value, lookupNextItem());
        };

        if (animate) {
            nextSlide.classList.add('animate__animated', animate_transitions[0], animate_speed);
            nextSlide.onanimationend = function() {
                nextSlide.classList.remove(animate_transitions[0], animate_speed);
                loadingNextSlide();
            };

            curSlide.classList.add('animate__animated', animate_transitions[1], animate_speed);
            curSlide.onanimationend = function() {
                curSlide.classList.remove(animate_transitions[1], animate_speed);
            };
        } else {
            loadingNextSlide();
        }
    }

    const loadContent = function(element, callbackReady, item) {
        switch (item.type) {
            case 'url':
                loadUrl(element, callbackReady, item);
                break;
            case 'picture':
                loadPicture(element, callbackReady, item);
                break;
            case 'video':
                loadVideo(element, callbackReady, item);
                break;
            case 'youtube':
                loadYoutube(element, callbackReady, item);
                break;
            default:
                loadUrl(element, callbackReady, item);
                break;
        }
    }

    const loadUrl = function(element, callbackReady, item) {
        element.innerHTML = `<iframe src="${item.location}"></iframe>`;
        callbackReady(function() {});
    }

    const loadPicture = function(element, callbackReady, item) {
        element.innerHTML = `<img src="/${item.location}" alt="" />`;
        callbackReady(function() {});
    };

    const loadYoutube = function(element, callbackReady, item) {
        element.innerHTML = `youtube`;
        callbackReady(function() {});

        const loadingDelayMs = 1000;
        let delayNoisyContentJIT = Math.max(100, (lookupCurrentItem().duration * 1000) - loadingDelayMs);
        delayNoisyContentJIT = lookupCurrentItem().id !== item.id ? delayNoisyContentJIT : 0;

        const autoplayLoader = function() {
            if (secondsBeforeNext * 1000 > loadingDelayMs) {
                return setTimeout(autoplayLoader, 500);
            }

            if (element.innerHTML === 'youtube') {
                element.innerHTML = `<iframe src="https://www.youtube.com/embed/${item.location}?version=3&autoplay=1&showinfo=0&controls=0&modestbranding=1&fs=1&rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>`;
            }
        }
        setTimeout(autoplayLoader, delayNoisyContentJIT);
    };

    const loadVideo = function(element, callbackReady, item) {
        element.innerHTML = `<video><source src=/${item.location} type="video/mp4" /></video>`;
        const video = element.querySelector('video');
        callbackReady(function() {});

        const loadingDelayMs = 1000;
        let delayNoisyContentJIT = Math.max(100, (lookupCurrentItem().duration * 1000) - loadingDelayMs);
        delayNoisyContentJIT = lookupCurrentItem().id !== item.id ? delayNoisyContentJIT : 0;

        video.addEventListener('loadedmetadata', function() {
            if (item.duration !== video.duration) {
                console.warn('Given duration ' + item.duration + 's is different from video file ' + Math.ceil(video.duration) + 's');
            }
        });

        const autoplayLoader = function() {
            if (secondsBeforeNext * 1000 > loadingDelayMs) {
                return setTimeout(autoplayLoader, 500);
            }

            if (element.innerHTML.match('<video>')) {
                video.play();
            }
        }
        setTimeout(autoplayLoader, delayNoisyContentJIT);
    }

    const checkAndMoveNotifications = function() {
        for (let i = 0; i < items.notifications.length; i++) {
            const item = items.notifications[i];

            const now = new Date();
            const isFullyElapsedMinute = (new Date()).getSeconds() === 0;
            const hasCron = item.cron_schedule && item.cron_schedule.length > 0;
            const hasCronEnd = item.cron_schedule_end && item.cron_schedule_end.length > 0;
            const hasDateTime = hasCron && validateCronDateTime(item.cron_schedule);
            const hasDateTimeEnd = hasCronEnd && validateCronDateTime(item.cron_schedule_end);

            if (notificationItemIndex !== i && hasDateTime) {
                const startDate = cronToDateTimeObject(item.cron_schedule);
                const endDate = hasDateTimeEnd ? cronToDateTimeObject(item.cron_schedule_end) : modifyDate(startDate, item.duration);

                if (now >= startDate && now < endDate) {
                    moveToNotificationSlide(i);
                }
            }

            if (notificationItemIndex === i && hasDateTime) {
                const startDate = cronToDateTimeObject(item.cron_schedule);
                const endDate = hasDateTimeEnd ? cronToDateTimeObject(item.cron_schedule_end) : modifyDate(startDate, item.duration);

                if (now >= endDate) {
                    stopNotificationSlide();
                }
            }

            if (notificationItemIndex !== i && isFullyElapsedMinute && hasCron && cron.isActive(item.cron_schedule)) {
                moveToNotificationSlide(i);
            }

            if (notificationItemIndex === i && isFullyElapsedMinute && hasCronEnd && cron.isActive(item.cron_schedule_end)) {
                stopNotificationSlide();
            }
        }
    };

    const moveToNotificationSlide = function(notificationSlideIndex) {
        const item = items.notifications[notificationSlideIndex];
        notificationItemIndex = notificationSlideIndex;
        pause();
        const callbackReady = function() {
            notificationSlide.style.zIndex = '20000';
            if (!item.cron_schedule_end) {
                setTimeout(function() {
                    stopNotificationSlide();
                }, safe_duration(item) * 1000);
            }
        };
        loadContent(notificationSlide, callbackReady, item);
    };

    const stopNotificationSlide = function() {
        notificationItemIndex = null;
        notificationSlide.style.zIndex = '0';
        notificationSlide.innerHTML = '';
        play();
    };

    main();
</script>
</body>
</html>
