import { InteractiveObject } from '@/components/InteractiveObjects/InteractiveObject';
import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer';
import { GameEngine } from '@/lib/GameEngine';
import { Hero } from '@/lib/Hero';
import { autoScale, getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxSize } from '@/lib/MeshUtils';
import Utils from '@/lib/Utils';
let gameEngine = null;

export default {

    async mounted(){
        gameEngine = new GameEngine();
        //this.gameEngine = gameEngine;
        await gameEngine.init(this.$refs.target, { 
            xr: true, 
            gizmo: this.env == 'GameDesigner', 
            designMode: this.env == 'GameDesigner', 
            depthSense: this.env == 'GameDesigner' ? false : this.store.prefs.xr.depthSense
        });
        //gameEngine.scene.add(new gameEngine.$.GridHelper(100,100));
        if (this.env == 'GameDesigner'){
            gameEngine.scene.add(gameEngine.transformControls.getHelper());
        }else{
            gameEngine.dashboard.enable();
        }
        this.resize();
        //gameEngine.setCamera(gameEngine.orthographicCamera)
        //gameEngine.setCameraOrthographic();
        await this.loadScenario();
        window.addEventListener('resize', this.resize);
    },

    unmounted(){
        window.removeEventListener('resize', this.resize);
        gameEngine.clearScene();
        gameEngine.stop();
    },

    computed:{
        scenes(){
            return this.scenario?.scenes || [];
        },
        scene(){
            return this.scenesList[0];
        },
        currentObject(){
            return this.objectsList[0];
        },
        sceneObjects(){
            return this.object.scenes?.[this.scene?.data?.id]?.objects;
        },
        object(){
            return this.modelValue;
        },
        objectAnimations(){
            return this.currentObject?.__g?.animations?.map(a => ({
                name: a.name, id: a.uuid, a
            }));
        }
    },

    watch:{
        async scene(n){
            this.object.scenes = this.object.scenes || {}
            this.object.scenes[n.data.id] = this.object.scenes[n.data.id] || {}
            await this.loadEnvironment(n, this.object.scenes[n.data.id]);
        },
        mode(n){
            gameEngine.transformControls.setMode(n)
        },
        async 'object.scenario'(n){
            await this.loadScenario()
        },
        currentObject(n){
            if (this.env == 'GameDesigner'){
                gameEngine.transformControls.attach(n.__o);
                gameEngine.gizmo.target = n.__o.position;
                gameEngine.camera.updateProjectionMatrix()
            }
        },
        renderType(v){
            gameEngine.renderType = v;
        },
        cameraType(v){
            if (v == 'perspective'){
                gameEngine.setCameraPerspective();
            }else{
                gameEngine.setCameraOrthographic();
            }
        }
    },

    methods:{
        async loadScenario(){
            if (this.object.scenario){
                this.scenario = (await this.$api.scenario.load(this.object.scenario)).data;
            }else{
                this.scenario = null;
            }
        },

        async expandScenarioData(scene){
            const promises = [];
            ['environment', 'scene', 'intro', 'audio'].filter(e=>scene.data[e]).forEach(e=>{
                promises.push(this.$api.gameObject.load(scene.data[e]).then(r=>scene.data['$'+e] = r.data))
            })
            
            for (let i of scene.data.items || []) {
                promises.push(this.$api.gameObject.load(i.data.go).then(r=>i.data.$go = r.data));
            }
            await Promise.all(promises);
        },

        /**
         * loads all environment objects
         * @param scene Scene object from the Scenario Module
         * @param target Target scene definition from Game Module
         */
        async loadEnvironment(scene, target){
            //await gameEngine.loadPanorama(`/asset/default/43.webp`);
            gameEngine.clearScene();
            gameEngine.activeObjects.visible = false;
            gameEngine.dashboard?.loading(0,0);
            await this.expandScenarioData(scene);
            gameEngine.dashboard?.loading(0.1);

            gameEngine.orbitControls.enableRotate = this.env == 'GameDesigner'

            //this is needed cause when mounted canvas has different size
            this.resize();
            target.objects = target.objects || {};
            let l = target.objects;
            if (this.scene.data.$environment){
                await gameEngine.loadPanorama(this.scene.data.$environment.asset.name);
            }
            if (this.scene.data.$audio){
                await gameEngine.playAmbientSound(this.scene.data.$audio.asset.name);
                gameEngine.ambientSound.setVolume( 0.5 );
            }
            if (this.scene.data.$scene){
                let env = await gameEngine.load(this.scene.data.$scene.asset.name);
                this.setObjectAttributes(l, this.scene.data, env.scene, env, 100);
                if (this.env != 'GameDesigner'){
                    env.scene.traverse(o=>{
                        if (o.name == 'Sphere'){
                            console.log('Fixing ground. TODO!!!')
                            gameEngine.physics.add(o, 'fixed', true, undefined, 'mesh', { root: env.scene}) 
                        }
                    })
                }
                gameEngine.activeObjects.add(env.scene);
            }
            let expectToFinish = 0, finished = 0;;
            if (this.scene.data.items){
                let loaded = 0;
                for (let i of this.scene.data.items) {
                    if (this.env != 'GameDesigner'){
                        if (i.data.activationTriggers?.length || i.data.activationScore){
                            i.data.shouldBeLocked = true;
                        }
                    }
                    let io = await new InteractiveObject(gameEngine, i.data)
                    i.__io = io;
                    if (io.emits?.includes('finish')){
                        expectToFinish++;
                    }
                    this.setObjectAttributes(l, i.data, io.object, io.source, 1);
                    gameEngine.activeObjects.add(io.object);
                    if (this.env != 'GameDesigner'){
                        if (i.data.$go?.type == 'player3d'){
                            let hero = new Hero(io.source, i.data.$go);
                            hero.init(gameEngine);
                        }else{
                            if (io.source?.animations?.length){
                                gameEngine.playAnimation(gameEngine.scene, io.source.animations[0]);
                            }

                            if (!i.data.noPhysics){
                                let bb = getBoundingBox(io.object);
                                let bbs = getBoundingBoxSize(bb);
                                gameEngine.physics.add(io.object, 'fixed', false, undefined, 'capsule', {
                                    radius: Math.max(bbs.x, bbs.z)/2, halfHeight: bbs.y/2
                                })
                            }
                        }
                        io.addEventListener('finish', ()=>{
                            finished ++;
                            if (!i.data.pointsGiven){
                                gameEngine.dashboard?.addPoints(i.data.points)
                            }
                            i.data.pointsGiven = true;
                            this.scene.data.items.forEach(di=>{
                                if (di.data.activationTriggers?.includes(i.data.id)){
                                    let idx = di.data.activationTriggers.indexOf(i.data.id)
                                    di.data.activationTriggers.splice(idx, 1);
                                }
                                if (!di.data.activationTriggers?.length && di.__io.object.__locked &&
                                    gameEngine.dashboard.points > di.data.activationScore){
                                        di.__io.locker.unlock();
                                    }
                            });
                            if (finished == expectToFinish){
                                //GO TO NEXT LEVEL
                                console.log('LEVEL FINISHED')
                            }
                        })
                    }
                    loaded += 1/this.scene.data.items.length
                    gameEngine.dashboard?.loading(0.1 + 0.89*loaded);
                }
            }

            if (this.scene.data.$intro && this.env != 'GameDesigner'){
                let intro = await new VideoPlayer(gameEngine, {$go: this.scene.data.$intro, skipTransition: true, playInHud: true});
                gameEngine.activeObjects.add(intro.object);
                intro.video.addEventListener('pause',()=>{
                    intro.object.removeFromParent();
                    gameEngine.clickable.remove(intro.object); //TODO!!!!
                    gameEngine.activeObjects.visible = true;
                });
                intro.video.play();
            }else{
                gameEngine.activeObjects.visible = true;
            }

            gameEngine.dashboard?.loading(1)
            gameEngine.physics.start();
        },
        
        targetPointerDown(){
            this.pointerDownTime = performance.now();
        },
        targetClick(e){
            if (this.env == 'GameDesigner'){
                if (performance.now() - this.pointerDownTime < 200){
                    let intersects = gameEngine.intersect(e, this.$refs.target, gameEngine.activeObjects.children, true);
                    //console.log(intersects)
                    if (intersects.length){
                        //console.log('attaching controls to', intersects[0].object)
                        //gameEngine.transformControls.attach(intersects[0].object);
                        //console.log(this.sceneObjects[intersects[0].object.__pn_id])
                        this.objectsList[0] = this.sceneObjects[intersects[0].object.__pn_id]
                    }else{
                        gameEngine.transformControls.detach();
                    }
                }
            }else{
                gameEngine.onClick(e, this.$refs.target);
            }
        },

        targetPointer(e, t){
            gameEngine.onPointer(e, this.$refs.target, t);
        },

        setObjectAttributes(l, data, object, source, autoScaleFactor = 1){
            if (l[data.id]){
                ['position', 'scale', 'rotation'].forEach(p=>{
                    object[p].copy(l[data.id][p])
                })
            }else if (!data.type || data.type == 'GenericObject'){
                autoScale(object, autoScaleFactor);
            }
            l[data.id] = l[data.id] || {};
            ['position', 'scale', 'rotation', 'visible'].forEach(p=>{
                l[data.id][p] = object[p];
            })
            l[data.id].__o = object;
            l[data.id].__g = source;
            l[data.id].__title = data.title;
            object.__pn_id = data.id;
        },

        async toggleAnimation(animation){
            animation.playing = !animation.playing;
            gameEngine.playAnimation(gameEngine.scene, animation.a, animation.playing);
        },
        resize(){
            let r = this.$refs.target;
            gameEngine.resize(r.clientWidth, r.clientHeight);
            //this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
        },

        control(){
            gameEngine.hero.lockControls();
        }
    }
}