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

class PuzzleGame2 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 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);
            }
            const material = new MeshBasicMaterial({
                map
            });
            const aq = engine.motionQueue;
            const m2 = new MeshBasicMaterial({
                map,
                transparent: true,
                opacity: 0.37
            });
            let last, lidx = w - 1;

            let container = new Group();
            let d = 1.2, p = [];

            function check() {
                if (p.length) {
                    for (let i = 0; i < p.length; i++) {
                        if (p[i] != i) return false;
                    }
                    return true;
                }
                return false;
            }

            function xch(x, y) {
                let temp = p[x];
                p[x] = p[y];
                p[y] = temp;
            }

            this.shuffle = function () {
                function getMoves() {
                    let m = [], pl = p[lidx];
                    let xl = pl % w, yl = ~~(pl / h);
                    if (xl > 0) m.push(p.indexOf(pl - 1));
                    if (xl < w - 1) m.push(p.indexOf(pl + 1));
                    if (yl > 0) m.push(p.indexOf(pl - w));
                    if (yl < h - 1) m.push(p.indexOf(pl + w));
                    return m;
                }
                for (let i = 0; i < wh; i++) {
                    p[i] = i;
                }

                for (let iter = 0; iter < 73 * wh; iter++) {
                    let m = getMoves();
                    xch(m[Math.floor(Math.random() * m.length)], lidx);
                }
                // while (p.length<9){
                //     let n = Math.floor(Math.random()*9);
                //     if (p.indexOf(n) == -1) p.push(n);
                // }
                p.forEach((e, i) => {
                    let x = e % w, y = ~~(e / h);
                    container.children[i].position.set(x * d, y * d, 0);
                });
            };

            for (let i = 0; i < wh; i++) {
                let bg = new BoxGeometry(1, 1, 1);
                let uv = bg.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 k = 0; k < 6; k++) {
                    let idx = idxs[k];
                    let x = idx % w, y = Math.trunc(idx / w)
                    let xp = x / w, yp = y / h;
                    //top left
                    uv.array[8 * k] = xp;
                    uv.array[8 * k + 1] = yp + 1 / h;
                    //top right
                    uv.array[8 * k + 2] = xp + 1 / w;
                    uv.array[8 * k + 3] = yp + 1 / h;
                    //bottom left
                    uv.array[8 * k + 4] = xp;
                    uv.array[8 * k + 5] = yp;
                    //bottom right
                    uv.array[8 * k + 6] = xp + 1 / w;
                    uv.array[8 * k + 7] = yp;
                }
                let mesh = new Mesh(bg, i != lidx ? material : m2);
                mesh.position.set(x * d, y * d, 0);
                container.add(mesh);
            }
            last = container.children[lidx];

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

            this.shuffle();

            let clickFn = (i) => {
                this.dispatchEvent({type:'interaction'});
                if (vi?.paused) vi.play();
                if (!this.done && !aq.isActive(i.object)) {
                    let idx = container.children.indexOf(i.object);
                    if (idx == lidx) return; //we ignore the empty cell
                    let xc = p[idx] % w, yc = ~~(p[idx] / h);
                    let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
                    if (Math.abs(xc - xl) + Math.abs(yc - yl) == 1) {
                        aq.add([{
                            o: i.object,
                            a: { position: { x: (xl - xc) * d, y: (yl - yc) * d } },
                            t: .3,
                            m: 'offset',
                            s: 'PG2'
                        },{
                            o: last,
                            a: { position: { x: (xc - xl) * d, y: (yc - yl) * d } },
                            t: .3,
                            m: 'offset',
                            s: 'PG2'
                        }]);
                        xch(idx, lidx);
                    }
                }
            };

            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) => {
                        last.material = material;
                        aq.add({
                            o: c,
                            a: { position: { x: i % w, y: ~~(i / h) } },
                            t: 1,
                            f: i == 0 ? ()=>{
                                this.dispatchEvent({type:'finish'})
                            } : undefined,
                            s: 'PG2'
                        });
                    });
                }
            };
            engine.addEventListener('beforeRender', this.update)
            
            this.object = engine.meshUtils.centerOrigin(container)
            resolve(this)
        });
    }
}

export { PuzzleGame2 };