import { USDZExporter } from "../../engine-components/export/usdz/USDZExporter.js";
import { isDevEnvironment, showBalloonMessage } from "../debug/index.js";
import { findObjectOfType } from "../engine_components.js";
import { Context } from "../engine_setup.js";
import { DeviceUtilities } from "../engine_utils.js";
import { NeedleXRSession } from "../engine_xr.js";
import { onXRSessionEnd, onXRSessionStart } from "../xr/events.js";
import { ButtonsFactory } from "./buttons.js";
import { getIconElement } from "./icons.js";

// TODO: move these buttons into their own web components so their logic is encapsulated (e.g. the CSS animation when a xr session is requested)

/**
 * Factory to create WebXR buttons for AR, VR, Quicklook and Send to Quest  
 * The buttons are created as HTMLButtonElements and can be added to the DOM.  
 * The buttons will automatically hide when a XR session is started and show again when the session ends.
 */
export class WebXRButtonFactory {

    private static _instance: WebXRButtonFactory;
    private static create() {
        return new WebXRButtonFactory();
    }

    static getOrCreate() {
        if (!this._instance) {
            this._instance = this.create();
        }
        return this._instance;
    }

    private get isSecureConnection() { return window.location.protocol === "https:"; }


    get quicklookButton() { return this._quicklookButton }
    private _quicklookButton?: HTMLButtonElement;

    get arButton() { return this._arButton; }
    private _arButton?: HTMLButtonElement;

    get vrButton() { return this._vrButton }
    private _vrButton?: HTMLButtonElement;

    get sendToQuestButton() { return this._sendToQuestButton; }
    private _sendToQuestButton?: HTMLButtonElement;

    get qrButton() { return ButtonsFactory.getOrCreate().createQRCode(); }

    /** get or create the quicklook button 
     * Behaviour of the button:
     * - if the button is clicked a USDZExporter component will be searched for in the scene and if found, it will be used to export the scene to USDZ / Quicklook
    */
    createQuicklookButton(): HTMLButtonElement {
        if (this._quicklookButton) return this._quicklookButton;

        const button = document.createElement("button");
        this._quicklookButton = button;
        button.dataset["needle"] = "quicklook-button";
        const supportsQuickLook = DeviceUtilities.supportsQuickLookAR();
        let buttonText = "View in AR";
        // we can immediately enter this scene, because the platform supports rel="ar" links
        if (DeviceUtilities.isVisionOS()) {
            buttonText = "View in AR";
        }
        else if (supportsQuickLook || DeviceUtilities.isiOS()) {
            buttonText = "Open in Quicklook";
        }

        button.innerText = buttonText;
        button.prepend(getIconElement("view_in_ar"));

        let createdExporter = false;
        let usdzExporter: USDZExporter | null = null;
        button.addEventListener("click", () => {
            usdzExporter = findObjectOfType(USDZExporter);

            // if the scene doesnt have an USDZExporter component, create one
            if (!usdzExporter) {
                createdExporter = true;
                usdzExporter = new USDZExporter();
            }
            // if we have created a USDZExporter
            if (createdExporter)
                usdzExporter.objectToExport = Context.Current.scene;

            if (usdzExporter) {
                button.classList.add("this-mode-is-requested");
                usdzExporter.exportAndOpen().then(() => {
                    button.classList.remove("this-mode-is-requested");
                }).catch(err => {
                    button.classList.remove("this-mode-is-requested");
                    console.error(err);
                });
            }
            else {
                console.warn("No USDZExporter component found in the scene");
            }
        });
        this.hideElementDuringXRSession(button);
        return button;
    }
    /** get or create the WebXR AR button  
     * @param init optional session init options
     * Behaviour of the button:
     * - if the device supports AR, the button will be visible and clickable
     * - if the device does not support AR, the button will be hidden
     * - if the device changes and now supports AR, the button will be visible
     */
    createARButton(init?: XRSessionInit): HTMLButtonElement {
        if (this._arButton) return this._arButton;

        const mode: XRSessionMode = "immersive-ar";
        const button = document.createElement("button");
        this._arButton = button;

        button.classList.add("webxr-button");
        button.dataset["needle"] = "webxr-ar-button";
        button.innerText = "Enter AR";
        button.prepend(getIconElement("view_in_ar"))
        button.title = "Click to start an AR session";
        button.addEventListener("click", () => NeedleXRSession.start(mode, init));
        this.updateSessionSupported(button, mode);
        this.listenToXRSessionState(button, mode);
        this.hideElementDuringXRSession(button);

        if (!this.isSecureConnection) {
            button.disabled = true;
            button.title = "WebXR requires a secure connection (HTTPS)";
        }

        if (!DeviceUtilities.isMozillaXR()) // WebXR Viewer can't attach events before session start
            navigator.xr?.addEventListener("devicechange", () => this.updateSessionSupported(button, mode));

        return button;
    }

    /** get or create the WebXR VR button 
     * @param init optional session init options
     * Behaviour of the button:
     * - if the device supports VR, the button will be visible and clickable
     * - if the device does not support VR, the button will be hidden
     * - if the device changes and now supports VR, the button will be visible
    */
    createVRButton(init?: XRSessionInit): HTMLButtonElement {
        if (this._vrButton) return this._vrButton;

        const mode: XRSessionMode = "immersive-vr";
        const button = document.createElement("button");
        this._vrButton = button;
        button.classList.add("webxr-button");
        button.dataset["needle"] = "webxr-vr-button";
        button.innerText = "Enter VR";
        button.prepend(getIconElement("panorama_photosphere"));
        button.title = "Click to start a VR session";
        button.addEventListener("click", () => NeedleXRSession.start(mode, init));
        this.updateSessionSupported(button, mode);
        this.listenToXRSessionState(button, mode);
        this.hideElementDuringXRSession(button);

        if (!this.isSecureConnection) {
            button.disabled = true;
            button.title = "WebXR requires a secure connection (HTTPS)";
        }

        if (!DeviceUtilities.isMozillaXR()) // WebXR Viewer can't attach events before session start
            navigator.xr?.addEventListener("devicechange", () => this.updateSessionSupported(button, mode));

        return button;
    }

    /** get or create the Send To Quest button 
     * Behaviour of the button:
     * - if the button is clicked, the current URL will be sent to the Oculus Browser on the Quest
    */
    createSendToQuestButton(): HTMLButtonElement {
        if (this._sendToQuestButton) return this._sendToQuestButton;
        const baseUrl = `https://oculus.com/open_url/?url=`
        const button = document.createElement("button");
        this._sendToQuestButton = button;
        button.dataset["needle"] = "webxr-sendtoquest-button";
        button.innerText = "Open on Quest";
        button.prepend(getIconElement("share_windows"));
        button.title = "Click to send this page to the Oculus Browser on your Quest";
        button.addEventListener("click", () => {
            const urlParameter = encodeURIComponent(window.location.href);
            const url = baseUrl + urlParameter;
            if (window.open(url) == null) {
                showBalloonMessage("This page doesn't allow popups. Please paste " + url + " into your browser.");
            }
        });
        this.listenToXRSessionState(button);
        this.hideElementDuringXRSession(button);
        // make sure to hide the button when we have VR support directly on the device
        if (!DeviceUtilities.isMozillaXR()) { // WebXR Viewer can't attach events before session start
            navigator.xr?.addEventListener("devicechange", () => {
                if (navigator.xr?.isSessionSupported("immersive-vr")) {
                    button.style.display = "none";
                }
                else {
                    button.style.display = "";
                }
            });
        }
        return button;
    }

    /**
     * @deprecated please use ButtonsFactory.getOrCreate().createQRCode(). This method will be removed in a future update
     */
    createQRCode(): HTMLButtonElement {
        return ButtonsFactory.getOrCreate().createQRCode();
    }

    private updateSessionSupported(button: HTMLButtonElement, mode: XRSessionMode) {
        if (mode === "immersive-ar" && DeviceUtilities.isiOS() && !DeviceUtilities.isVisionOS()) {
            // on iOS, we can forward to the AppClip experience from the same button,
            // so we always show the button. No AppClip support on VisionOS for now, so
            // button state depends on WebXR support there.
            return;
        }

        if (!("xr" in navigator)) {
            button.style.display = "none";
            return;
        }
        NeedleXRSession.isSessionSupported(mode).then(supported => {
            button.style.display = !supported ? "none" : "";
            if (isDevEnvironment() && !supported) console.log("[WebXR] \"" + mode + "\" is not supported on this device. Make sure your server runs using HTTPS and you have a device connected that supports " + mode);
        });
    }

    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"];
        });
    }

    private listenToXRSessionState(button: HTMLButtonElement, mode?: XRSessionMode) {

        if (mode) {
            NeedleXRSession.onSessionRequestStart(args => {
                if (args.mode === mode) {
                    button.classList.add("this-mode-is-requested");
                    // button["original-text"] = button.innerText;
                    // let modeText = mode === "immersive-vr" ? "VR" : "AR";
                    // button.innerText = "Starting " + modeText + "...";
                }
                else {
                    button["was-disabled"] = button.disabled;
                    button.disabled = true;
                    button.classList.add("other-mode-is-requested");
                }
            });
            NeedleXRSession.onSessionRequestEnd(_ => {
                button.classList.remove("this-mode-is-requested");
                button.classList.remove("other-mode-is-requested");
                button.disabled = button["was-disabled"];
                // button.innerText = button["original-text"];
            });
        }
    }
}


/** @deprecated please use WebXRButtonFactory. This type will be removed in a future update */
export type NeedleWebXRHtmlElement = WebXRButtonFactory;