import { isDevEnvironment, showBalloonWarning } from "../debug/debug.js";
import { IContext } from "../engine_types.js";
import { generateQRCode, isMobileDevice } from "../engine_utils.js";
import { onXRSessionEnd, onXRSessionStart } from "../xr/events.js";
import { getIconElement } from "./icons.js";

/** Use the ButtonsFactory to create buttons with icons and functionality   
 * Get access to the default buttons by using `ButtonsFactory.getOrCreate()`  
 * The factory will create the buttons if they don't exist yet, and return the existing ones if they do (this allows you to reparent or modify created buttons) 
 */
export class ButtonsFactory {

    private static _instance?: ButtonsFactory;
    /** Get access to the default HTML button factory.    
     * Use this to get or create default Needle Engine buttons that can be added to your HTML UI  
     * If you want to create a new factory and create new button instances instead of shared buttons, use `ButtonsFactory.create()` instead
     */
    static getOrCreate() {
        if (!this._instance) {
            this._instance = new ButtonsFactory();
        }
        return this._instance;
    }
    /** create a new buttons factory */
    static create() {
        return new ButtonsFactory();
    }


    private _fullscreenButton?: HTMLButtonElement;

    /**
     * Get the fullscreen button (or undefined if it doesn't exist yet). Call {@link ButtonsFactory.createFullscreenButton} to get or create it
     */
    get fullscreenButton() {
        return this._fullscreenButton;
    }

    /** Create a fullscreen button (or return the existing one if it already exists) */
    createFullscreenButton(ctx: IContext) : HTMLButtonElement | null {
        if (this._fullscreenButton) {
            return this._fullscreenButton;
        }

        // check for fullscreen support
        if (!document.fullscreenEnabled) {
            if (isDevEnvironment()) console.warn("NeedleMenu: Fullscreen button could not be created, device doesn't support the Fullscreen API");
            return null;
        }

        const button = document.createElement("button");
        this._fullscreenButton = button;
        button.classList.add("fullscreen-button");
        button.title = "Click to enter fullscreen mode";
        const enterFullscreenIcon = getIconElement("fullscreen");
        const exitFullscreenIcon = getIconElement("fullscreen_exit");
        button.appendChild(enterFullscreenIcon);
        button.onclick = () => {
            if (document.fullscreenElement) {
                document.exitFullscreen();
            } else {
                if("webkitRequestFullscreen" in ctx.domElement && typeof ctx.domElement["webkitRequestFullscreen"] === "function")
                    ctx.domElement["webkitRequestFullscreen"]();
                else if ("requestFullscreen" in ctx.domElement)
                    ctx.domElement.requestFullscreen();
            }
        };
        document.addEventListener("fullscreenchange", () => {
            if (document.fullscreenElement) {
                enterFullscreenIcon.remove();
                button.appendChild(exitFullscreenIcon);
                button.title = "Click to enter fullscreen mode";
            } else {
                exitFullscreenIcon.remove();
                button.appendChild(enterFullscreenIcon);
                button.title = "Click to exit fullscreen mode";
            }
        });
        // xr session started?
        globalThis.addEventListener("needle-xrsession-start", () => {
            button.style.display = "none";
        });
        globalThis.addEventListener("needle-xrsession-end", () => {
            button.style.display = "";
        });
        return button;
    }

    private _muteButton?: HTMLButtonElement;

    /** Get the mute button (or undefined if it doesn't exist yet). Call {@link ButtonsFactory.createMuteButton} to get or create it */
    get muteButton() { return this._muteButton; }

    /** Create a mute button (or return the existing one if it already exists) */
    createMuteButton(ctx: IContext) {
        if (this._muteButton) {
            return this._muteButton;
        }
        const button = document.createElement("button");
        this._muteButton = button;
        button.classList.add("mute-button");
        button.title = "Click to mute/unmute";
        const muteIcon = getIconElement("volume_off");
        const unmuteIcon = getIconElement("volume_up");

        // save state in session storage (this needs consent)
        // if (sessionStorage.getItem("muted") === "true") {
        //     ctx.application.muted = true;
        // }
        // else {
        //     ctx.application.muted = false;
        // }

        if (ctx.application.muted) {
            button.appendChild(muteIcon);
        }
        else {
            button.appendChild(unmuteIcon);
        }
        button.onclick = () => {
            if (ctx.application.muted) {
                muteIcon.remove();
                button.appendChild(unmuteIcon);
                ctx.application.muted = false;
                // sessionStorage.setItem("muted", "false");
            } else {
                unmuteIcon.remove();
                button.appendChild(muteIcon);
                ctx.application.muted = true;
                // sessionStorage.setItem("muted", "true");
            }
        };
        return button;
    }


    private _qrButton?: HTMLButtonElement;

    /**
     * Get the QR code button (or undefined if it doesn't exist yet). Call {@link ButtonsFactory.createQRCode} to get or create it
     */
    get qrButton() { return this._qrButton; }

    /** Create a QR code button (or return the existing one if it already exists)
     * The QR code button will show a QR code that can be scanned to open the current page on a phone  
     * The QR code will be generated with the current URL when the button is clicked
     * @returns the QR code button element
     */
    createQRCode(): HTMLButtonElement {

        if (this._qrButton) return this._qrButton;

        const qrCodeButton = document.createElement("button");
        this._qrButton = qrCodeButton;
        qrCodeButton.innerText = "QR Code";
        qrCodeButton.prepend(getIconElement("qr_code"));
        qrCodeButton.title = "Scan this QR code with your phone to open this page";
        this.hideElementDuringXRSession(qrCodeButton);

        const qrCodeContainer = document.createElement("div");
        qrCodeContainer.style.cssText = `
            position: fixed;
            display: inline-block;
            padding: 1rem;
            background-color: white;
            border-radius: 0.4rem;
            cursor: pointer;
            z-index: 1000;
            box-shadow: 0 0 12px rgba(0, 0, 0, 0.2);
        `;

        const qrCodeElement = document.createElement("div");
        qrCodeElement.classList.add("qr-code-container");
        qrCodeContainer.appendChild(qrCodeElement);

        qrCodeButton.addEventListener("click", () => {
            if (qrCodeContainer.parentNode) return hideQRCode();
            if (window.location.href.includes("://localhost")) {
                showBalloonWarning("To access your website from another device in the same local network you have to use the IP address instead of localhost.");
            }
            showQRCode();
        });


        /** shows the QRCode near the button */
        async function showQRCode() {
            // generate the qr code when the button is clicked
            // this ensures that we get the QRcode with the latest URL
            await generateAndInsertQRCode();
            // TODO: make sure it doesnt overflow the screen
            // we need to add the qrCodeContainer to the body to get the correct size
            document.body.appendChild(qrCodeContainer);
            const containerRect = qrCodeElement.getBoundingClientRect();
            const buttonRect = qrCodeButton.getBoundingClientRect();
            qrCodeContainer.style.left = (buttonRect.left + buttonRect.width * .5 - containerRect.width * .5) + "px";
            const isButtonInTopHalf = buttonRect.top < containerRect.height;
            if (isButtonInTopHalf)
                qrCodeContainer.style.top = `calc(${buttonRect.bottom}px + ${qrCodeContainer.style.padding} * .6)`;
            else
                qrCodeContainer.style.top = `calc(${buttonRect.top - containerRect.height}px - ${qrCodeContainer.style.padding} * 2.5)`;
            qrCodeContainer.style.opacity = "0";
            qrCodeContainer.style.pointerEvents = "all";
            qrCodeContainer.style.transition = "opacity 0.2s ease-in-out";

            // context click to hide the QR code again, if we dont timeout the event will be triggered immediately
            setTimeout(() => {
                qrCodeContainer.style.opacity = "1";
                window.addEventListener("click", hideQRCode, { once: true })
            });
            window.addEventListener("resize", hideQRCode);
            window.addEventListener("scroll", hideQRCode);

            // if we're in fullscreen:
            if (document.fullscreenElement) {
                document.fullscreenElement.appendChild(qrCodeContainer);
            }
            else
                document.body.appendChild(qrCodeContainer);
        }

        /** hides to QRCode overlay and unsubscribes from events */
        function hideQRCode() {
            qrCodeContainer.style.pointerEvents = "none";
            qrCodeContainer.style.transition = "opacity 0.2s";
            qrCodeContainer.style.opacity = "0";
            setTimeout(() => qrCodeContainer.parentNode?.removeChild(qrCodeContainer), 500);
            window.removeEventListener("click", hideQRCode);
            window.removeEventListener("resize", hideQRCode);
            window.removeEventListener("scroll", hideQRCode);
        };

        /** generates a QR code and inserts it into the qrCodeElement */
        async function generateAndInsertQRCode() {
            const size = 200;
            const code = await generateQRCode({
                text: window.location.href,
                width: size,
                height: size,
            });
            qrCodeElement.innerHTML = "";
            qrCodeElement.appendChild(code);
        }

        // lazily create the qr button
        qrCodeButton.addEventListener("pointerenter", () => {
            generateAndInsertQRCode();
        }, { once: true });

        return qrCodeButton;
    }

    private hideElementDuringXRSession(element: HTMLElement) {
        onXRSessionStart(_ => {
            element["previous-display"] = element.style.display;
            element.style.display = "none";
        });
        onXRSessionEnd(_ => {
            if (element["previous-display"] != undefined)
                element.style.display = element["previous-display"];
        });
    }
}
