import { Group, EventDispatcher, MeshStandardMaterial, Mesh, SphereGeometry } from "three";

import { GenericObject } from "./GenenricObject";
import { TextObject } from "./TextObject";
import { ImageObject } from "./ImageObject";
import { CharacterObject } from "./CharacterObject";
import { VideoPlayer } from "./VideoPlayer";
import { PuzzleGame1 } from "./PuzzleGame1";
import { PuzzleGame2 } from "./PuzzleGame2";
// import { Game3 } from "./games/Game3";
import { PuzzleGame4 } from "./PuzzleGame4";
import { ClassicPuzzle } from "./ClassicPuzzle";
// import { Game5 } from "./games/Game5";
// import { Game6 } from "./games/Game6";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
import { Particles } from "./Particles";
import { assignMaterial, assignParams, wrapInGroup, getBoundingBoxMaxLength } from "@/lib/MeshUtils";
import { GameEngine } from "@/lib/GameEngine";

const InteractiveObjectsImports = { 
    GenericObject, CharacterObject, TextObject, ImageObject, VideoPlayer, Particles,
    PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame, ClassicPuzzle
};

class InteractiveObject extends EventDispatcher{
    constructor(gameEngine, obj) {
        super();
        this.name = obj.name;
        return new Promise(async (resolve, reject) => {
            switch (obj.type || 'GenericObject') {
                case 'Group':
                    this.object = new Group();
                    for (let g of obj.group){
                        let gameMesh = await new InteractiveObject(gameEngine, g);
                        this.object.add(gameMesh.object);
                    }
                    break;
                case 'Gltf':
                    let gltf = await gameEngine.load(obj.value, '');
                    let gltfObj = gltf.scene;
                    gltfObj.traverse(function (object) {
                        object.frustumCulled = false;
                        if (obj.name && obj.name == object.name) {
                            gltfObj = object;
                        }
                        // object.castShadow = true;
                        // object.receiveShadow = true;
                    });
                        
                    assignMaterial(gltfObj, obj);
                    if (gltf.animations && gltf.animations.length) {
                        let mixer = new AnimationMixer(gltfObj);
                        gameEngine.mixers.push(mixer);
                        let action = mixer.clipAction(gltf.animations[0]);
                        action.setLoop(LoopPingPong);
                        action.play();
                    }
                    this.object = gltfObj;
                    this.source = gltf;
                    break;
                    
                case 'TextObject':
                case 'ImageObject':
                case 'GenericObject':
                case 'CharacterObject':
                case 'VideoPlayer':
                case 'PuzzleGame1':
                case 'PuzzleGame2':
                case 'MazeQuizGame':
                case 'ClassicPuzzle':
                case 'Particles':
                    this.io = await new InteractiveObjectsImports[obj.type](gameEngine, 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))
                    })
                    break;
            }
            if (obj.shouldBeLocked){
                this.object = wrapInGroup(this.object)
                this.locker = new Locker(gameEngine, this.object);
                this.locker.lock();
            }
            assignParams(this.object, obj);
            resolve(this);
        });
    }
}

class Locker{
    static materialLocked = new MeshStandardMaterial({
        transparent: true, opacity:1, color: 0xaaaaaa
    })
    constructor(engine, group){
        const bckGeometry = new SphereGeometry(getBoundingBoxMaxLength(group.userData.bbox)/2,8,8)
        const bckMesh = new Mesh(bckGeometry, Locker.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.lock = function(){
            bckMesh.visible = true;
            //bckMesh.material.needsUpdate = true;
            engine.motionQueue.clear(bckMesh);
            group.__locked = true;
            animate();
        }
        this.unlock = function(){
            bckMesh.visible = false;
            engine.motionQueue.clear(bckMesh);
            group.__locked = false;
        }
    }
}
GameEngine.loadTexture('locked.webp', '/static/textures/', null, [Locker.materialLocked, 'alphaMap'])

const InteractiveObjectTypes = [
    {
        id: 'CharacterObject', name: 'Character'
    }, {
        id: 'PuzzleGame1', name: 'Puzzle Game 1'
    },{
        id: 'PuzzleGame2', name: 'Puzzle Game 2'
    },{
        id: 'MazeQuizGame', name: 'Maze Quiz Game'
    },{
        id: 'ClassicPuzzle', name: 'Classic Puzzle Game'
    },{
        id: 'VideoPlayer', name: 'Video Player'
    },{
        id: 'Particles', name: 'Particles'
    }
];

export { InteractiveObject, InteractiveObjectTypes, InteractiveObjectsImports }