import { Raycaster, Vector3 } from "three"

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

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

        this.update = function(pointer, camera, action){
            raycaster.setFromCamera(pointer, camera);
            if (action == 'start'){
                let forExecute = [];
                objects.forEach(o=>{
                    o.getWorldPosition(v);
                    if (camera.position.distanceTo(v) <= o._draggable.distance && o.visible){
                        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 (action == 'end' && dragging){
                dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging);
                dragging = null;
            }else if(action == 'drag' && 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 }