import * as MU from "./MathUtils.js";
import M3 from "./M3.js";
import V2 from "./V2.js";
import {getPixelPosition, getMousePositionInCanvas, getTouchPositionInCanvas} from "./InputEventsHandler.js";

/**
 * Clase auxiliar para la manipulación de vectores usando el ratón.
 * @author Melissa Méndez Servín.
 */

/**
 * MouseVector
 * 
 * Constructor auxiliar para manejar los eventos de  movimientos de 
 * vectores 2D.
 * @param {} gl 
 * @param {*} radius 
 * @param {*} unit 
 * @param {*} vector 
 */
function MouseVector(gl, radius, unit, vector, limits=null, onlyScale=false, sPoint=null){
    this.gl = gl;
    this.isInRegion = false;
    this.showRegion = false;
    this.onlyScale = onlyScale;
    this.unit = unit;
    this.basis = vector;
    this.vector = vector;
    this.auxVec = vector;
    this.sPoint = sPoint || new V2(0,0);
    this.rCircle = radius;
    this.theta = Math.atan2(vector.y, vector.x);
    this.width = gl.canvas.width;
    this.height = gl.canvas.height;
    this.limits = limits || {l: -0.5, r: 0.5, t: 0.5, b: -0.5}; 
    this.projectionMatrix = M3.projection(this.limits.l * this.width, 
                                          this.limits.r * this.width, 
                                          this.limits.t * this.height, 
                                          this.limits.b * this.height);
    this.rDist = getRadiusPixelLength(radius, this.projectionMatrix, this.width, this.height, this.unit);
    //console.log(this.vector, this.sPoint);
}
/**
 * Devuelve las de pantalla aproximadas al punto inicial del vector de un objeto
 * tipo MouseVector.
 */
MouseVector.prototype.sPointPixelPosition = function (){
    return getPixelPosition(this.sPoint, this.projectionMatrix, this.width, this.height, this.unit);
}

MouseVector.prototype.vectorPixelPosition = function (){
    return getPixelPosition(this.sPoint.add(this.vector), this.projectionMatrix, this.width, this.height, this.unit);
}
MouseVector.prototype.auxVectorPixelPosition = function (){
    return getPixelPosition(this.sPoint.add(this.auxVec), this.projectionMatrix, this.width, this.height, this.unit);
}
/**
 * Captura los eventos del mouse y actualiza los valores del vector 2D de acuerdo
 * a la posición del mouse.
 * @param {*} canvas 
 * @param {*} mouseVectors 
 * @param {*} draw_callback 
 */
function registerMouseEvents2DVec(canvas, mouseVectors, draw_callback) {
    let currentPosition = null;
    if(((window.hasOwnProperty) && (window.hasOwnProperty("ontouchstart"))) || ("ontouchstart" in window)){
        console.log("TOUCH");
        let passiveIfSupported = false;
        try {
        window.addEventListener("test", null,
            Object.defineProperty(
            {},
            "passive",
            {
                get: function() { passiveIfSupported = { passive: true }; }
            }
            )
        );
        } catch(err) {}

        canvas.addEventListener("touchstart", (evt) => {
            if(passiveIfSupported == false)
                evt.preventDefault();
            var initialPosition = getTouchPositionInCanvas(evt, canvas);
            start2DEvent(initialPosition, mouseVectors, draw_callback);
            canvas.addEventListener("touchmove", touchmove, passiveIfSupported);
        },  passiveIfSupported);
    
        window.addEventListener("touchend", (evt) => {
            if(passiveIfSupported == false)
                evt.preventDefault();
            currentPosition = getTouchPositionInCanvas(evt, canvas);
            finish2DEvent(currentPosition, mouseVectors, draw_callback);
            canvas.removeEventListener("touchmove", touchmove);v
        }, false);

        function touchmove(evt) {
            if(passiveIfSupported == false)
                evt.preventDefault();
            currentPosition = getTouchPositionInCanvas(evt, canvas);
            move2DEvent(currentPosition, mouseVectors, draw_callback);
        }
    }
    else{
        canvas.addEventListener("mousedown", (evt) => {
            var initialPosition = getMousePositionInCanvas(evt, canvas);
            start2DEvent(initialPosition, mouseVectors, draw_callback);
            canvas.addEventListener("mousemove", mousemove);
        });
    
        window.addEventListener("mouseup", (evt) => {
            currentPosition = getMousePositionInCanvas(evt, canvas);            
            finish2DEvent(currentPosition, mouseVectors, draw_callback);
            canvas.removeEventListener("mousemove", mousemove);
        });
        function mousemove(evt) {
            currentPosition = getMousePositionInCanvas(evt, canvas);
            move2DEvent(currentPosition, mouseVectors, draw_callback);
        }
    }
}

function start2DEvent(initialPosition, mouseVectors, draw_callback){
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];
        if (isInCircle(mouseVector, initialPosition)) {
            mouseVector.isInRegion = true;
            //console.log("IN REGION");
        }else 
            mouseVector.showRegion = true;
    }
    draw_callback();
}
function finish2DEvent(currentPosition, mouseVectors, draw_callback){
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];
        if(mouseVector.isInRegion) {
            updateAuxVector(mouseVector, currentPosition);
            mouseVector.isInRegion = false;
        }
        mouseVector.showRegion = false;
    }
    draw_callback();
}
function move2DEvent(currentPosition, mouseVectors, draw_callback){
    var dontShow = false;
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];

        if(mouseVector.isInRegion) {
            dontShow = true;
            updateVector(mouseVector, currentPosition);  
        }
    }
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];
        if(dontShow)
            mouseVector.showRegion = false;
    }
    
    draw_callback();
}
function updateAuxVector(mouseVector, currentPosition){
    updateVector(mouseVector, currentPosition);
    mouseVector.auxVec = mouseVector.vector;
}
function updateVector(mouseVector, currentPosition){
    if(mouseVector.onlyScale){
        const radius = getRadiusX(mouseVector, currentPosition);
        mouseVector.vector = new V2( radius * MU.cos(mouseVector.theta), 
                                     radius * MU.sin(mouseVector.theta));
    }
    else{
        var diff = getDistance(mouseVector, currentPosition, mouseVector.unit);
        mouseVector.vector = mouseVector.auxVec.movePoints(diff.x, diff.y);
    }
}
function getDistance(mouseVector, currentPosition, unit){
    var auxVecPos = mouseVector.auxVectorPixelPosition();
    //console.log("Aux",mouseVector.auxVec, auxVecPos);
    var dist = new V2(currentPosition.x - auxVecPos.x, 
                      currentPosition.y - auxVecPos.y);   
    return new V2((dist.x/unit) + 1 - 1, (-dist.y/unit) + 1 - 1);
}

function getRadiusX(mouseVector, currentPosition){
    var sPointCoord = mouseVector.sPointPixelPosition();
    var diff = currentPosition.x - sPointCoord.x;   
    return diff/mouseVector.unit; 
}
/**
 * Calcula el radio con respecto a la posición actual del mouse.
 * @param {*} mouseVector 
 * @param {*} currentPosition 
 */
function getRadius(mouseVector, currentPosition){
    var sPointCoord = mouseVector.sPointPixelPosition();
    var diff = new V2(currentPosition.x - sPointCoord.x, currentPosition.y - sPointCoord.y);   
    var sign = (diff.x < 0) ? -1 : 1;
    var dist = Math.sqrt((diff.x-mouseVector.rCircle) ** 2 + (diff.y-mouseVector.rCircle) ** 2);
    
    return sign * dist/mouseVector.unit; 
}

function getRadiusPixelLength(radius, projectionMatrix, width, height, unit){
    let centerCoord = getPixelPosition( new V2(0, 0), projectionMatrix, width, height, unit);
    var rCoord = getPixelPosition( new V2(0, radius), projectionMatrix, width, height, unit);
    return Math.sqrt((centerCoord.x - rCoord.x) ** 2 + (centerCoord.y - rCoord.y) ** 2);
}
/**
 * 
 * @param {*} mouseVector 
 * @param {*} mousePosition 
 */
function isInCircle(mouseVector, mousePosition) {
    const {width, height} = mouseVector.gl.canvas;
    mouseVector.width = width;
    mouseVector.height = height;
    
    mouseVector.projectionMatrix = M3.projection(mouseVector.limits.l * width, 
                                                 mouseVector.limits.r * width, 
                                                 mouseVector.limits.t * height, 
                                                 mouseVector.limits.b * height);
         
    let centerPoint = new V2(mouseVector.sPoint.x + mouseVector.vector.x, mouseVector.sPoint.y + mouseVector.vector.y);
    const unit = mouseVector.unit;

    var centerCoord = getPixelPosition(centerPoint, mouseVector.projectionMatrix, width, height, unit);  
    var dist = Math.sqrt((centerCoord.x - mousePosition.x) ** 2 + (centerCoord.y - mousePosition.y) ** 2);
    return dist <= mouseVector.rDist;
}
/**
 * MousePointer
 * 
 * Constructor auxiliar para manejar eventos de puntos con un 
 * cierto radio.
 * 
 * @param {*} gl 
 * @param {*} point 
 * @param {*} radius 
 * @param {*} unit 
 * @param {*} limits 
 */
function MousePointer(gl, point, radius, unit, limits){
    this.gl = gl;
    this.vector = point;
    this.sPoint = new V2(0,0);
    this.isInRegion = false;
    this.showRegion = false;
    this.unit = unit;
    this.width = gl.canvas.width;
    this.height = gl.canvas.height;
    this.rCircle = radius;
    this.limits = limits || {l: -0.5, r: 0.5, t: 0.5, b: -0.5}; 
    this.projectionMatrix = M3.projection(this.limits.l * this.width, 
                                          this.limits.r * this.width, 
                                          this.limits.t * this.height, 
                                          this.limits.b * this.height);
    this.rDist = getRadiusPixelLength(radius, this.projectionMatrix, this.width, this.height, this.unit);
}

function registerMousePointerEvents(canvas, mousePointers, draw_callback) {
    if(((window.hasOwnProperty) && (window.hasOwnProperty("ontouchstart"))) || ("ontouchstart" in window)){
        console.log("TOUCH");
        let passiveIfSupported = false;
        try {
        window.addEventListener("test", null,
            Object.defineProperty(
            {},
            "passive",
            {
                get: function() { passiveIfSupported = { passive: true }; }
            }
            )
        );
        } catch(err) {}

        canvas.addEventListener("touchstart", (evt) => {
            if(passiveIfSupported == false)
                evt.preventDefault();
            var initialPosition = getTouchPositionInCanvas(evt, canvas);
            startMouseEvent(initialPosition, mousePointers, draw_callback);
        },  passiveIfSupported);
    
        window.addEventListener("touchend", (evt) => {
            if(passiveIfSupported == false)
                evt.preventDefault();
            finishMouseEvent(mousePointers, draw_callback);
        }, false);
    }
    else{
        canvas.addEventListener("mousedown", (evt) => {
            var initialPosition = getMousePositionInCanvas(evt, canvas);
            startMouseEvent(initialPosition, mousePointers, draw_callback);
        });
    
        window.addEventListener("mouseup", (evt) => {            
            finishMouseEvent(mousePointers, draw_callback);
        });
    }
}
function startMouseEvent(initialPosition, mousePointers, draw_callback){
    for(var i = 0; i < mousePointers.length; i ++){
        var mousePointer = mousePointers[i];
        if (isInCircle(mousePointer, initialPosition)) {
            mousePointer.isInRegion = true;
            console.log("IN REGION");
        }
    }
    draw_callback();
}
function finishMouseEvent(mousePointers, draw_callback){
    for(var i = 0; i < mousePointers.length; i ++){
        var mousePointer = mousePointers[i];
        if(mousePointer.isInRegion) {
            mousePointer.isInRegion = false;
        }
    }
    draw_callback();
}

function MousePoint(point, radius){
    this.point = point;
    this.radius = radius;
    this.isInRegion = false;
}
MousePoint.prototype.isInCircle = function(mousePosition){
    var dist = Math.sqrt((this.point.x - mousePosition.x) ** 2 + (this.point.y - mousePosition.y) ** 2);
    return dist <= this.radius;
}
function registerMousePointsEvents(canvas, mousePoints, draw_callback){
    let last = null;
    if(((window.hasOwnProperty) && (window.hasOwnProperty("ontouchstart"))) || ("ontouchstart" in window)){
        console.log("TOUCH");
        let passiveIfSupported = false;
        try {
        window.addEventListener("test", null,
            Object.defineProperty(
            {},
            "passive",
            {
                get: function() { passiveIfSupported = { passive: true }; }
            }
            )
        );
        } catch(err) {}

        canvas.addEventListener("touchstart", (evt) => {
            if(passiveIfSupported == false)
                evt.preventDefault();
            var initialPosition = getTouchPositionInCanvas(evt, canvas);
            last = startPointEvent(mousePoints, initialPosition, draw_callback);
            canvas.addEventListener("touchmove", touchmove, passiveIfSupported);
        },  passiveIfSupported);
    
        window.addEventListener("touchend", (evt) => {
            if(passiveIfSupported == false)
                evt.preventDefault();
            finishPointEvent(mousePoints, draw_callback);
            canvas.removeEventListener("touchmove", touchmove);
        }, false);
    }
    else{
        canvas.addEventListener("mousedown", (evt) => {
            var initialPosition = getMousePositionInCanvas(evt, canvas);
            last = startPointEvent(mousePoints, initialPosition, draw_callback);
            draw_callback();
            canvas.addEventListener("mousemove", mousemove);
        });

        window.addEventListener("mouseup", (evt) => {
            finishPointEvent(mousePoints, draw_callback);
            canvas.removeEventListener("mousemove", mousemove);
        });
    }
    function mousemove(evt) {
        if(last == null)
            return;
            console.log()
        var currentPosition = getMousePositionInCanvas(evt, canvas);
        movePointEvent(currentPosition, last, draw_callback);
    }
    function touchmove(evt) {
        if(last == null)
            return;
        var currentPosition = getTouchPositionInCanvas(evt, canvas);
        movePointEvent(currentPosition, last, draw_callback);
    }
}
function startPointEvent(mousePoints, initialPosition, draw_callback){
    let last = null;
    for(var i = 0; i < mousePoints.length; i ++){
        if (mousePoints[i].isInCircle(initialPosition)) {
            mousePoints[i].isInRegion = true;
            last = mousePoints[i];
            //console.log("ITS IN REGION");
        }
    }
    return last;
}
function movePointEvent(currentPosition, last, draw_callback){
    var currentPoint = last.point;
    var diff = new V2(currentPosition.x - currentPoint.x, currentPosition.y - currentPoint.y);
    last.point = currentPoint.movePoints(diff.x, diff.y);
    draw_callback();
}
function finishPointEvent(points, draw_callback){
    for(var i = 0; i < points.length; i ++){
        if(points[i].isInRegion) {
            points[i].auxPoint = points[i].point;
            points[i].isInRegion = false;
        }
    }
    draw_callback();
}
/*
function MouseVector3D(vector){
    this.vector = vector;
    this.isSelected = false;
}
function Mouse3DEvents(vectors, radius){
    this.vectors = vectors;
    this.radius = radius;
    this.inverse_PV_Matrix;
}
function registerMouse3DEvents(canvas, mouseVectors, draw_callback){
    
    canvas.addEventListener("mousedown", (evt) => {
        var initialPosition = getMousePositionInCanvas(evt, canvas);
        start2DEvent(initialPosition, mouseVectors, draw_callback);
        canvas.addEventListener("mousemove", mousemove);
    });

    window.addEventListener("mouseup", (evt) => {
        currentPosition = getMousePositionInCanvas(evt, canvas);            
        finish2DEvent(currentPosition, mouseVectors, draw_callback);
        canvas.removeEventListener("mousemove", mousemove);
    });
    function mousemove(evt) {
        currentPosition = getMousePositionInCanvas(evt, canvas);
        move2DEvent(currentPosition, mouseVectors, draw_callback);
    }
}
function start3DEvent(initialPosition, vectors, draw_callback){
    let 
    for(var i = 0; i < vectors.length; i ++){
        var mouseVector = mouseVectors[i];
        if (isInCircle(mouseVector, initialPosition)) {
            mouseVector.isInRegion = true;
            //console.log("IN REGION");
        }else 
            mouseVector.showRegion = true;
    }
    draw_callback();
}
function finish3DEvent(currentPosition, mouseVectors, draw_callback){
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];
        if(mouseVector.isInRegion) {
            updateAuxVector(mouseVector, currentPosition);
            mouseVector.isInRegion = false;
        }
        mouseVector.showRegion = false;
    }
    draw_callback();
}
function move3DEvent(currentPosition, mouseVectors, draw_callback){
    var dontShow = false;
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];

        if(mouseVector.isInRegion) {
            dontShow = true;
            updateVector(mouseVector, currentPosition);  
        }
    }
    for(var i = 0; i < mouseVectors.length; i ++){
        var mouseVector = mouseVectors[i];
        if(dontShow)
            mouseVector.showRegion = false;
    }
    
    draw_callback();
}
function intersectRayWithSphere(center, s_ray, ray_dir, sphere_radius){
    let sub = s_ray.add(center);
    let a = ray_dir.squareModule(); //ray_dir dot ray_dir
    let b = ray_dir.dotProuct(sub);
    let c = sub.dotProuct(sub).sub(sphere_radius**2);
    let discriminant = b*b - 4 *a*c;
    if(discriminant > 0)
        return true;
    return false;
}
function gerRayFromPixelPosition(position, inverse_PV_Matrix){
    let near = M4.transformVector(inverse_PV_Matrix, [position.x, position.y, -1, 1]);
    let far = M4.transformVector(inverse_PV_Matrix, [position.x, position.y, 1, 1]);
    let ray = far.sub(near).normalize();
    return { near: near,
             far: far,
             ray: ray};
}*/
export {
    MouseVector, 
    registerMouseEvents2DVec,
    MousePointer, 
    registerMousePointerEvents, 
    MousePoint, 
    registerMousePointsEvents
}