import { Raycaster, Vector3 } from "three"

class Draggable{
    constructor(engine, defaultDistance){
        const objects = [];
        const raycaster = new Raycaster();
        let v = new Vector3(), cv = new Vector3();
        let dragging = null;
        this.add = function(object, dragZone, fn, distance){
            objects.push(object);
            object._draggable = {fn, dragZone, distance: (distance || defaultDistance) * engine.scale}
        }

        this.remove = function(object){
            delete object._draggable;
            objects.splice(objects.indexOf(object), 1);
        }

        this.handleController = function(controller, action){
            raycaster.setFromXRController(controller);
            this.handle(action);
        }

        this.handleMouse = function (mouse, action) {
            raycaster.setFromCamera(mouse, engine.camera);
            this.handle(action);
        };

        this.handle = function(action){
            if (['start', 'selectstart'].includes(action)){
                let forExecute = [];
                let target = engine.hero?.model || engine.cameraWorld;
                target.getWorldPosition(cv);
                objects.filter(o=>{
                    do {
                        if (o.__active === false || o.visible === false) return false;
                        o = o.parent;
                    } while (o);
                    return true;
                }).forEach(o=>{
                    o.getWorldPosition(v);
                    if (cv.distanceTo(v) <= o._draggable.distance){
                        const intersects = raycaster.intersectObject(o);
                        if (intersects[0]) forExecute.push({o, i:intersects[0]})
                    }
                });

                if (forExecute[0]) {
                    let s = forExecute.sort((a,b)=>a.i.distance-b.i.distance)[0];
                    s.o._draggable.fn.start && s.o._draggable.fn.start(s.i);
                    dragging = s;
                    dragging.zone = raycaster.intersectObject(s.o._draggable.dragZone)[0];
                }
            }else if (['end', 'selectend'].includes(action) && dragging){
                dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging);
                dragging = null;
            }else if(['drag', 'move'].includes(action) && dragging){
                const intersect = raycaster.intersectObject(dragging.o._draggable.dragZone)[0];
                if (intersect?.uv && dragging.zone?.uv){
                    dragging.o.position.x += -4*(dragging.zone.uv.x - intersect.uv.x);
                    dragging.o.position.y += 4*(dragging.zone.uv.y - intersect.uv.y);
                    dragging.o._draggable.fn.drag && dragging.o._draggable.fn.drag(dragging);
                    dragging.zone = intersect;
                }
            }
        }
    }
}

export { Draggable }