import { DoubleSide, NormalBlending, ShaderMaterial, Vector2 } from "three";

const vertexShader = `
    varying vec2 vUv;
    varying vec4 vColor;

    attribute float thickness;
    attribute vec3 previous, next;
    attribute float off;
    attribute vec3 color;
    attribute float uv_offset;
    attribute float age;
    attribute float alpha;
    
    uniform vec2 resolution;
    uniform float maxAge;
    uniform float time;
    
    float pi = 3.141592653589793;

    vec4 transform(vec3 coord){
        return projectionMatrix * modelViewMatrix * vec4(coord, 1.0);
    }

    vec2 project(vec4 device){
        vec3 device_normal = device.xyz / device.w;
        vec2 clip_pos = (device_normal*0.5+0.5).xy;
        return clip_pos * resolution;
    }

    vec4 unproject(vec2 screen, float z, float w){
        vec2 clip_pos = screen / resolution;
        vec2 device_normal = clip_pos*2.0-1.0;
        return vec4( device_normal * w, z, w);
    }
    
    float estimateScale(vec3 position, vec2 sPosition){
    
        vec4 view_pos = modelViewMatrix * vec4( position, 1.0 );
        
        float halfWidth = thickness*0.5;
        
        vec4 scale_pos = view_pos - vec4( normalize(view_pos.xy) * halfWidth, 0.0, 0.0);
        
        vec2 screen_scale_pos = project(projectionMatrix * scale_pos);
        
        return distance( sPosition, screen_scale_pos );
        
    }

    void main(){
        vec2 sLast = project(transform(previous.xyz));
        vec2 sNext = project(transform(next.xyz));

        vec4 dCurrent = transform(position.xyz);
        vec2 sCurrent = project(dCurrent);

        vec2 normal1 = normalize(sLast - sCurrent);
        vec2 normal2 = normalize(sCurrent - sNext);
        
        // vector between two segments
        vec2 normal = (normal1 + normal2) * 0.5;
        
        float offset_signed = off*2.0 - 1.0;
        
        vUv = vec2(uv_offset, off);
        
        float relativeAge = clamp(age / maxAge, 0.0, 1.0);
        
        vColor = vec4( color / 255.0, alpha );       
        
        vec2 dir =  vec2(normal.y, -normal.x) * offset_signed;
        
        float angular_compensation = max( normal.x * normal.x + normal.y * normal.y, 0.25 );
        
        float visual_size = resolution.y * projectionMatrix[1][1] * thickness*0.5 / dCurrent.w;
        
        float scale = visual_size / angular_compensation;
        
        vec2 pos = sCurrent + dir * scale;

        gl_Position = unproject(pos, dCurrent.z, dCurrent.w);
    }
`;

const fragmentShader = `
    varying vec2 vUv;
    varying vec4 vColor;

    uniform sampler2D uDiffuse;
    
    void main(){
        vec4 diffuseColor = vColor;
        
        #ifdef USE_TEXTURE
            vec4 texel = texture2D(uDiffuse, vUv);
            diffuseColor *= texel;
        #endif
        
        gl_FragColor = diffuseColor;
    }
`;

export class RibbonMaterialX extends ShaderMaterial {
    constructor(params) {
        super({
            fragmentShader,
            vertexShader,
            uniforms: {
                resolution: {
                    value: new Vector2(600, 600)
                },
                uDiffuse: {
                    type: 't',
                    value: null
                }
            },
            defines: {
                USE_TEXTURE: false
            },
            blending: NormalBlending,
            transparent: true,
            depthWrite: false,
            depthTest: true,
            side: DoubleSide
        });

        this.setValues(params);
    }

    /**
     *
     * @param {Vector2} v2
     */
    set resolution(v2) {
        this.uniforms.resolution.value.set(v2.x, v2.y);
    }

    /**
     *
     * @return {Vector2}
     */
    get resolution() {
        return this.uniforms.resolution.value;
    }
}
