import { InteractiveObject } from "@/components/InteractiveObjects/InteractiveObject";
import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer/VideoPlayer';
import { Hero } from "./Hero";
import { api } from "./Api";
import { EventManager } from "./EventManager";
class GameManager extends EventManager{
    constructor(engine, gameData, scenarioData, opts = {}){
        super();
        this.engine = engine
        return new Promise(async (resolve, reject)=>{
            if (typeof gameData != 'object'){
                gameData = (await api.game.load(gameData)).data;
            }
            if (!scenarioData){
                scenarioData = (await api.scenario.load(gameData.scenario)).data;
            }
            this.gameData = gameData;
            this.scenarioData = scenarioData;
            this.opts = opts;

            this.loadScene = async function(sceneId){
                let scene = scenarioData.scenes.find(sc=>sc.data.id == sceneId);
                gameData.scenes = gameData.scenes || {};
                gameData.scenes[sceneId] = gameData.scenes[sceneId] || {};
                gameData.scenes[sceneId].objects = gameData.scenes[sceneId].objects || {};
                let sceneObjects = gameData.scenes[sceneId].objects;
                engine.tm?.setGame(gameData.id);
                let intro;
                await engine.resetScene();
                engine.activeObjects.visible = false;
                await engine.dashboard?.ready;
                await this.expandScenarioData(scene);
                engine.dashboard?.initScene(scene, async ()=>{
                    if (scene.data.$audio){
                        await engine.playAmbientSound(scene.data.$audio.asset.name);
                        engine.ambientSound.setVolume( 0.5 );
                    }
                    engine.tm?.setScene(scene.data.id);
                    if (intro){
                        intro.play();
                    }else{
                        engine.activeObjects.visible = true;
                    }
                });
                engine.dashboard?.loading(0.05);

                //this is needed cause when mounted canvas has different size
                //this.resize();
                if (scene.data.$environment){
                    await engine.loadPanorama(scene.data.$environment.asset.name);
                }
                if (scene.data.$scene){
                    let env = await engine.load(scene.data.$scene.asset.name);
                    this.setObjectAttributes(sceneObjects, scene.data, env.scene, env, null);
                    if (engine.opts.mode != 'GameDesigner'){
                        engine.physics.apply(env.scene)
                    }
                    engine.activeObjects.add(env.scene);
                }
                let expectToFinish = 0, finished = 0, iobjs = [];
                if (scene.data.items){
                    let loaded = 0, maxPoints = 0;
                    for (let i of scene.data.items) {
                        //this.debug('Loading', i.data.id);
                        if (engine.opts.mode != 'GameDesigner'){
                            if (i.data.activationTriggers?.length || i.data.activationScore){
                                i.data.shouldBeLocked = true;
                            }
                        }
                        let io = await new InteractiveObject(engine, i.data)
                        iobjs.push({io, data: i.data})
                        //i.__io = io;
                        if (io.emits?.includes('finish')){
                            expectToFinish++;
                        }
                        this.setObjectAttributes(sceneObjects, i.data, io.object, io.source, 1);
                        engine.activeObjects.add(io.object);
                        maxPoints += io.maxPoints;
                        if (engine.opts.mode != 'GameDesigner'){
                            if (i.data.$go?.type == 'player3d'){
                                let hero = new Hero(engine, io);
                            }else{
                                if (i.data.collisionType == 'basic'){
                                    let bb = engine.meshUtils.getBoundingBox(io.object);
                                    let bbs = engine.meshUtils.getBoundingBoxSize(bb);
                                    engine.physics.add(io.object, 'fixed', false, undefined, 'capsule', {
                                        radius: Math.max(bbs.x, bbs.z)/2, halfHeight: bbs.y/2
                                    })
                                }else if (i.data.collisionType == 'objectDefinied'){
                                    engine.physics.apply(io.object)
                                }
                            }
                            io.addEventListener('finish', ()=>{
                                finished ++;
                                if (!i.data.pointsGiven){
                                    engine.dashboard?.addPoints(i.data.points)
                                }
                                i.data.pointsGiven = true;
                                iobjs.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.__active === false &&
                                        engine.dashboard.points > di.data.activationScore){
                                            di.io.activator.activate();
                                        }
                                });
                                // if (finished == expectToFinish){
                                //     //GO TO NEXT LEVEL
                                //     this.debug('LEVEL FINISHED')
                                // }
                                engine.tm?.setGameObject(null);
                            });
                            io.addEventListener('sceneSwitch', (e)=>{
                                //this.scenesList = [this.scenes.find(s=>s.data.id == e.scene)]
                                //switch to next scene:
                                if (e.scene == '__end__'){
                                    let score = {
                                        value: engine.dashboard.points,
                                        max: maxPoints
                                    }
                                    this.dispatchEvent({ 
                                        type: 'the-end',
                                        score
                                    });
                                    engine.tm?.completeGame(gameData.id, score);
                                }else{
                                    this.loadScene(e.scene);
                                }
                                iobjs.forEach(di=>di.io.dispose?.());
                            })
                        }
                        loaded += 1/scene.data.items.length
                        engine.dashboard?.loading(0.1 + 0.89*loaded);
                    }
                }

                if (engine.opts.mode == 'GameDesigner'){
                    engine.activeObjects.visible = true;
                }else if (scene.data.$intro){
                    intro = await new VideoPlayer(engine, {
                        $go: scene.data.$intro, 
                        skipTransition: true, 
                        playInHud: true,
                        immersive: true
                    });
                    engine.activeObjects.add(intro.object);
                    intro.video.addEventListener('pause',()=>{
                        intro.object.removeFromParent();
                        engine.clickable.remove(intro.object); //TODO!!!!
                        engine.activeObjects.visible = true;
                    });
                }

                engine.dashboard?.loading(1)
                engine.physics.start();
                //console.log('Scene loaded', engine.renderer.info.memory)
            }
            resolve(this);
        })
    }

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

    setObjectAttributes(sceneObjects, data, object3d, source, autoScaleFactor = 1){
        if (sceneObjects[data.id]){
            ['position', 'scale', 'rotation'].forEach(p=>{
                object3d[p].copy(sceneObjects[data.id][p])
            })
        }else if (!data.type || data.type == 'GenericObject'){
            console.log('autoscaling', data.id, autoScaleFactor);
            this.engine.meshUtils.autoScale(object3d, autoScaleFactor);
        }
        sceneObjects[data.id] ??= {};
        if (this.opts.onObjectLoad){
            this.opts.onObjectLoad(sceneObjects, data, object3d, source)
        }
        if (object3d.hasIndividualChildren){
            sceneObjects[data.id].children ??= {};
            object3d.traverse(c=>{
                if (c.isIndividual){
                    this.setObjectAttributes(sceneObjects[data.id].children, {
                        id: c.name,
                        title: `${data.title}: ${c.name}`,
                        type: data.type,
                        level: (data.level || 0) + 1
                    }, c, c, null);
                }
            })
        }
    }
}

export{ GameManager }