// @license http://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
let ffmpeg = null;
(function () {
    if (Hls.isSupported()) {

        var downloadsEnabled = document.cookie.split("; ").find((row) => row.startsWith("ffmpeg_video_downloads="))?.split("=")[1] == "on";

        var videoSources = document.querySelectorAll("video source[type='application/vnd.apple.mpegurl']");
        videoSources.forEach(function (source) {
            var playlist = source.src;

            var oldVideo = source.parentNode;
            var autoplay = oldVideo.classList.contains("hls_autoplay");

            // If HLS is supported natively then don't use hls.js
            if (oldVideo.canPlayType(source.type) === "probably") {
                if (autoplay) {
                    oldVideo.play();
                }
                return;
            }

            // Replace video with copy that will have all "source" elements removed
            var newVideo = oldVideo.cloneNode(true);
            var allSources = newVideo.querySelectorAll("source");
            allSources.forEach(function (source) {
                source.remove();
            });

            // Empty source to enable play event
            newVideo.src = "about:blank";

            oldVideo.parentNode.replaceChild(newVideo, oldVideo);

            function initializeHls() {
                newVideo.removeEventListener('play', initializeHls);
                var hls = new Hls({ autoStartLoad: false });
                hls.loadSource(playlist);
                hls.attachMedia(newVideo);
                hls.on(Hls.Events.MANIFEST_PARSED, function () {
                    hls.loadLevel = hls.levels.length - 1;
                    var availableLevels = hls.levels.map(function(level) {
                        return {
                            height: level.height,
                            width: level.width,
                            bitrate: level.bitrate,
                        };
                    });

                    addQualitySelector(newVideo, hls, availableLevels);
                    if (downloadsEnabled){ addVideoDownload(newVideo, hls); }
                    hls.startLoad();
                    newVideo.play();
                });

                hls.on(Hls.Events.ERROR, function (event, data) {
                    var errorType = data.type;
                    var errorFatal = data.fatal;
                    if (errorFatal) {
                        switch (errorType) {
                            case Hls.ErrorType.NETWORK_ERROR:
                                hls.startLoad();
                                break;
                            case Hls.ErrorType.MEDIA_ERROR:
                                hls.recoverMediaError();
                                break;
                            default:
                                hls.destroy();
                                break;
                        }
                    }

                    console.error("HLS error", data);
                });
            }

            if (downloadsEnabled){
                const { fetchFile } = FFmpegUtil;
                const { FFmpeg } = FFmpegWASM;

                function addVideoDownload(videoElement, hlsInstance) {
                    var mediaStream = [];
                    var downloadButton = document.createElement('button');
                    downloadButton.classList.add('video-options','download');
                    downloadButton.innerText = "⏳"
                    const mergeStreams = async () => {
                        if (ffmpeg === null) {
                          ffmpeg = new FFmpeg();
                          await ffmpeg.load({
                            coreURL: "/ffmpeg/ffmpeg-core.js",
                          });
                          ffmpeg.on("log", ({ message }) => {
                            console.log(message); // This is quite noisy but i will include it
                          })
                          ffmpeg.on("progress", ({ progress, time }) => { // Progress TODO: show progress ring around button not just ⏳
                            // console.log("ffmpeg prog:",progress * 100) 
                          });
                        }
                        // Combine Video Audio Streams
                        await ffmpeg.writeFile("video", await fetchFile(concatBlob(mediaStream['video'])));
                        await ffmpeg.writeFile("audio", await fetchFile(concatBlob(mediaStream['audio'])));
                        console.time('ffmpeg-exec');
                        await ffmpeg.exec(['-i', "video", '-i', "audio",'-c:v', "copy", '-c:a', "aac",  'output.mp4']);
                        console.timeEnd('ffmpeg-exec')
    
                        // Save
                        const toSlug = (str) => {
                            return str
                              .normalize('NFD')
                              .replace(/[\u0300-\u036f]/g, '')
                              .replace(/[\W_]+/g, '-')
                              .toLowerCase()
                              .replace(/^-+|-+$/g, '');
                        }
    
                        var filename = toSlug(videoElement.parentNode.parentNode.id || document.title)
                        const data = await ffmpeg.readFile('output.mp4');
                        saveAs(new Blob([data.buffer]),filename, {type: 'video/mp4'});
                        return
                    }
                    function saveAs(blob, filename) { // Yeah ok...
                        var url = URL.createObjectURL(blob);
                        var a = document.createElement("a");
                        document.body.appendChild(a);
                        a.style = "display: none";
                        a.href = url;
                        a.download = filename;
                        a.click();
                        window.URL.revokeObjectURL(url);
                    }
                    function concatBlob(inputArray) {
                        var totalLength = inputArray.reduce(function (prev, cur) {
                            return prev + cur.length
                        }, 0);
                        var result = new Uint8Array(totalLength);
                        var offset = 0;
                        inputArray.forEach(function (element) {
                            result.set(element, offset);
                            offset += element.length;
                        });
                        return new Blob([result], {
                            type: 'application/octet-stream'
                        });
                    }
                    function getStreams() {
                       var video = document.createElement('video');
                       video.autoplay = true;
                        var dataStreams = {
                            'video': [],
                            'audio': []
                        };
                        mediaStream = dataStreams; // Update stream
    
                        hlsInstance.on(Hls.Events.BUFFER_APPENDING, function (event, data) {
                            dataStreams[data.type].push(data.data);
                        });
                        var isDownloading = false
                        function startDownload() {
                            if (!isDownloading) { isDownloading = true } else { return }
                            downloadButton.innerText = "⏳"
                            mergeStreams()
                            .then(_ => {
                                isDownloading = false
                                downloadButton.innerText = "⭳"
                            });
                        }
    
                        function waitForLoad() {
                            const poll = resolve => {
                              if(hlsInstance._media.buffered.length === 1 &&
                                 hlsInstance._media.buffered.start(0) === 0 &&
                                 hlsInstance._media.buffered.end(0) === hlsInstance._media.duration) 
                                resolve();
                              else setTimeout(_ => poll(resolve), 400);
                            }
                            return new Promise(poll);
                        }
    
                        waitForLoad(_ => flag === true)
                        .then(_ => {
                            downloadButton.innerText = "⭳"
                            downloadButton.addEventListener('click', startDownload);
                        });
                    }
    
                    videoElement.parentNode.appendChild(downloadButton);
                    getStreams()
                }
            }

            function addQualitySelector(videoElement, hlsInstance, availableLevels) {
                var qualitySelector = document.createElement('select');
                qualitySelector.classList.add('video-options');
                var last = availableLevels.length - 1;
                availableLevels.forEach(function (level, index) {
                    var option = document.createElement('option');
                    option.value = index.toString();
                    var bitrate = (level.bitrate / 1_000).toFixed(0);
                    option.text = level.height + 'p (' + bitrate + ' kbps)';
                    if (index === last) {
                        option.selected = "selected";
                    }
                    qualitySelector.appendChild(option);
                });
                qualitySelector.selectedIndex = availableLevels.length - 1;
                qualitySelector.addEventListener('change', function () {
                    var selectedIndex = qualitySelector.selectedIndex;
                    hlsInstance.nextLevel = selectedIndex;
                    hlsInstance.startLoad();
                });

                videoElement.parentNode.appendChild(qualitySelector);
            }

            newVideo.addEventListener('play', initializeHls);

            if (autoplay) {
                newVideo.play();
            }
        });
    } else {
        var videos = document.querySelectorAll("video.hls_autoplay");
        videos.forEach(function (video) {
            video.setAttribute("autoplay", "");
        });
    }
})();
// @license-end