import { BoxGeometry, Mesh, MeshBasicMaterial, Group, VideoTexture } from 'three';
import { EventManager } from '@/lib/EventManager';

class PuzzleGame1 extends EventManager {
    emits = ['finish', 'interaction']
    constructor(engine, data) {
        super();
        return new Promise(async (resolve, reject)=>{
            let w = data.w, h = data.h, wh = w*h;
            let container = new Group();
            const aq = engine.motionQueue;
            const pr = [[0, 2], [0, -1], [0, 1], [1, 0], [-1, 0], [0, 0]];
            let d = 1.2;

            let bm = new BoxGeometry(1, 1, 1);

            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 });
                })
            }else{
                map = await engine.loadTexture(data.$go.asset.name);
            }

            let material = new MeshBasicMaterial({
                map,
                // roughness:1, metalness:0,
                // normalMap: await engine.loadTexture('NormalMap.png', '/static/textures/'),
            });
            //material.map.encoding = sRGBEncoding;

            for (let i = 0; i < wh; i++) {
                let b = bm.clone();
                let uv = b.getAttribute('uv');

                //0-8->R, 8-16->L, 16-24->Top, 24-32->Bottom, 32-40->Front, 40-48->Back
                let x = i % w, y = Math.trunc(i / w);
                let idxs = [(i+1) % wh, (i+2)%wh, (i+3)%wh, (i+4)%wh, i, y*w + w - x -1]
                for (let i = 0; i < 6; i++) {
                    let idx = idxs[i];
                    let x = idx % w, y = Math.trunc(idx / w)
                    let s = [x / w, y / h];
                    //top left
                    uv.array[8 * i] = s[0];
                    uv.array[8 * i + 1] = s[1] + 1 / h;
                    //top right
                    uv.array[8 * i + 2] = s[0] + 1 / w;
                    uv.array[8 * i + 3] = s[1] + 1 / h;
                    //bottom left
                    uv.array[8 * i + 4] = s[0];
                    uv.array[8 * i + 5] = s[1];
                    //bottom right
                    uv.array[8 * i + 6] = s[0] + 1 / w;
                    uv.array[8 * i + 7] = s[1];
                }

                let mesh = new Mesh(b, material);
                mesh.position.set((i % w) * d, (Math.trunc(i / w)) * d, 0);
                let ri = Math.floor(Math.random() * 5);
                mesh.rotation.set(pr[ri][0] * Math.PI / 2, pr[ri][1] * Math.PI / 2, 0);
                mesh._ri = ri;
                if (idxs[4] == idxs[5]){
                    mesh._dd = true;
                }
                container.add(mesh);
            }

            // container.children[0].onBeforeRender = () => {
            //     this.update();
            // };

            var check = () => {
                let i = 0;
                container.children.forEach(o=>{
                    if (o._ri == 5 || o._dd && o._ri == 0) i++;
                })
                return i == wh;
            };

            let clickFn = (i) => {
                this.dispatchEvent({type:'interaction'});
                if (vi?.paused) vi.play();
                if (!this.done && !aq.isActive(i.object)) {
                    i.object._ri = (i.object._ri + 1) % 6;
                    aq.add({
                        o: i.object,
                        a: { rotation: { x: pr[i.object._ri][0] * Math.PI / 2, y: pr[i.object._ri][1] * Math.PI / 2 } },
                        t: .5,
                        s: 'PG2'
                    });
                }
            };

            container.children.forEach(c => {
                engine.clickable.add(c, clickFn);
            });

            this.update = () => {
                if (aq.isIdle('PG2') && !this.done && check()) {
                    this.done = true;
                    container.children.forEach((c, i) => {
                        aq.add({
                            o: c,
                            a: { position: { x: i % w, y: Math.trunc(i/w)} },
                            t: 1,
                            f: i == 0 ? ()=>{
                                this.dispatchEvent({type:'finish'})
                            } : undefined,
                            s: 'PG2'
                        });
                    });
                }
            };

            engine.addEventListener('beforeRender', this.update)

            this.object = engine.meshUtils.centerOrigin(container);

            this.dispose = () => {
                //console.log('disposing PG1')
                engine.meshUtils.clearMaterial(material);
                bm.dispose();
                super.dispose();
            }

            resolve(this);
        })
    }
}

export { PuzzleGame1 }