// Rot.js
// one rotten apple spoils the bunch

(() => {
    //Helper functions
    function getPromiseFromEvent(item, event) {
        return new Promise((resolve) => {
            const listener = () => {
                item.removeEventListener(event, listener);
                resolve();
            }
            item.addEventListener(event, listener);
        });
    }

    // MIT Licensed
    // Author: jwilson8767
    function waitUntil(selector) {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) {
                resolve(el);
                return;
            }
            new MutationObserver((mutationRecords, observer) => {
                // Query for elements matching the specified selector
                Array.from(document.querySelectorAll(selector)).forEach((element) => {
                    resolve(element);
                    //Once we have resolved we don't need the observer anymore.
                    observer.disconnect();
                });
            }).observe(document.documentElement, {
                childList: true,
                subtree: true
            });
        });
    }

    function sex(sex) {
        return sex // Sex
    }

    function rand(list) {
        return list ? list[list.length * Math.random() | 0] : null;
    }

    //Music functions
    function setMusic(url) {
        if (audio.src == url || (!url && audio.src === window.location.toString()))
            return;

        audio.pause();

        if (url) {
            console.log("Setting music: " + url);
            audio.src = url;
            playMusic();
        } else {
            // This line will cause a lot of errors.
            // Setting src to "" will cause any pending .play()s to fail.
            audio.src = "";
        }
    }

    function playMusic() {
        //Starts playing the music if it isn't muted and isn't already playing
        if (audio.src && audio.src != "" && audio.paused && !audio.muted)
            audio.play().catch(() => getPromiseFromEvent(window, 'click').then(audio.play));
    }

    function volumeSet(number) {
        audio.volume = number; //Set
        localStorage.audiovolume = number; //Save
        updateVolumeLabel();
    }
    function volumeAdd(number) {
        volumeSet(Math.min(1, Math.max(0, audio.volume + number)));
    }
    function updateVolumeLabel() {
        document.getElementById("user-audio-percentage").innerHTML = Math.round(audio.volume * 100) + "%";
    }

    //Registers a mutation observer for an element. Upon triggering and the callback
    //returning true, observation ceases. All observers are disconnected and the array is cleared.
    const observers = [];
    function waitUntilSpecial(selector, callback) {
        const newObserver = new MutationObserver((mutationRecords, observer) => {
            // Query for elements matching the specified selector
            Array.from(document.querySelectorAll(selector)).forEach((element) => {
                //Callback
                if (!callback(element))
                    return;

                //Clean up
                console.log("Cleaning up");
                for (const o of observers)
                    o.disconnect();
                observers.length = 0;
            });
        });
        observers.push(newObserver);
        newObserver.observe(document.documentElement, {
            childList: true,
            subtree: true
        });
    }

    //Theme application
    function applyMainTheme() {
        console.log("Applying main theme");
        setMusic(null);

        //<meta name="pageMusic" content="/assets/music.ogg">
        waitUntilSpecial('meta[name="pageMusic"]', (pageMusic) => {
            setMusic(pageMusic.content);
            playMusic();
            return true;
        });

        //<a id="pageMusic" href="/assets/music.ogg"></a>
        waitUntilSpecial("#pageMusic", (pageMusic) => {
            setMusic(pageMusic.getAttribute("href"));
            playMusic();
            return true;
        });
    }

    function applyUserTheme() {
        console.log("Applying user theme");
        setMusic(null);

        //Configure by post
        waitUntilSpecial(".pin", (pinnedPost) => {
            let p = pinnedPost.nextElementSibling;
            if (!p)
                return false;

            let statusBody = p.querySelector(".StatusBody");
            if (!statusBody)
                return false;

            let txtElm = statusBody.querySelector(".text");
            if (!txtElm)
                return false;

            if (txtElm.innerHTML.replace(/(<([^>]+)>)/ig, '').search(/profile them/ig) == -1)
                return false;

            const ptp = pinnedPost.nextElementSibling.querySelector(".StatusBody");
            if (!ptp)
                return false;

            const musicContainer = ptp.querySelector(".audio-container");
            if (musicContainer) {
                setMusic(rand(musicContainer.children).src);
                return true;
            } else {
                const placeholderContainer = ptp.querySelector(".placeholder-container");
                if (placeholderContainer && placeholderContainer.href.endsWith(".m4a")) {
                    setMusic(placeholderContainer.href);
                    return true;
                } else {
                    setMusic(null);
                    return false;
                }
            }
        });

        //Configure by fields
        waitUntilSpecial(".user-profile-field-name", () => {
            const fields = [...document.getElementsByClassName("user-profile-field-name")]
            if (fields.length == 0)
                return false;

            const musicFields = fields.filter((x) => x.title.toLowerCase().replace(/\s+/g, '') == "music")
            if (musicFields.length > 0) {
                setMusic(rand(musicFields).nextElementSibling.title);
                return true;
            } else {
                setMusic(null);
                return false;
            }
        });
    }

    //Switch-based monkey patching router bullshit
    let lastPath = null;
    function updateRot() {
        try {
            let newPath = window.location.pathname;
            if (lastPath == newPath)
                return;
            lastPath = newPath;

            let pathSpl = newPath.split("/");
            switch (pathSpl.length) {
                case 1:
                    applyMainTheme(); //Root
                    break;

                case 2:
                    switch (pathSpl[1]) {
                        case "about":
                        case "announcements":
                        case "lists":
                        case "bookmarks":
                            applyMainTheme();
                            break;

                        default:
                            applyUserTheme();
                            break;
                    }
                    break;

                case 3:
                    switch (pathSpl[1]) {
                        case "main":
                            applyMainTheme(); //Main timelines
                            break;

                        case "users":
                            applyUserTheme();
                            break;

                        case "notice":
                            applyMainTheme(); //Statuses
                            break;

                        default:
                            applyMainTheme();
                            break;
                    }
                    break;

                default:
                    applyMainTheme();
                    break;
            }
        } catch (e) {
            console.error(e);
        }
    }

    //Rot music player
    const audio = document.createElement("audio");
    audio.loop = true;
    audio.id = "user-music";
    audio.style = "display:none;";
    if (localStorage.audiovolume && localStorage.audiovolume >= 0 && localStorage.audiovolume <= 1)
        audio.volume = localStorage.audiovolume; //Load volume
    else
        audio.volume = 0.2; //Default volume

    //Initialize audio controls and event listeners
    waitUntil("#music-up").then((btn) => btn.addEventListener('click', () => volumeAdd(0.05)));
    waitUntil("#music-down").then((btn) => btn.addEventListener('click', () => volumeAdd(-0.05)));
    waitUntil("#user-audio-percentage").then(updateVolumeLabel);

    waitUntil(".volume-slider").then((slider) => {
        const thumb = slider.querySelector('.volume-thumb');
        const fill = slider.querySelector('.volume-fill');
        let isDragging = false;

        function updateVolume(e) {
            const rect = slider.getBoundingClientRect();
            let x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
            let percentage = (x / rect.width) * 100;

            // Update visual elements
            thumb.style.left = `${percentage}%`;
            fill.style.width = `${percentage}%`;

            // Update actual volume
            volumeSet(percentage / 100);
        }

        function onMouseDown(e) {
            isDragging = true;
            updateVolume(e);
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        }

        function onMouseMove(e) {
            if (isDragging) {
                updateVolume(e);
            }
        }

        function onMouseUp() {
            isDragging = false;
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);
        }

        slider.addEventListener('mousedown', onMouseDown);

        // Update slider position when volume is changed through other means
        const originalVolumeSet = volumeSet;
        window.volumeSet = function(number) {
            originalVolumeSet(number);
            const percentage = number * 100;
            thumb.style.left = `${percentage}%`;
            fill.style.width = `${percentage}%`;
        }
    });

    waitUntil("#music-mute").then((box) => {
        audio.muted = box.checked = localStorage.audiomuted === "true";
        box.addEventListener('click', () => {
            localStorage.audiomuted = audio.muted = box.checked;
            playMusic();
        })
    });

    //Monkey patches and event listeners
    const oldPushState = history.pushState;
    history.pushState = function pushState() {
        const ret = oldPushState.apply(this, arguments);
        updateRot();
        return ret;
    };

    const oldReplaceState = history.replaceState;
    history.replaceState = function replaceState() {
        const ret = oldReplaceState.apply(this, arguments);
        updateRot();
        return ret;
    };

    addEventListener('locationchange', updateRot);
    addEventListener('popstate', updateRot);
})();

console.log("rot.js loaded");
