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

/**
 * Programa de sombreado DisplacementeMapping. 
 * @author Melissa Méndez Servín
 */
export default class DisplacementMapping{
    constructor(gl, WebGL, figure, srcTextures, draw_callback, initialUniforms){
        var vsh = `#version 300 es
                    precision highp float;
                    precision mediump int;

                    uniform mat4 u_PVM_matrix;
                    uniform mat4 u_VM_matrix;
                    uniform mat4 u_VMN_matrix;
                    
                    uniform sampler2D displacement_map;
                    uniform float max_height;
                    uniform float texture_factor;
                    in vec4 a_position;
                    in vec3 a_normal;
                
                    out vec3 v_normal; 
                    out vec3 v_position; 
                    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 = normalize(a_position.xyz);
                        
                        vec2 v_texcoord;
                    
                        float u = atan(v_local_position.z, v_local_position.x)/(2.0*3.14159) * texture_factor;
                        float v = asin(v_local_position.y)/3.14159 * texture_factor;
                        v_texcoord = vec2(u + 0.5, v + 0.5);
                      
                        vec4 direction = vec4(texture(displacement_map, v_texcoord).r * a_normal, 0) * max_height;
                        gl_Position = u_PVM_matrix * (a_position + direction);
                    }`;
        var fsh = `#version 300 es
                    precision highp float;
                    
                    struct Material{    
                        sampler2D diffuse_map;
                        float shininess;
                    };
                    
                    struct Light{
                        vec4 position;
                        vec3 la;
                        vec3 ld;
                        vec3 ls;
                    };

                    uniform Light u_light;
                    uniform Material u_material; 
                    uniform float texture_factor;

                    in vec3 v_normal;
                    in vec3 v_position;
                    in vec3 v_local_position;

                    out vec4 glColor;
                
                    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);
                        
                        vec2 v_texcoord;
                        float u = atan(v_local_position.z, v_local_position.x)/(2.0*3.14159) * texture_factor;
                        float v = asin(v_local_position.y)/3.14159 * texture_factor;
                        v_texcoord = vec2(u + 0.5, v + 0.5);
                        
                        vec3 diffuse_color = texture(u_material.diffuse_map, v_texcoord).rgb;
                        
                        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, u_material.shininess);
                        }
                        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);
                        glColor = vec4(contribution, 1.0);
                    }`; 

        if (WebGL.programs["DM"])
            this.program = WebGL.programs["DM"];
        else
            this.program = WebGL.createProgram(gl, vsh, fsh, "DM");
            
        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);
        
        let attributes = {  position: { numComponents: 3, 
                                                 data: this.vertices},
                              normal: { numComponents: 3, 
                                                 data: this.normals}
                         };
        
        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.texture = WebGL.loadImages( srcTextures, createTextures);
        this.uniforms = Object.assign({texture_factor : 1.5, max_height: 0.05}, initialUniforms);

        function createTextures(images){
            WebGL.createTextures(gl, images, srcTextures, draw_callback);
        }
    }
}
/**
 if(uv_mode == 0){
        v_texcoord = v_local_position.xz * 0.1+ vec2(0.5);
    }else if(uv_mode == 1){
        float f = 0.87;
        if((abs(v_local_position.x) > abs(v_local_position.y)) && (abs(v_local_position.x) > abs(v_local_position.z))){
            v_texcoord = v_local_position.zy * f + vec2(0.5);
        }else if((abs(v_local_position.y) > abs(v_local_position.x)) && (abs(v_local_position.y) > abs(v_local_position.z))) {
            v_texcoord = v_local_position.xz * f + vec2(0.5);
        }else {
            v_texcoord = v_local_position.xy * f + vec2(0.5); //escalar tamaño de textura
        }
    }else{
        float u = atan(v_local_position.z, v_local_position.x)/(2.0*3.14159);
        float v = asin(v_local_position.y)/3.14159;
        v_texcoord = vec2(u + 0.5, v + 0.5);
    }
 */