import V2 from "../modules/V2.js";
import M3 from "../modules/M3.js";
import * as MU from "../modules/MathUtils.js";
import Simple2D from "../materials/Simple2D.js";
import Texture2D from "../materials/Texture2D.js";

/**
 * Clase auxiliar para generar la geometría de figuras 2D: 
 * Círculos, circunferencias, arcos, rebanada de círculo o arco sólido y rectángulo.
 * @author Melissa Méndez Servín.
 */
export default class Figure2D{
    constructor(gl, WebGL, type, unit, color, params, srcTextures=""){
        this.attributes = {  position:   { numComponents: 2,
                                           data: [] }};
        
        this.unit = unit;
        this.type = type;
        this.hasTexture = (srcTextures != "");

        switch(type){
            case 'circle':
                this.attributes.position.data = this.circle(params.r, params.numPoints);
                break;
            case 'circumference':
                this.attributes.position.data = this.circumference(params.r, params.thickness, params.numPoints);
                break;
            case 'arc':
                this.attributes.position.data = this.arc(params.sAngle, params.eAngle, params.r, params.thickness, params.numPoints);
                break;
            case 'sliceCircle':
                    this.attributes.position.data = this.sliceCircle(params.sAngle, params.eAngle, params.r, params.numPoints);
                    break;
            case 'rect':
                this.attributes.position.data = this.rectangle(params.sPoint, params.width, params.height);
                if(this.hasTexture) 
                    this.attributes.texcoord = { numComponents: 2,
                                                  normalize: true,
                                                  data : this.rectangleTexture()};
                break;
            default:
                throw 'No tenemos el tipo ' + type;
        }
        if(this.hasTexture)//Solo para 'rectangle'
            this.material = new Texture2D(gl, WebGL, this.attributes, srcTextures); 
        else
            this.material = new Simple2D(gl, WebGL, this.attributes);
        
        this.scaleMatrix = M3.scale(unit, unit);    
        if(params.center == undefined)
            this.initTransform = this.scaleMatrix;
        else{
            let translateCenter = M3.translate(params.center.x, params.center.y);
            this.initTransform = M3.multiply(this.scaleMatrix, translateCenter);
        }
        this.numVertices = this.attributes.position.data.length/2;
        this.uniforms = {   unit: unit, 
                            color: color || [MU.randomInt(255), MU.randomInt(255), MU.randomInt(255), 255],
        }; 

        
    }
    draw(gl, modelMatrix, projectionMatrix, sPoint){
        gl.useProgram(this.material.program);
        gl.bindVertexArray(this.material.vao);
        if(sPoint){
            let translateCenter = M3.translate(sPoint.x, sPoint.y);
            this.initTransform = M3.multiply(this.scaleMatrix, translateCenter);
        }
        this.uniforms.u_matrix = M3.multiply(projectionMatrix,  M3.multiply(this.initTransform, modelMatrix));
        //Si le vamos a aplicar una textura
        if(this.hasTexture){    
            this.uniforms.u_texture = { id: 0, texture : this.material.texture.texture};
        }
        
        this.material.setUniforms(this.uniforms);
        gl.drawArrays(gl.TRIANGLES, 0, this.numVertices);
    }
    /**
     * 
     * @param {*} r 
     * @param {*} numPoints 
     */
    circle(r, numPoints){
        const div = 1/numPoints;
        let points = [];
        var prev = new V2(r * MU.cos(0),r * MU.sin(0));
        for(var i = div; i <= 2 + div; i += div){
            points.push(0); //Origin
            points.push(0);
            points.push(prev.x);
            points.push(prev.y);

            var currPoint = new V2(r * MU.cos(i*Math.PI), r * MU.sin(i*Math.PI));
            points.push(currPoint.x);
            points.push(currPoint.y);

            prev = currPoint;
        }

        return points;
    }
    /**
     *  
     * 
     * @param {*} r 
     * @param {*} numPoints 
     */
    circumference(r, thickness, numPoints){
        const div = 1/numPoints;
        
        let points = [];
        const innerR = r - thickness;

        var prev = new V2(innerR * MU.cos(0),innerR * MU.sin(0));
        var prevB = new V2(r * MU.cos(0),r * MU.sin(0));

        for(var i = div; i <= 2 + div; i += div){
            
            var curr = new V2(innerR * MU.cos(i*Math.PI), innerR * MU.sin(i*Math.PI));
            var currB = new V2(r * MU.cos(i*Math.PI), r * MU.sin(i*Math.PI)); 
           
            points.push(prev.x);
            points.push(prev.y);
            points.push(prevB.x);
            points.push(prevB.y);
            points.push(currB.x);
            points.push(currB.y);
            
            points.push(currB.x);
            points.push(currB.y);
            points.push(curr.x);
            points.push(curr.y);
            points.push(prev.x);
            points.push(prev.y);
            
            prev = curr;
            prevB = currB;
        }

        return points;
    }
    /**
     * Devuelve los puntos para hacer un árco.
     * El ángulo inicial debe ser menor que el ángulo final.
     * @param {*} sAngle 
     * @param {*} eAngle 
     * @param {*} r 
     * @param {*} thickness 
     * @param {*} numPoints 
     */
    arc(sAngle, eAngle, r, thickness, numPoints){
        const div = Math.abs(eAngle-sAngle)/numPoints;
        let points = [];
        const innerR = r - thickness;
        var prev = new V2(innerR * MU.cos(sAngle),innerR * MU.sin(sAngle));
        var prevB = new V2(r * MU.cos(sAngle),r * MU.sin(sAngle));

        for(var i = sAngle + div; i < eAngle + div; i += div){
            
            var curr =  new V2(innerR * MU.cos(i), innerR * MU.sin(i));
            var currB =  new V2(r * MU.cos(i), r * MU.sin(i)); 
            
            points.push(prev.x);
            points.push(prev.y);
            points.push(prevB.x);
            points.push(prevB.y);
            points.push(currB.x);
            points.push(currB.y);
            
            points.push(currB.x);
            points.push(currB.y);
            points.push(curr.x);
            points.push(curr.y);
            points.push(prev.x);
            points.push(prev.y);
            
            prev = curr;
            prevB = currB;
        }

        return points;
    }
    sliceCircle(sAngle, eAngle, r, numPoints){
        const div = 1/numPoints;
        let points = [];
        var prev = new V2(r * MU.cos(sAngle),r * MU.sin(sAngle));

        for(var i = sAngle + div; i < eAngle + div; i += div){
            
            points.push(0); //Origin
            points.push(0);
            points.push(prev.x);
            points.push(prev.y);

            var currPoint = new V2(r * MU.cos(i), r * MU.sin(i));
            points.push(currPoint.x);
            points.push(currPoint.y);

            prev = currPoint;
        }

        return points;
    }

    /**
     * EL punto inicial es la esquina inferior izquierda
     * del rectángulo.
     * @param {*} sPoint 
     * @param {*} width 
     * @param {*} height 
     */
    rectangle(sPoint, width, height){   
        return [ sPoint.x, sPoint.y,
                 sPoint.x + width, sPoint.y,
                 sPoint.x + width, sPoint.y + height, 
                 sPoint.x + width, sPoint.y + height, 
                 sPoint.x, sPoint.y + height,
                 sPoint.x, sPoint.y];
    }

    rectangleTexture(){
        return [ 0, 1,
                 1, 1,
                 1, 0,
                 1, 0,
                 0, 0,
                 0, 1];
    }
}
