//@ts-check

import JadefinIntegrity from '../JadefinIntegrity.js';

import Jadefin from "../Jadefin.js";
import JadefinMod from "../JadefinMod.js";
import JadefinModules from "../JadefinModules.js";
import JadefinUtils from "../JadefinUtils.js";

import { rd, rdom, rd$, RDOMListHelper } from "../utils/rdom.js";

export default JadefinIntegrity("InputEater", import.meta.url, () => new (class InputEater extends JadefinMod {
    hasShownPopupThisSession = false;
    canBeEating = false;

    _isEnabled = true;
    _isEating = false;

    /** @type {Element | undefined} */
    _lastOsdPage;

    _btnRemote = rd$()`
        <button is="paper-icon-button-light" class="btnRemote autoSize paper-icon-button-light" title="Grab remote">
            <span class="xlargePaperIconButton material-icons settings_remote" aria-hidden="true"></span>
        </button>
    `;

    constructor() {
        super();

        this._btnRemote.addEventListener("click", () => this.isEnabled = false);

        this.initHookDocumentAddEventListener();
        this.initHookDocumentRemoveEventListener();
    }

    get isEnabled() {
        return this._isEnabled;
    }

    set isEnabled(value) {
        this._isEnabled = value;

        this.updateIsEating();
    }

    get shouldShowPopup() {
        return this.storage.get("shouldShowPopup", true);
    }

    set shouldShowPopup(value) {
        this.storage.set("shouldShowPopup", value);
    }

    get isEating() {
        return this._isEating;
    }

    set isEating(value) {
        if (this._isEating == value) {
            return;
        }

        this.log.i(`isEating = ${value}`);
        this._isEating = value;

        this.update();
    }

    async init(name, url) {
        await super.init(name, url);

        await JadefinUtils.waitUntil(() => JadefinModules.syncPlay);

        this.initStyle();
        this.initHookSyncPlayEnabled();
        this.initHookSyncPlayDisabled();
        this.initHookMediaSessionHandlers();
        this.initHookInputManagerHandler();

        document.addEventListener("viewshow", () => {
            this.updateIsEating();
        });

        const ExtrasMenu = /** @type {import("./ExtrasMenu.js").default} */ (Jadefin.getMod("ExtrasMenu"));

        ExtrasMenu.items.push({
            name: "Grab remote",
            secondaryText: "Take control over the party",
            icon: "settings_remote",
            in: ExtrasMenu.IN_CUSTOM,
            inCustom: (current, item) => {
                if (this.isEnabled) {
                    item.name = "Grab remote";
                    item.secondaryText = "Take control over the party";
                } else {
                    item.name = "Disable controls";
                    item.secondaryText = "Give up control over the party";
                }

                return this.canBeEating;
            },
            cb: () => {
                this.isEnabled = !this.isEnabled;
            }
        });

        this.log.i("Ready");
    }

    initHookSyncPlayEnabled() {
        const orig = this._enableSyncPlay = JadefinModules.syncPlay.Manager.enableSyncPlay.bind(JadefinModules.syncPlay.Manager);
        JadefinModules.syncPlay.Manager.enableSyncPlay = (apiClient, groupInfo, showMessage) => {
            const rv = orig(apiClient, groupInfo, showMessage);

            this.updateIsEating();

            return rv;
        };
    }

    initHookSyncPlayDisabled() {
        const orig = this._disableSyncPlay = JadefinModules.syncPlay.Manager.disableSyncPlay.bind(JadefinModules.syncPlay.Manager);
        JadefinModules.syncPlay.Manager.disableSyncPlay = (showMessage) => {
            const rv = orig(showMessage);

            this.updateIsEating();

            return rv;
        };
    }

    initHookDocumentAddEventListener() {
        const orig = this._addEventListener = document.addEventListener.bind(document);
        document.addEventListener = (type, listener, options) => {
            if (type == "keydown") {
                const listenerStr = listener.toString();

                // Anonymous function in playback-video
                if (listenerStr.indexOf(".btnPause") != -1 &&
                    listenerStr.indexOf("32") != -1 &&
                    listenerStr.indexOf("playPause") != -1) {
                    this.log.i("Wrapping playback-video keydown listener");
                    this.log.dir(listener);

                    const origListener = this._playbackKeyDown = listener;
                    listener = this._playbackKeyDownWrap = (e) => {
                        if (this.isEating) {
                            if (e.keyCode == 32) {
                                return;
                            }

                            switch (e.key) {
                                case "ArrowLeft":
                                case "ArrowRight":
                                case "Enter":
                                case "Escape":
                                case "Back":
                                case "k":
                                case "l":
                                case "ArrowRight":
                                case "Right":
                                case "j":
                                case "ArrowLeft":
                                case "Left":
                                case "p":
                                case "P":
                                case "n":
                                case "N":
                                case "NavigationLeft":
                                case "GamepadDPadLeft":
                                case "GamepadLeftThumbstickLeft":
                                case "NavigationRight":
                                case "GamepadDPadRight":
                                case "GamepadLeftThumbstickRight":
                                case "Home":
                                case "End":
                                case "0":
                                case "1":
                                case "2":
                                case "3":
                                case "4":
                                case "5":
                                case "6":
                                case "7":
                                case "8":
                                case "9":
                                case ">":
                                case "<":
                                case "PageUp":
                                case "PageDown":
                                    return;
                            }
                        }

                        return origListener(e);
                    };
                }
            }

            return orig(type, listener, options);
        };
    }

    initHookDocumentRemoveEventListener() {
        const orig = this._removeEventListener = document.removeEventListener.bind(document);
        document.removeEventListener = (type, listener, options) => {
            if (listener == this._playbackKeyDown) {
                listener = this._playbackKeyDownWrap;
            }

            return orig(type, listener, options);
        };
    }

    initHookMediaSessionHandlers() {
        const owner = Jadefin.findWebpackRawLoad(e => JadefinUtils.findDeep(e, 1, (_, o) => o.nextTrack && o.fastForward));

        if (!owner || !("mediaSession" in navigator)) {
            this.log.e("Couldn't hook media session handlers");
            return;
        }

        const basic = (name, e) => {
            if (this.isEating) {
                return;
            }

            owner[name](owner.getCurrentPlayer());
        }

        const handlerTuples = [
            ["previoustrack", (e) => basic("previousTrack", e)],
            ["nexttrack", (e) => basic("nextTrack", e)],
            ["play", (e) => basic("unpause", e)],
            ["pause", (e) => basic("pause", e)],
            ["seekbackward", (e) => basic("rewind", e)],
            ["seekforward", (e) => basic("fastForward", e)],
            ["seekto", (e) => {
                if (this.isEating) {
                    return;
                }

                const player = owner.getCurrentPlayer();
                const item = owner.getPlayerState(player).NowPlayingItem;
                const currentTime = Math.floor(item.RunTimeTicks ? item.RunTimeTicks / 10000 : 0);
                const seekTime = 1000 * e.seekTime;
                owner.seekPercent(seekTime / currentTime * 100, player);
            }]
        ];

        const handlers = this._handlers = {};

        for (let handlerTuple of handlerTuples) {
            this.log.i(`Replacing media session action handler ${handlerTuple[0]}`);
            // @ts-ignore
            navigator.mediaSession.setActionHandler(handlerTuple[0], handlers[handlerTuple[0]] = handlerTuple[1].bind(this));
        }
    }

    initHookInputManagerHandler() {
        const orig = this._handleCommand = JadefinModules.inputManager.handleCommand.bind(JadefinModules.inputManager);
        JadefinModules.inputManager.handleCommand = (commandName, options) => {
            if (this.isEating) {
                switch (commandName) {
                    case "nextchapter":
                    case "next":
                    case "nexttrack":
                    case "previous":
                    case "previoustrack":
                    case "previouschapter":
                    case "play":
                    case "pause":
                    case "playpause":
                    case "stop":
                    case "increaseplaybackrate":
                    case "decreaseplaybackrate":
                    case "fastforward":
                    case "rewind":
                    case "seek":
                    case "repeatnone":
                    case "repeatall":
                    case "repeatone":
                        return;
                }
            }

            return orig(commandName, options);
        };
    }

    /**
     * @param {boolean} [force]
     */
    showPopup(force) {
        if (!force && (!this.shouldShowPopup || this.hasShownPopupThisSession)) {
            return false;
        }

        this.hasShownPopupThisSession = true;

        const ExtrasMenu = /** @type {import("./ExtrasMenu.js").default} */ (Jadefin.getMod("ExtrasMenu"));

        JadefinModules.actionSheet.show({
            dialogClass: "inputEaterInfo",
            positionTo: ExtrasMenu.headerExtrasEl,
            title: "Controls disabled.",
            text: "You can toggle it in this corner.",
            items: [
                {name: "Okay", icon: "tune"}
            ]
        });

        return true;
    }

    async updateIsEating() {
        let isSyncPlayEnabled = JadefinModules.syncPlay.Manager.isSyncPlayEnabled();
        let isSyncPlayOwner = true;

        if (isSyncPlayEnabled) {
            const groupInfo = JadefinModules.syncPlay.Manager.groupInfo;
            const currentUser = await JadefinModules.ApiClient.getCurrentUser();

            isSyncPlayOwner = groupInfo.GroupName.indexOf(currentUser.Name) != -1;
        }

        this.canBeEating = isSyncPlayEnabled && !isSyncPlayOwner && JadefinUtils.routePathIsVideo;
        this.isEating = this.isEnabled && this.canBeEating;
    }

    update() {
        document.body.setAttribute("input-eaten", this.isEating ? "true" : "false");

        if (this.isEating) {
            this.canBeEating = true;

            if (!this.showPopup()) {
                JadefinModules.toast("Controls disabled.");
            }
        }

        this._btnRemote.classList.toggle("hide", !this.isEating);

        const videoOsdPage = document.querySelector("div#videoOsdPage:not(.hide)");
        if (!videoOsdPage) {
            return;
        }

        if (this._lastOsdPage == videoOsdPage) {
            return;
        }
        this._lastOsdPage = videoOsdPage;

        this.log.i("Adding event listener to videoOsdPage");
        this.log.dir(videoOsdPage);

        videoOsdPage.addEventListener(window.PointerEvent ? "pointerdown" : "click", e => {
            if (this.isEating) {
                e.stopPropagation();
            }
        }, true);

        const buttons = videoOsdPage.querySelector(".osdControls > .buttons");
        if (this._btnRemote.parentElement != buttons && buttons) {
            this.log.i("Adding remote button to osd buttons");
            this.log.dir(buttons);

            buttons.insertBefore(this._btnRemote, buttons.firstChild);
        }
    }

})());