import { serializable } from "../../engine/engine_serialization_decorator.js";
import { getParam } from "../../engine/engine_utils.js";
import { Behaviour, GameObject } from "../Component.js";


const debug = getParam("debugxrflags");
const disable = getParam("disablexrflags");
if (disable) { console.warn("XRFlags are disabled") }

export enum XRStateFlag {
    Never = 0,
    Browser = 1 << 0,
    AR = 1 << 1,
    VR = 1 << 2,
    FirstPerson = 1 << 3,
    ThirdPerson = 1 << 4,
    All = 0xffffffff
}

export class XRState {

    public static Global: XRState = new XRState();

    public Mask: XRStateFlag = XRStateFlag.Browser | XRStateFlag.ThirdPerson;

    public Has(state: XRStateFlag) {
        const res = (this.Mask & state);
        return res !== 0;
    }

    public Set(state: number) {
        if (debug) console.warn("Set XR flag state to", state)
        this.Mask = state as number;
        XRFlag.Apply();
    }

    public Enable(state: number) {
        this.Mask |= state;
        XRFlag.Apply();
    }

    public Disable(state: number) {
        this.Mask &= ~state;
        XRFlag.Apply();
    }

    public Toggle(state: number) {
        this.Mask ^= state;
        XRFlag.Apply();
    }

    public EnableAll() {
        this.Mask = 0xffffffff | 0;
        XRFlag.Apply();
    }

    public DisableAll() {
        this.Mask = 0;
        XRFlag.Apply();
    }
}

/**
 * XRFlag shows or hides GameObjects based on the current XR state or session.  
 * Use for XR-responsive content that should only appear in specific modes.  
 *
 * **XR states:**  
 * - `Browser` - Normal web browsing (no XR)  
 * - `AR` - Augmented reality session  
 * - `VR` - Virtual reality session  
 * - `FirstPerson` - First-person view mode  
 * - `ThirdPerson` - Third-person/spectator view mode
 * - Combine with bitwise OR: `AR | VR`
 *
 * **Debug options:**  
 * - `?debugxrflags` - Log flag changes
 * - `?disablexrflags` - Disable all XR flags
 *
 * @example Show only in VR
 * ```ts
 * const flag = myObject.addComponent(XRFlag);
 * flag.visibleIn = XRStateFlag.VR;
 * ```
 *
 * @example Show in AR and VR, hide in browser
 * ```ts
 * flag.visibleIn = XRStateFlag.AR | XRStateFlag.VR;
 * ```
 *
 * @category XR
 * @category Utilities
 * @group Components
 * @see {@link XRStateFlag} for state options
 * @see {@link XRState} for global state management
 * @see {@link DeviceFlag} for device-based visibility
 * @see {@link WebXR} for XR session management
 */
export class XRFlag extends Behaviour {

    private static registry: XRFlag[] = [];

    public static Apply() {
        for (const r of this.registry) r.UpdateVisible(XRState.Global);
    }

    private static firstApply: boolean;
    private static buffer: XRState = new XRState();

    @serializable()
    public visibleIn!: number;

    awake() {
        XRFlag.registry.push(this);
    }

    onEnable(): void {
        if (!XRFlag.firstApply) {
            XRFlag.firstApply = true;
            XRFlag.Apply();
        }
        else {
            this.UpdateVisible(XRState.Global);
        }
    }

    onDestroy(): void {
        const i = XRFlag.registry.indexOf(this);
        if (i >= 0)
            XRFlag.registry.splice(i, 1);
    }

    public get isOn(): boolean { return this.gameObject.visible; }

    public UpdateVisible(state: XRState | XRStateFlag | null = null) {
        if (disable) {
            return;
        }
        // XR flags set visibility of whole hierarchy which is like setting the whole object inactive
        // so we need to ignore the enabled state of the XRFlag component
        // if(!this.enabled) return;
        let res: boolean | undefined = undefined;

        const flag = state as number;
        if (flag && typeof flag === "number") {
            console.assert(typeof flag === "number", "XRFlag.UpdateVisible: state must be a number", flag);
            if (debug)
                console.log(flag);
            XRFlag.buffer.Mask = flag;
            state = XRFlag.buffer;
        }

        if (state instanceof XRState) {
            if (debug)
                console.warn(this.name, "use passed in mask", state.Mask, this.visibleIn)
            res = state.Has(this.visibleIn);
        }
        else {
            if (debug)
                console.log(this.name, "use global mask")
            XRState.Global.Has(this.visibleIn);
        }
        if (res === undefined) return;
        if (res) {
            if (debug)
                console.log(this.name, "is visible", this.gameObject.uuid)
            // this.gameObject.visible = true;
            GameObject.setActive(this.gameObject, true);
        } else {
            if (debug)
                console.log(this.name, "is not visible", this.gameObject.uuid);
            const isVisible = this.gameObject.visible;
            if (!isVisible) return;
            this.gameObject.visible = false;
            // console.trace("DISABLE", this.name);
            // GameObject.setActive(this.gameObject, false);
        }
    }
}