import { Color, Group, DoubleSide, RepeatWrapping, MeshStandardMaterial, VideoTexture } from "three"
import { EventManager } from '@/lib/EventManager';
import Utils from "#/app/Utils";

class ClassicPuzzle extends EventManager {
    emits = ['finish', 'interaction']
    constructor(engine, data, objPrefix='Plane'){
        super();
        const container = new Group();
        const that = this;
        return new Promise(async (resolve, reject)=>{
            let gltf = await engine.load(`puzzle-${data.dimension}.glb`, '/static/meshes/classic-puzzle/');
            let map, vi;
            if (data.$go.type == 'video'){
                await new Promise((resolve, reject)=>{
                    vi = document.createElement('video');
                    vi.src = engine.assetPath + data.$go.asset.name;
                    vi.muted = true;
                    vi.addEventListener('loadedmetadata', async ()=>{
                        map = new VideoTexture( vi );
                        resolve();
                    }, { once: true }); //'once' is needed in order to avoid memory leaks (the listener gets removed after called)
                })
            }else{
                map = await engine.loadTexture(data.$go.asset.name);
            }
            
            map.wrapS = RepeatWrapping;
            map.wrapT = RepeatWrapping;
            map.flipY = false;
            map.needsUpdate = true;
            
            let dragZone = gltf.scene.getObjectByName('DragZone');
            dragZone.material.side = DoubleSide;
            let pCount = 0;
            let eventsFn= {
                async start(){
                    that.dispatchEvent({type:'interaction'});
                    if (vi?.paused) {
                        await vi.play();
                        await Utils.wait(5000);
                        done != pCount && vi.pause();
                    }
                },
                drag(){},
                end(e){
                    if (Math.abs(e.o.position.x)<.1 && Math.abs(e.o.position.y)<.1){
                        e.o.position.set(0,0,0);
                        engine.draggable.remove(e.o);
                        e.o.material = doneMaterial;
                        done++;
                        if (done == pCount){
                            doneMaterial.emissiveIntensity = .5;
                            engine.motionQueue.add({
                                o: doneMaterial,
                                t:1.5,
                                d:1,
                                a:{ emissiveIntensity:0 },
                                f:()=>{
                                    that.dispatchEvent({type:'finish'})
                                }
                            });
                        }
                    }
                }
            }
            dragZone.visible = false;

            let defaultMaterial = new MeshStandardMaterial({
                map, emissiveIntensity: 0.05, lightMapIntensity: 10, side:DoubleSide, roughness: 1, metalness: 0,
            })

            gltf.scene.children.forEach((o, i)=>{
                if (o.name.startsWith(objPrefix)){
                    let pp = o.clone();
                    container.add(pp);
                    defaultMaterial.emissiveMap ??= pp.material.emissiveMap;
                    pp.material = defaultMaterial;
                    pp.position.set(2*Math.random()-1, 2*Math.random() - 1, 0.01*(i+1));
                    engine.draggable.add(pp, dragZone, eventsFn);
                    pCount++;
                }
            });
            let doneMaterial = defaultMaterial.clone();
            doneMaterial.emissive = new Color(10,114,10);
            defaultMaterial.emissive = new Color(114,10,10);
            engine.motionQueue.add(
            {
                o: defaultMaterial,
                r: true,
                t:1.5,
                d:1,
                rd:true,
                a:{emissiveIntensity:function(k){return .05*Math.sin(k*3.14);}}
            });
            let done0 = container.children[Math.floor(Math.random() * container.children.length)];
            let done = 1;
            done0.material = doneMaterial;
            done0.position.set(0,0,0);
            engine.draggable.remove(done0)
            container.add(dragZone);
            this.object = engine.meshUtils.centerOrigin(container);
            this.dispose = () => {
                //console.log('disposing!!!!!!!')
                engine.meshUtils.clearMaterial(defaultMaterial);
                engine.meshUtils.clearObject(gltf.scene);
                super.dispose();
            }
            resolve(this);
        })
    }
}

export { ClassicPuzzle }