import { Group, MeshStandardMaterial, Mesh, SphereGeometry, Vector3 } from "three";
import { EventManager } from '@/lib/EventManager';

import { GenericObject } from "./GenericObject/GenericObject";
import { TextObject } from "./TextObject/TextObject";
import { ImageObject } from "./ImageObject/ImageObject";
import { GltfObject } from "./GltfObject";
import { CharacterObject } from "./CharacterObject/CharacterObject";
import { VideoPlayer } from "./VideoPlayer/VideoPlayer";
import { PuzzleGame1 } from "./PuzzleGame1/PuzzleGame1";
import { PuzzleGame2 } from "./PuzzleGame2/PuzzleGame2";
// import { Game3 } from "./games/Game3";
import { PuzzleGame4 } from "./PuzzleGame4";
import { ClassicPuzzle } from "./ClassicPuzzle/ClassicPuzzle";
import { PairMatchingGame } from "./PairMatchingGame/PairMatchingGame";
import { SingleQuestion } from "./SingleQuestion/SingleQuestion";
// import { Game5 } from "./games/Game5";
// import { Game6 } from "./games/Game6";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
import { Particles } from "./Particles/Particles";
import { SceneSwitcher } from "./SceneSwitcher/SceneSwitcher";
import { Teleporter } from "./Teleporter/Teleporter";
import { GameEngine  } from "@/lib/GameEngine";

const InteractiveObjectsImports = { 
    GenericObject, CharacterObject, TextObject, ImageObject, GltfObject, VideoPlayer, Particles, SceneSwitcher, Teleporter,
    PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame, ClassicPuzzle, PairMatchingGame, SingleQuestion
};

class InteractiveObject extends EventManager{
    constructor(engine, obj) {
        super();
        this.minPoints = 0;
        this.maxPoints = obj.points || 0;
        this.name = obj.name;
        return new Promise(async (resolve, reject) => {
            //TODO: refactor this!
            switch (obj.type || 'GenericObject') {
                case 'Group':
                    this.object = new Group();
                    for (let g of obj.group){
                        let gameMesh = await new InteractiveObject(engine, g);
                        this.object.add(gameMesh.object);
                    }
                    break;
                case 'GltfObject':
                case 'TextObject':
                case 'ImageObject':
                case 'GenericObject':
                case 'CharacterObject':
                case 'VideoPlayer':
                case 'PuzzleGame1':
                case 'PuzzleGame2':
                case 'MazeQuizGame':
                case 'ClassicPuzzle':
                case 'PairMatchingGame':
                case 'SingleQuestion':
                case 'Particles':
                case 'SceneSwitcher':
                case 'Teleporter':
                    this.io = await new InteractiveObjectsImports[obj.type](engine, obj);
                    this.source = this.io.source || this.io;
                    this.object = this.io.object;
                    this.emits = this.io.emits;
                    // this.io.emits?.forEach(event=>{
                    //     this.io.addEventListener?.(event, this.dispatchEvent.bind(this))
                    // })
                    this.io.forwardEvents?.(this);
                    if (this.emits?.includes('interaction')){
                        //process first interaction
                        this.io.once('interaction', ()=>{
                            if (obj.introText){
                                engine.dashboard.updateText(obj.introText, {hideOnFinish: true});
                            }
                        })
                        this.io.addEventListener('interaction', ()=>{
                            engine.tm?.setGameObject(obj.id);
                        })
                    }
                    ['minPoints', 'maxPoints'].filter(p=>this.io[p] !== undefined).forEach(p=> this[p] = this.io[p] )
                    break;
            }
            if (obj.shouldBeLocked){
                this.object = engine.meshUtils.wrapInGroup(this.object)
                this.activator = new (obj.activationType == 'unlock' ? LockActivator : VisibilityActivator)(engine, this.object);
                this.activator.deactivate();
            }else{
                this.object.dispatchEvent({type: 'io-activate'})
            }
            engine.meshUtils.assignParams(this.object, obj);
            if (obj.motion){
                engine.motionQueue.add({
                    o: this.object, ...obj.motion
                });
            }
            
            if (obj.distance) {
                engine.hideIfFar(this.object, obj.distance)
            }
            this.object.userData._io = this;
            resolve(this);
        });
    }
    dispose(){
        super.dispose();
        this.io?.dispose?.();
    }
}

class VisibilityActivator{
    constructor(engine, group){
        this.deactivate = function(){
            group.visible = false;
            group.__active = false;
            group.dispatchEvent({type: 'io-deactivate'})
        }
        this.activate = function(){
            group.visible = true;
            group.__active = true;
            group.dispatchEvent({type: 'io-activate'})
        }
        this.isActive = function(){
            return group.__active;
        }
    }
}

class LockActivator{
    static materialLocked = new MeshStandardMaterial({
        transparent: true, opacity:1, color: 0xaaaaaa
    })
    constructor(engine, group){
        const bckGeometry = new SphereGeometry(engine.meshUtils.getBoundingBoxMaxLength(group.userData.bbox)/2,8,8)
        const bckMesh = new Mesh(bckGeometry, LockActivator.materialLocked);
        bckMesh.visible = false;
        group.add(bckMesh)
        function animate(){
            engine.motionQueue.add(
                { o: bckMesh, a:{ 
                    material: { opacity: k=>0.11 + 0.52*Math.sin(k*Math.PI)},
                    rotation: { y: k=>k*Math.PI*2},
                    scale: (k,s)=>s.setScalar(1+0.11*Math.sin(2*k*Math.PI))
                }, r: true, t: 3 },
            )
        }
        this.object = bckMesh;
        this.deactivate = function(){
            bckMesh.visible = true;
            //bckMesh.material.needsUpdate = true;
            engine.motionQueue.clear(bckMesh);
            group.__active = false;
            animate();
            group.dispatchEvent({type: 'io-deactivate'})
        }
        this.activate = function(){
            bckMesh.visible = false;
            engine.motionQueue.clear(bckMesh);
            group.__active = true;
            group.dispatchEvent({type: 'io-activate'})
        }
        this.isActive = function(){
            return group.__active;
        }
    }
}
GameEngine.loadTexture('locked.webp', '/static/textures/', null, [LockActivator.materialLocked, 'alphaMap'])

const InteractiveObjectTypes = [
    'GenericObject', 
    'CharacterObject', 
    'PuzzleGame1', 
    'PuzzleGame2', 
    'MazeQuizGame', 
    'ClassicPuzzle', 
    'PairMatchingGame', 
    'SingleQuestion', 
    'VideoPlayer', 
    'Particles', 
    'SceneSwitcher',
    'Teleporter',
    'TextObject',
    'ImageObject'
];

export { InteractiveObject, InteractiveObjectTypes, InteractiveObjectsImports }