import { Color, DirectionalLight, Fog, GridHelper, MeshStandardMaterial, PerspectiveCamera, PointLight, Scene, WebGLRenderer } from "three";
import { ObjectUtils, PrimitiveType } from "../engine_create_objects.js";
import { Mathf } from "../engine_math.js";
import { delay } from "../engine_utils.js";
/** Create with static `start`- used to start an XR session while waiting for session granted */
export class TemporaryXRContext {
    static _active = null;
    static get active() {
        return this._active;
    }
    static _requestInFlight = false;
    static async start(mode, init) {
        if (this._active) {
            console.error("Cannot start a new XR session while one is already active");
            return null;
        }
        if (this._requestInFlight) {
            console.error("Cannot start a new XR session while a request is already in flight");
            return null;
        }
        if ('xr' in navigator && navigator.xr) {
            if (!init) {
                console.error("XRSessionInit must be provided");
                return null;
            }
            this._requestInFlight = true;
            const session = await navigator.xr.requestSession(mode, init).catch(err => {
                console.error("Failed to start temporary XR session:", err);
            });
            if (!session) {
                this._requestInFlight = false;
                return null;
            }
            session.addEventListener("end", () => {
                this._active = null;
            });
            if (!this._requestInFlight) {
                session.end();
                return null;
            }
            this._requestInFlight = false;
            this._active = new TemporaryXRContext(mode, init, session);
            return this._active;
        }
        return null;
    }
    static async handoff() {
        if (this._active) {
            return this._active.handoff();
        }
        return null;
    }
    static async stop() {
        this._requestInFlight = false;
        if (this._active) {
            await this._active.end();
            await delay(100);
        }
        this._active = null;
    }
    _session;
    _mode;
    _init;
    get isAR() {
        return this._mode === "immersive-ar";
    }
    _renderer;
    _camera;
    _scene;
    constructor(mode, init, session) {
        this._mode = mode;
        this._init = init;
        this._session = session;
        this._session.addEventListener("end", this.onEnd);
        this._renderer = new WebGLRenderer({ alpha: true, antialias: true });
        this._renderer.outputColorSpace = 'srgb';
        this._renderer.setAnimationLoop(this.onFrame);
        this._renderer.xr.setSession(session);
        this._renderer.xr.enabled = true;
        // Set pixel ratio and size
        this._renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
        this._renderer.setSize(window.innerWidth, window.innerHeight);
        this._camera = new PerspectiveCamera();
        this._scene = new Scene();
        this._scene.fog = new Fog(0x444444, 10, 250);
        this._scene.add(this._camera);
        this.setupScene();
    }
    end() {
        if (!this._session)
            return Promise.resolve();
        return this._session.end();
    }
    /** returns the session and session info and stops the temporary rendering */
    async handoff() {
        if (!this._session)
            throw new Error("Cannot handoff a session that has already ended");
        const info = {
            session: this._session,
            mode: this._mode,
            init: this._init
        };
        await this.onBeforeHandoff();
        // calling onEnd here directly because we dont end the session
        this.onEnd();
        // set the session to null because we dont want this class to accidentaly end the session
        //@ts-ignore
        this._session = null;
        return info;
    }
    onEnd = () => {
        this._session?.removeEventListener("end", this.onEnd);
        this._renderer.setAnimationLoop(null);
        this._renderer.dispose();
        this._scene.clear();
    };
    _lastTime = 0;
    onFrame = (time, _frame) => {
        const dt = time - this._lastTime;
        this.update(time, dt);
        if (this._camera.parent !== this._scene) {
            this._scene.add(this._camera);
        }
        this._renderer.render(this._scene, this._camera);
    };
    /** can be used to prepare the user or fade to black */
    async onBeforeHandoff() {
        // for(const sphere of this._spheres) {
        //     sphere.removeFromParent();
        //     await delay(10);
        // }
        // const obj = ObjectUtils.createPrimitive(PrimitiveType.Cube);
        // obj.position.z = -3;
        // obj.position.y = .5;
        // this._scene.add(obj); 
        await delay(1000);
        this._scene.clear();
        // await delay(100);
    }
    _objects = [];
    setupScene() {
        this._scene.background = new Color(0x000000);
        this._scene.add(new GridHelper(5, 10, 0x111111, 0x111111));
        const light = new DirectionalLight(0xffffff, 1);
        light.position.set(0, 20, 0);
        light.castShadow = false;
        this._scene.add(light);
        const light2 = new DirectionalLight(0xffffff, 1);
        light2.position.set(0, -1, 0);
        light2.castShadow = false;
        this._scene.add(light2);
        const light3 = new PointLight(0xffffff, 1, 100, 1);
        light3.position.set(0, 2, 0);
        light3.castShadow = false;
        light3.distance = 200;
        this._scene.add(light3);
        const range = 50;
        for (let i = 0; i < 100; i++) {
            const material = new MeshStandardMaterial({
                color: 0x222222,
                metalness: 1,
                roughness: .8,
            });
            // if we're in passthrough
            if (this.isAR) {
                material.emissive = new Color(Math.random(), Math.random(), Math.random());
                material.emissiveIntensity = Math.random();
            }
            const type = Mathf.random(0, 1) > .5 ? PrimitiveType.Sphere : PrimitiveType.Cube;
            const obj = ObjectUtils.createPrimitive(type, {
                material
            });
            obj.position.x = Mathf.random(-range, range);
            obj.position.y = Mathf.random(-2, range);
            obj.position.z = Mathf.random(-range, range);
            // random rotation
            obj.rotation.x = Mathf.random(0, Math.PI * 2);
            obj.rotation.y = Mathf.random(0, Math.PI * 2);
            obj.rotation.z = Mathf.random(0, Math.PI * 2);
            obj.scale.multiplyScalar(.5 + Math.random() * 10);
            const dist = obj.position.distanceTo(this._camera.position) - obj.scale.x;
            if (dist < 1) {
                obj.position.multiplyScalar(1 + 1 / dist);
            }
            this._objects.push(obj);
            this._scene.add(obj);
        }
    }
    update(time, _deltaTime) {
        const speed = time * .0004;
        for (let i = 0; i < this._objects.length; i++) {
            const obj = this._objects[i];
            obj.position.y += Math.sin(speed + i * .5) * 0.005;
            obj.rotateY(.002);
        }
    }
}
//# sourceMappingURL=TempXRContext.js.map