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

/**
 * Programa de sombreado de texturas procedurales aleatorias.
 * @author Melissa Méndez Servín.
 */
export default class RandomTexture{
  
    constructor(gl, WebGL, figure, initialUniforms){
        
        var vsh = `#version 300 es
                    uniform mat4 u_PVM_matrix;
                    in vec4 a_position;
                    in vec2 a_texcoord;

                    out vec2 v_texcoord;
                    void main(){
                    
                        v_texcoord = a_texcoord;

                        gl_Position = u_PVM_matrix * a_position;
                    }`;
        var fsh = `#version 300 es
                    precision highp float;
                    
                    uniform int texture_id;
                    uniform float scale_factor;
                    uniform float u_time;

                    in vec2 v_texcoord;

                    out vec4 glColor;
            
                    //
                    // Description : Array and textureless GLSL 2D simplex noise function.
                    //      Author : Ian McEwan, Ashima Arts.
                    //  Maintainer : stegu
                    //     Lastmod : 20110822 (ijm)
                    //     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
                    //               Distributed under the MIT License. See LICENSE file.
                    //               https://github.com/ashima/webgl-noise
                    //               https://github.com/stegu/webgl-noise
                    // 

                    vec3 mod289(vec3 x) {
                        return x - floor(x * (1.0 / 289.0)) * 289.0;
                    }

                    vec2 mod289(vec2 x) {
                        return x - floor(x * (1.0 / 289.0)) * 289.0;
                    }

                    vec3 permute(vec3 x) {
                        return mod289(((x*34.0)+10.0)*x);
                    }

                    float snoise(vec2 v)
                    {
                        const vec4 C = vec4(0.211324865405187,  // (3.0-sqrt(3.0))/6.0
                                            0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)
                                            -0.577350269189626,  // -1.0 + 2.0 * C.x
                                            0.024390243902439); // 1.0 / 41.0
                        // First corner
                        vec2 i  = floor(v + dot(v, C.yy) );
                        vec2 x0 = v -   i + dot(i, C.xx);

                        // Other corners
                        vec2 i1;
                        //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
                        //i1.y = 1.0 - i1.x;
                        i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
                        // x0 = x0 - 0.0 + 0.0 * C.xx ;
                        // x1 = x0 - i1 + 1.0 * C.xx ;
                        // x2 = x0 - 1.0 + 2.0 * C.xx ;
                        vec4 x12 = x0.xyxy + C.xxzz;
                        x12.xy -= i1;

                        // Permutations
                        i = mod289(i); // Avoid truncation effects in permutation
                        vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
                                + i.x + vec3(0.0, i1.x, 1.0 ));

                        vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
                        m = m*m ;
                        m = m*m ;

                        // Gradients: 41 points uniformly over a line, mapped onto a diamond.
                        // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)

                        vec3 x = 2.0 * fract(p * C.www) - 1.0;
                        vec3 h = abs(x) - 0.5;
                        vec3 ox = floor(x + 0.5);
                        vec3 a0 = x - ox;

                        // Normalise gradients implicitly by scaling m
                        // Approximation of: m *= inversesqrt( a0*a0 + h*h );
                        m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );

                        // Compute final noise value at P
                        vec3 g;
                        g.x  = a0.x  * x0.x  + h.x  * x0.y;
                        g.yz = a0.yz * x12.xz + h.yz * x12.yw;
                        return 130.0 * dot(m, g);
                    }
                    // Cellular noise ("Worley noise") in 2D in GLSL.
                    // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved.
                    // This code is released under the conditions of the MIT license.
                    // See LICENSE file for details.
                    // https://github.com/stegu/webgl-noise

                    #define K 0.142857142857 // 1/7
                    #define Ko 0.428571428571 // 3/7
                    #define jitter 1.0 // Less gives more regular pattern
                   
                    // Modulo 7 without a division
                    vec3 mod7(vec3 x) {
                    return x - floor(x * (1.0 / 7.0)) * 7.0;
                    }

                    // Cellular noise, returning F1 and F2 in a vec2.
                    // Standard 3x3 search window for good F1 and F2 values
                    vec2 cellular(vec2 P) {
                        vec2 Pi = mod289(floor(P));
                        vec2 Pf = fract(P);
                        vec3 oi = vec3(-1.0, 0.0, 1.0);
                        vec3 of = vec3(-0.5, 0.5, 1.5);
                        vec3 px = permute(Pi.x + oi);
                        vec3 p = permute(px.x + Pi.y + oi); // p11, p12, p13
                        vec3 ox = fract(p*K) - Ko;
                        vec3 oy = mod7(floor(p*K))*K - Ko;
                        vec3 dx = Pf.x + 0.5 + jitter*ox;
                        vec3 dy = Pf.y - of + jitter*oy;
                        vec3 d1 = dx * dx + dy * dy; // d11, d12 and d13, squared
                        p = permute(px.y + Pi.y + oi); // p21, p22, p23
                        ox = fract(p*K) - Ko;
                        oy = mod7(floor(p*K))*K - Ko;
                        dx = Pf.x - 0.5 + jitter*ox;
                        dy = Pf.y - of + jitter*oy;
                        vec3 d2 = dx * dx + dy * dy; // d21, d22 and d23, squared
                        p = permute(px.z + Pi.y + oi); // p31, p32, p33
                        ox = fract(p*K) - Ko;
                        oy = mod7(floor(p*K))*K - Ko;
                        dx = Pf.x - 1.5 + jitter*ox;
                        dy = Pf.y - of + jitter*oy;
                        vec3 d3 = dx * dx + dy * dy; // d31, d32 and d33, squared
                        // Sort out the two smallest distances (F1, F2)
                        vec3 d1a = min(d1, d2);
                        d2 = max(d1, d2); // Swap to keep candidates for F2
                        d2 = min(d2, d3); // neither F1 nor F2 are now in d3
                        d1 = min(d1a, d2); // F1 is now in d1
                        d2 = max(d1a, d2); // Swap to keep candidates for F2
                        d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx; // Swap if smaller
                        d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx; // F1 is in d1.x
                        d1.yz = min(d1.yz, d2.yz); // F2 is now not in d2.yz
                        d1.y = min(d1.y, d1.z); // nor in  d1.z
                        d1.y = min(d1.y, d2.x); // F2 is in d1.y, we're done.
                        return sqrt(d1.xy);
                    }
                    // Cellular noise ("Worley noise") in 2D in GLSL.
                    // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved.
                    // This code is released under the conditions of the MIT license.
                    // See LICENSE file for details.
                    // https://github.com/stegu/webgl-noise
                    
                    vec4 mod289(vec4 x) {
                        return x - floor(x * (1.0 / 289.0)) * 289.0;
                    }
                    // Permutation polynomial: (34x^2 + 6x) mod 289
                    vec4 permute(vec4 x) {
                        return mod289((34.0 * x + 10.0) * x);
                    }
                    // Cellular noise, returning F1 and F2 in a vec2.
                    // Speeded up by using 2x2 search window instead of 3x3,
                    // at the expense of some strong pattern artifacts.
                    // F2 is often wrong and has sharp discontinuities.
                    // If you need a smooth F2, use the slower 3x3 version.
                    // F1 is sometimes wrong, too, but OK for most purposes.
                    vec2 cellular2x2(vec2 P) {
                        #define K 0.142857142857 // 1/7
                        #define K2 0.0714285714285 // K/2
                        #define jitter2 0.8 // jitter 1.0 makes F1 wrong more often
                        vec2 Pi = mod(floor(P), 289.0);
                         vec2 Pf = fract(P);
                        vec4 Pfx = Pf.x + vec4(-0.5, -1.5, -0.5, -1.5);
                        vec4 Pfy = Pf.y + vec4(-0.5, -0.5, -1.5, -1.5);
                        vec4 p = permute(Pi.x + vec4(0.0, 1.0, 0.0, 1.0));
                        p = permute(p + Pi.y + vec4(0.0, 0.0, 1.0, 1.0));
                        vec4 ox = mod(p, 7.0)*K+K2;
                        vec4 oy = mod(floor(p*K),7.0)*K+K2;
                        vec4 dx = Pfx + jitter2*ox;
                        vec4 dy = Pfy + jitter2*oy;
                        vec4 d = dx * dx + dy * dy; // d11, d12, d21 and d22, squared
                        // Sort out the two smallest distances
                    #if 0
                        // Cheat and pick only F1
                        d.xy = min(d.xy, d.zw);
                        d.x = min(d.x, d.y);
                        return d.xx; // F1 duplicated, F2 not computed
                    #else
                        // Do it right and find both F1 and F2
                        d.xy = (d.x < d.y) ? d.xy : d.yx; // Swap if smaller
                        d.xz = (d.x < d.z) ? d.xz : d.zx;
                        d.xw = (d.x < d.w) ? d.xw : d.wx;
                        d.y = min(d.y, d.z);
                        d.y = min(d.y, d.w);
                        return sqrt(d.xy);
                    #endif
                    }                    

                    // Author @patriciogv - 2015 (modificado)
                    // http://patriciogonzalezvivo.com
                    float random (vec2 texcoord) {
                            return fract(sin(dot(texcoord.xy, vec2(13,78.233) * u_time)) * 401.0);
                    }
                    
                    void main(){
                        vec3 color;
                        if(texture_id == 0)
                            color = vec3(random(v_texcoord));     
                        else if(texture_id == 1){
                            vec2 random_coord = v_texcoord + fract(abs(sin(u_time * .8)));
                            random_coord.x += u_time * 0.05;
                            float value = 0.75 + snoise(scale_factor * random_coord) * 0.7;
                            color = vec3(value);
                        }else if(texture_id == 2){ 
                            //Fragmento de https://www.shadertoy.com/view/lsf3WH
                            // The MIT License
                            // Copyright © 2013 Inigo Quilez
                            vec2 uv = scale_factor * .05 * v_texcoord + u_time *.6;	
                            float f = 0.0;
                            uv *= 8.0;
                            mat2 m = mat2( 1.8,  1.34, -1.34,  1.8);
                            f  = 0.5000*snoise(uv); uv = m*uv;
                            f += 0.2500*snoise(uv); uv = m*uv;
                            f += 0.1250*snoise(uv); uv = m*uv;
                            f += 0.0625*snoise(uv); uv = m*uv;
                            f = 0.5 + 0.5*f;
                            
                            color = vec3(f); 
                        }else if(texture_id == 3){
                            vec2 random_coord = v_texcoord + abs(cos(u_time * .8));
                            color = vec3(cellular(scale_factor * random_coord), 1.0);
                        }else{
                            vec2 random_coord = v_texcoord + vec2(u_time * .8,0);
                            vec2 f = cellular2x2(scale_factor * random_coord);
                            color = vec3(1.0,0.,0.) * vec3(1.0-1.5*f.x);
                        }

                        glColor = vec4(color, 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() : getTexCoordinates(this.vertices);
        let attributes = {  position: { numComponents: 3, 
                                                 data: this.vertices},
                            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({ texture_id: 0, scale_factor : 20.0, u_time: 0}, initialUniforms);
    }
}