import M4 from "../modules/M4.js";
/**
 * Clase auxiliar para renderizar una geometría definida dentro de un programa dado.
 * @author Melissa Méndez Servín.
 */
let gl;
export default class GeometryPainter{
    /**
     * Constructor.
     * @param {WebGLRenderingContext} GL el WebGLRenderingContext.
     * @param {*} material el programa a renderizar
     * @param {*} initialTransform la transformación incial a aplicar a la geometría del programa.
     */
    constructor(GL, material, initialTransform){
        gl = GL;
        this.material = material;
        this.initialTransform = initialTransform || M4.identity();
        this.numIndices = material.numIndices;
        this.numElements = material.numElements;
    }
    /**
     * Renderiza la geometría de un objeto.
     * @param {*} gl 
     * @param {*} modelViewMatrix 
     * @param {*} projectionMatrix 
     * @param {*} uniforms 
     */
    draw(gl, modelViewMatrix, projectionMatrix, uniforms={}){
        
        gl.useProgram(this.material.program);
        gl.bindVertexArray(this.material.vao);
        
        let MV_matrix = M4.multiply( modelViewMatrix, this.initialTransform);
        let matrix = M4.multiply(projectionMatrix, MV_matrix); 
        this.material.uniforms.u_PVM_matrix = matrix;
        this.material.uniforms.u_VM_matrix = MV_matrix;
        if(uniforms.u_VMN_matrix == undefined){
            this.material.uniforms.u_VMN_matrix = M4.transpose(M4.inverse(MV_matrix));
        }
        
        this.material.uniforms = Object.assign({}, this.material.uniforms, uniforms);

        this.material.setUniforms(this.material.uniforms);
        if (this.numIndices) {
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.material.indexBuffer);
            gl.drawElements( gl.TRIANGLES, this.numIndices,  gl.UNSIGNED_SHORT, 0);
        }
        else {
            gl.drawArrays(gl.TRIANGLES, 0, this.numElements);
        }      
    
        gl.bindBuffer(gl.ARRAY_BUFFER, null);      
    }
    /**
     * Renderiza un objeto sin calcular la matriz de normales.
     * @param {*} gl 
     * @param {*} modelViewMatrix 
     * @param {*} projectionMatrix 
     * @param {*} uniforms 
     */
    drawSimple(gl, modelViewMatrix, projectionMatrix, uniforms={}){
        gl.useProgram(this.material.program);
        gl.bindVertexArray(this.material.vao);
        
        let MV_matrix = M4.multiply( modelViewMatrix, this.initialTransform);
        let matrix = M4.multiply(projectionMatrix, MV_matrix); 
        this.material.uniforms.u_PVM_matrix = matrix;
        
        this.material.uniforms = Object.assign({}, this.material.uniforms, uniforms);
       
        this.material.setUniforms(this.material.uniforms);
        if (this.numIndices) {
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.material.indexBuffer);
            gl.drawElements( gl.TRIANGLES, this.numIndices,  gl.UNSIGNED_SHORT, 0);
        }
        else {
            gl.drawArrays(gl.TRIANGLES, 0, this.numElements);
        }      
    
        gl.bindBuffer(gl.ARRAY_BUFFER, null);      
    }
    /**
     * Dibuja únicamente las primitivas (triángulos)
     * @param {*} gl 
     * @param {*} modelViewMatrix 
     * @param {*} projectionMatrix 
     */
    drawWireframe(gl, modelViewMatrix, projectionMatrix){
        gl.useProgram(this.material.program);
        gl.bindVertexArray(this.material.vao);
        
        let MV_matrix = M4.multiply( modelViewMatrix, this.initialTransform);
        this.material.uniforms.u_PVM_matrix =  M4.multiply(projectionMatrix, MV_matrix); 
        this.material.setUniforms(this.material.uniforms);
        
        //console.log(this.numElements);
        for (let start=0; start < this.numElements/3; start++) {
            gl.drawArrays(gl.LINE_LOOP, start*3, 3);
        }
        gl.bindBuffer(gl.ARRAY_BUFFER, null);      
    }

    /**
     * Dibuja una malla de cuadrados para un parche de Bézier.
     * @param {*} gl 
     * @param {*} modelViewMatrix 
     * @param {*} projectionMatrix 
     */
    drawWireframeQuad(gl, modelViewMatrix, projectionMatrix){
        gl.useProgram(this.material.program);
        gl.bindVertexArray(this.material.vao);
        
        let MV_matrix = M4.multiply( modelViewMatrix, this.initialTransform);
        this.material.uniforms.u_PVM_matrix =  M4.multiply(projectionMatrix, MV_matrix); 
        this.material.setUniforms(this.material.uniforms);

        for (let start=0; start < this.numElements/4; start++) {
            gl.drawArrays(gl.LINE_LOOP, start*4, 4);
        }
        gl.bindBuffer(gl.ARRAY_BUFFER, null);      
    }
}