import {getCylindricTexCoordinates, getDisconnectedVertices, getNormalsFromIndices, getNormalsFromVertices, getTexCoordinates} from "../geometry/Figure.js";

/**
 * Programa de sombreado de texturas procedurales geométricas básicas.
 * MODO 0: coordenadas de textura.
 * MODO 1: coordenadas de superficie.
 * @author Melissa Méndez Servín.
 */
export default class TexcoordGenerator{
   
    constructor(gl, WebGL, figure, initialUniforms){
        
        var vsh = `#version 300 es
                    uniform mat4 u_PVM_matrix;
                    uniform mat4 u_VM_matrix;
                    uniform mat4 u_VMN_matrix;
                    
                    in vec4 a_position;
                    in vec3 a_normal;
                    in vec2 a_texcoord;
                
                    out vec3 v_normal; 
                    out vec3 v_position; 
                    out vec2 v_texcoord; 
                    out vec3 v_local_position;

                    void main(){
                    
                        v_position = (u_VM_matrix * a_position).xyz;
                        v_normal = (u_VMN_matrix * vec4(a_normal, 0)).xyz;
                        v_local_position =  a_position.xyz;
                        v_texcoord = a_texcoord;

                        gl_Position = u_PVM_matrix * a_position;
                    }`;
        var fsh = `#version 300 es
                    precision highp float;
                    
                    struct Light{
                        vec4 position;
                        vec3 la;
                        vec3 ld;
                        vec3 ls;
                    };

                    uniform Light u_light;
                    uniform int coord_type;
                    uniform int texture_type;
                    uniform float scale_factor;

                    in vec3 v_normal;
                    in vec3 v_position;
                    in vec2 v_texcoord;
                    in vec3 v_local_position;

                    out vec4 glColor;

                    #define PI 3.14159
                    float TWO_PI = 2. * 3.14159;

                    vec3 checkboard2D(vec2 texcoords){
                        float u = sin(texcoords.x * scale_factor * PI);
                        float v = sin(texcoords.y * scale_factor * PI);
                        if((u > 0.0)^^(v > 0.0))
                            return vec3(0.97);
                        else
                            return vec3(0.8,0.52,0.247);
                    }
                    vec3 checkboard3D(vec3 position){
                        float x = sin(position.x * scale_factor * PI);
                        float y = sin(position.y * scale_factor * PI);
                        float z = sin(position.z * scale_factor * PI);
                        if((x > 0.0)^^(y > 0.0)^^(z > 0.0))
                            return vec3(0.9);
                        else
                            return vec3(0.5,0.5,0.0);//Thistle
                    }
                    vec3 stripes2D(vec2 texcoords){
                        float c = cos(texcoords.y * 2.0 * PI * scale_factor);
                        if(c >= 0.0) 
                            return vec3(1.0,0.34,0.2);//Orange
                        return vec3(1.0,1.0,0.0);
                    }
                    vec3 stripes3D(vec3 position){
                        float c = cos(position.y * scale_factor * TWO_PI);
                        if(c > 0.0) 
                            return vec3(0.5,0.0,0.5);//Purple
                        return vec3(1.0,0.75,0.796);//Pink
                    }
                    vec3 lattice2D(vec2 texcoords){
                        float u = texcoords.x * scale_factor;
                        float v = texcoords.y * scale_factor;
                        if(mod(u,2.0) >= 0.5 && mod(v,2.0) >= 0.5) 
                            discard;
                        return vec3(0.6,0.8,0.196);//YellowGreen
                    }
                    vec3 lattice3D(vec3 position){
                        float x = position.x * scale_factor;
                        float y = position.y * scale_factor;
                        float z = position.z * scale_factor;
                        if(abs(position.x) > abs(position.y) && abs(position.x) > abs(position.z)){
                            if(mod(z,2.0) >= 0.5 && mod(y,2.0) >= 0.5) 
                                discard;
                        }else if(abs(position.y) > abs(position.x) && abs(position.y) > abs(position.z)){
                            if(mod(x,2.0) >= 0.5 && mod(z,2.0) >= 0.5) 
                                discard;
                        }else{
                            if(mod(x,2.0) >= 0.5 && mod(y,2.0) >= 0.5) 
                                discard;
                        }
                        return vec3(0.5);
                    }
                    vec3 circles2D(vec2 uv){
                        float d = length(uv - 0.5);
                        return vec3(0.54,0.26,0.886) * (fract(d*scale_factor));//Violet
                    }
                    vec3 circles3D(vec3 position){
                        float d = length(abs(position) - 0.5);
                        return vec3(0.,0.,1.) * (fract(d*scale_factor));//Green
                    }
                    vec2 figures(vec2 uv){
                        float angle = atan(uv.x, uv.y);
                        vec2 vertex = vec2(cos(TWO_PI/scale_factor), sin(TWO_PI/scale_factor));
                        return vertex;
                    }                                                                                                                                                                                                                                                                                       
                    vec3 flower2D(vec2 uv){
                        uv = vec2(0.5)-fract(uv);
                        float angle = atan(uv.x, uv.y);
                        float r = length(uv* 1./scale_factor) * 3.;
                        float f = step(abs(cos(angle*3.)),r);
                        if(f == 0.0)
                            return vec3(.8,.8,.98);
                        return vec3(f);
                    }
                    vec3 flower3D(vec3 position){
                        vec3 pos = position;
                        position = vec3(.5) - fract(position);
                        float angle;
                        if(abs(pos.x) > abs(pos.y) && abs(pos.x) > abs(pos.z)){
                            angle = atan(position.z, position.y);
                        }else if(abs(pos.y) > abs(pos.x) && abs(pos.y) > abs(pos.z)){
                            angle = atan(position.x, position.z);
                        }else{
                            angle = atan(position.x, position.y);
                        }
                        float r = length(abs(position* 1./scale_factor)) * 3.;
                        float f = step(abs(cos(angle*2.5))*.5+.3, r);
                        if(f == 0.0)
                            return vec3(.84,.74,.84);
                        return vec3(f);
                    }
                    void main(){
                        vec3 L = normalize(u_light.position.xyz - v_position);
                        vec3 N = normalize(v_normal);
            
                        float cos_angle = max(dot(N, L), 0.0);
                        
                        vec3 diffuse_color;
                        if(texture_type == 0){ //Ajedréz
                            diffuse_color = (coord_type == 0) ? checkboard2D(v_texcoord) : checkboard3D(v_local_position);
                        }else if(texture_type == 1){ //Líneas
                            diffuse_color = (coord_type == 0) ? stripes2D(v_texcoord) : stripes3D(v_local_position);
                        }else if(texture_type == 2){ //Enrejado
                            diffuse_color = (coord_type == 0) ? lattice2D(v_texcoord) : lattice3D(v_local_position);
                        }else if(texture_type == 3){ //Círculos
                            diffuse_color = (coord_type == 0) ? circles2D(v_texcoord) : circles3D(v_local_position);
                        }else if(texture_type == 4){ //Hoyos 
                            diffuse_color = (coord_type == 0) ? flower2D(v_texcoord) : flower3D(v_local_position);
                        }
                        float specular = 0.0;

                        if(cos_angle > 0.0){
                            vec3 R = reflect(-L,N);
                            vec3 V = normalize(-v_position);
                            float spec_angle = max(dot(R,V),0.0);
                            specular = pow(spec_angle,  0.088*128.0);
                        }
                        float d = length(u_light.position.xyz - v_position);
                        float attenuation = 1.0/(0.1*d*d + 0.33);

                        vec3 contribution = vec3(diffuse_color * u_light.la + 
                                                attenuation * (diffuse_color * u_light.ld * cos_angle +
                                                                u_light.ls * specular));
                        glColor = vec4(contribution, 1.0);
                    }`; 

        if (WebGL.programs["TG"])
            this.program = WebGL.programs["TG"];
        else
            this.program = WebGL.createProgram(gl, vsh, fsh, "TG");
            
        this.vertices = (figure.byIndices || figure.getNormals) ?  figure.getVertices() : getDisconnectedVertices(figure.getVertices(), figure.getFaces());
        if(figure.getNormals)
            this.normals = figure.getNormals();
        else
            this.normals = (figure.byIndices) ? getNormalsFromIndices(figure.getFaces(), this.vertices) : getNormalsFromVertices(this.vertices);
        this.texcoords = (figure.getTexCoordinates) ? figure.getTexCoordinates() : getCylindricTexCoordinates(this.vertices);
        let attributes = {  position: { numComponents: 3, 
                                                 data: this.vertices},
                              normal: { numComponents: 3, 
                                                 data: this.normals},
                            texcoord: { numComponents: 2, 
                                            data: this.texcoords,
                                            normalize: true}
                         };
        if(figure.byIndices){
            let vaoAndIndices = WebGL.setVAOAndAttributes(gl, this.program, attributes, figure.getFaces());
            this.vao = vaoAndIndices.vao;
            this.indexBuffer = vaoAndIndices.indexBuffer;
            this.numIndices = figure.numIndices;
        }else{
            this.vao = WebGL.setVAOAndAttributes(gl, this.program, attributes);  
            this.numElements = this.vertices.length/3;
        }
        this.setUniforms = WebGL.setUniforms(gl, this.program);

        this.uniforms = Object.assign({ coord_type: 0, texture_type: 0, scale_factor : 4.0}, initialUniforms);
    }
}