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

import { GenericObject } from "./GenenricObject";
import { TextObject } from "./TextObject";
import { ImageObject } from "./ImageObject";
import { GltfObject } from "./GltfObject";
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 { SceneSwitcher } from "./SceneSwitcher";
import { assignParams, wrapInGroup, getBoundingBoxMaxLength } from "@/lib/MeshUtils";
import { GameEngine  } from "@/lib/GameEngine";

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

class InteractiveObject extends EventManager{
    constructor(engine, 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(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 'Particles':
                case 'SceneSwitcher':
                    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);
                    break;
            }
            if (obj.shouldBeLocked){
                this.object = wrapInGroup(this.object)
                this.activator = new (obj.activationType == 'unlock' ? LockActivator : VisibilityActivator)(engine, this.object);
                this.activator.deactivate();
            }
            assignParams(this.object, obj);
            if (obj.motion){
                engine.motionQueue.add({
                    o: this.object, ...obj.motion
                });
            }
            
            if (obj.distance) {
                const o = this.object;
                let dstm = obj.distance;
                let v = new Vector3();
                o.visible = false;
                engine.addEventListener('beforeRender', function () {
                    o.getWorldPosition(v);
                    var dst = engine.camera.position.distanceTo(v);
                    if (dst <= dstm && !o.visible) {
                        o.visible = true;
                    }else if (dst > dstm && o.visible){
                        o.visible = false;
                    }
                });
            }
            resolve(this);
        });
    }
}

class VisibilityActivator{
    constructor(engine, group){
        this.deactivate = function(){
            group.visible = false;
            group.__active = false;
        }
        this.activate = function(){
            group.visible = true;
            group.__active = true;
        }
        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(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();
        }
        this.activate = function(){
            bckMesh.visible = false;
            engine.motionQueue.clear(bckMesh);
            group.__active = true;
        }
        this.isActive = function(){
            return group.__active;
        }
    }
}
GameEngine.loadTexture('locked.webp', '/static/textures/', null, [LockActivator.materialLocked, 'alphaMap'])

const InteractiveObjectTypes = [
    {
        id: 'GenericObject', name: 'Generic Game Object'
    },{
        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'
    },{
        id: 'SceneSwitcher', name: 'Scene Switcher'
    }
];

export { InteractiveObject, InteractiveObjectTypes, InteractiveObjectsImports }