import { isDevEnvironment } from "./debug/index.js";
import { Context } from "./engine_setup.js";

declare global {
    interface UserActivation {
        readonly hasBeenActive: boolean;
        readonly isActive: boolean;
    }
    interface Navigator {
        readonly userActivation: UserActivation;
    }
}

export enum ApplicationEvents {
    Visible = "application-visible",
    Hidden = "application-hidden",
    MuteChanged = "application-mutechanged",
}

let userInteractionRegistered = false;
const userInteractionCallbacks: Function[] = [];
/**
 * Invoked when the user interacts with the page (click, touch, keypress, etc) allowing to play media / audio / start XR
 * @internal
 */
export function internalOnUserInputRegistered() {
    if (userInteractionRegistered) return;
    if (isDevEnvironment()) console.debug("[Needle Engine] User input registered: Media playback is now allowed.");
    userInteractionRegistered = true;
    const copy = [...userInteractionCallbacks];
    userInteractionCallbacks.length = 0;
    copy.forEach(cb => cb());
}
document.addEventListener('mousedown', internalOnUserInputRegistered);
document.addEventListener('pointerup', internalOnUserInputRegistered);
document.addEventListener('click', internalOnUserInputRegistered);
document.addEventListener('dragstart', internalOnUserInputRegistered);
document.addEventListener('touchend', internalOnUserInputRegistered);
document.addEventListener('keydown', internalOnUserInputRegistered);


// User Activation should be available across browsers since November 2023 https://developer.mozilla.org/en-US/docs/Web/API/UserActivation
if (typeof window !== "undefined" && "userActivation" in navigator && (navigator.userActivation as UserActivation)?.isActive) {
    if (isDevEnvironment()) console.debug("[Needle Engine] User input already active: Media playback is now allowed.");
    userInteractionCallbacks.length = 0;
    userInteractionRegistered = true; 
}

/** 
 * The Application class can be used to mute audio globally, and to check if the application (canvas) is currently visible (it's tab is active and not minimized). 
 */
export class Application extends EventTarget {

    public static get userInteractionRegistered(): boolean {
        return userInteractionRegistered;
    }

    /**  @deprecated use Application.registerWaitForInteraction instead */
    public static readonly registerWaitForAllowAudio = Application.registerWaitForInteraction;
    /**
     * Register a callback that will be called when the user interacts with the page (click, touch, keypress, etc).  
     * If the user has already interacted with the page, the callback will be called immediately.  
     * This can be used to wait for user interaction before playing audio, for example.
     */
    public static registerWaitForInteraction(cb: Function) {
        if (cb !== null) {
            if (userInteractionRegistered) {
                cb();
                return;
            }
            if (userInteractionCallbacks.indexOf(cb) === -1)
                userInteractionCallbacks.push(cb);
        }
    }
    /**
     * Unregister a callback that was previously registered with registerWaitForInteraction.
     */
    public static unregisterWaitForInteraction(cb: Function) {
        const index = userInteractionCallbacks.indexOf(cb);
        if (index !== -1) {
            userInteractionCallbacks.splice(index, 1);
        }
    }

    private _mute: boolean = false;
    /** audio muted? */
    get muted() { return this._mute; }
    /** set global audio mute */
    set muted(value) {
        if (value === this._mute) return;
        this._mute = value;
        this.dispatchEvent(new Event(ApplicationEvents.MuteChanged));
    }

    private readonly context: Context;

    /** @returns true if the document is focused */
    public get hasFocus(): boolean {
        return document.hasFocus();
    }

    /**
     * @returns true if the application is currently visible (it's tab is active and not minimized)
     */
    public get isVisible(): boolean {
        return this._isVisible;
    }

    private _isVisible: boolean = true;

    /** @internal */
    constructor(context: Context) {
        super();
        this.context = context;
        window.addEventListener("visibilitychange", this.onVisiblityChanged.bind(this), false);
    }

    private onVisiblityChanged(evt) {
        // console.log(evt.target.visibilityState)
        switch (evt.target.visibilityState) {
            case "hidden":
                this._isVisible = false;
                this.dispatchEvent(new Event(ApplicationEvents.Hidden));
                break;
            case "visible":
                this._isVisible = true;
                this.dispatchEvent(new Event(ApplicationEvents.Visible));
                break;
        }
    }
}
