import * as THREE from 'three';
import { Sky as SkyAsset, MviewAsset } from '../assets/marmosets/MviewFiles'


//默认形状提取自八猴
const vertexs = [0, 1, 0, 0.5, 0.5, 1, 0, 0, 0.99609375, 0.5, 0, 0, 1, 0.5, 0.99609375, -1, 0, 0, 0.00390625, 0.5, 0, 0, -1, 0.5, 0.00390625, 0, -1, 0, 0.99609375, 0.00390625, 0, -1, 0, 0.99609375, 0.99609375, 0, -1, 0, 0.00390625, 0.99609375, 0, -1, 0, 0.00390625, 0.00390625, 0.0054687499068677425, 0.9945312738418579, -0.0054687499068677425, 0.501953125, 0.498046875, 0.0054687499068677425, 0.9945312738418579, 0.0054687499068677425, 0.501953125, 0.501953125, -0.0054687499068677425, 0.9945312738418579, 0.0054687499068677425, 0.498046875, 0.501953125, -0.0054687499068677425, 0.9945312738418579, -0.0054687499068677425, 0.498046875, 0.498046875, -0.0054687499068677425, 0, -0.9945312738418579, 0.498046875, 0.005859375, 0.0054687499068677425, 0, -0.9945312738418579, 0.501953125, 0.005859375, 0.9945312738418579, 0, -0.0054687499068677425, 0.994140625, 0.498046875, 0.9945312738418579, 0, 0.0054687499068677425, 0.994140625, 0.501953125, 0.0054687499068677425, 0, 0.9945312738418579, 0.501953125, 0.994140625, -0.0054687499068677425, 0, 0.9945312738418579, 0.498046875, 0.994140625, -0.9945312738418579, 0, 0.0054687499068677425, 0.005859375, 0.501953125, -0.9945312738418579, 0, -0.0054687499068677425, 0.005859375, 0.498046875, 1, 0, 0, 0.9951171875, 0.5, 0, 0, 1, 0.5, 0.9951171875, -1, 0, 0, 0.0048828125, 0.5, 0, 0, -1, 0.5, 0.0048828125, 0, 1, 0, 0.4990234375, 0.5, 0, 1, 0, 0.5, 0.4990234375, 0, 1, 0, 0.5009765625, 0.5, 0, 1, 0, 0.5, 0.5009765625]
const indices = [2, 1, 6, 3, 2, 7, 8, 4, 3, 4, 5, 1, 9, 14, 15, 17, 10, 16, 18, 19, 11, 20, 13, 12, 28, 12, 13, 13, 24, 28, 28, 24, 9, 9, 24, 14, 25, 9, 15, 25, 15, 21, 10, 25, 21, 10, 21, 16, 22, 26, 10, 22, 10, 17, 18, 11, 26, 22, 18, 26, 19, 23, 27, 19, 27, 11, 23, 20, 27, 27, 20, 12]


export default class MarmosetSky extends THREE.Mesh {

    private diffuseCoefficients: Float32Array
    private brightness: number;

    specularTexture: THREE.Texture
    backgrondTexture: THREE.Texture

    material: THREE.ShaderMaterial

    constructor() {

        super();

        this.diffuseCoefficients = new Float32Array(36);
        this.brightness = 0;

        this.specularTexture = new THREE.Texture();
        this.backgrondTexture = new THREE.Texture();

        let vertexBuffer = new THREE.InterleavedBuffer(new Float32Array(vertexs), 5);

        let geometry = new THREE.BufferGeometry();
        geometry.setAttribute('position', new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0));
        geometry.setAttribute('uv', new THREE.InterleavedBufferAttribute(vertexBuffer, 2, 3))
        geometry.setIndex(new THREE.Uint16BufferAttribute(new Uint16Array(indices), 3))
        geometry.scale(1000, 1000, 1000)

        this.geometry = geometry //new THREE.PlaneBufferGeometry(10,10) //geometry; 
        this.material = new THREE.ShaderMaterial();
    
    }


    private buildMaterial(skyMode: number) {
        
        
        this.material = new THREE.ShaderMaterial({
            side: THREE.DoubleSide,
            defines: {

                SKYMODE: skyMode
                
            },
            uniforms: {

                uSkyCoefficients: { value: null },
                uAlpha: { value: null},
                tSkyTexture: {value: null}

            },
            transparent: false,
            vertexShader: `
           
                uniform mat4 uInverseSkyMatrix;
                uniform mat4 uViewProjection;

                attribute vec3 vPosition;
                attribute vec2 vTexCoord;


                #if SKYMODE == 3
                varying vec3 jJ;
                #else
                varying vec2 d;
                #endif

                vec4 h(mat4 i,vec3 p)
                {
                    return i[0]*p.x+(i[1]*p.y+(i[2]*p.z+i[3]));
                }

                vec4 u(mat4 i,vec3 v)
                {
                    return i[0]*v.x+i[1]*v.y+i[2]*v.z;
                }


                
                void main(void)
                {
                    mat4 s;
                    s[0] = vec4(1, 0, -7.179587147021493e-9, 0);
                    s[1] = vec4(0, 1, 0, 0);
                    s[2] = vec4(7.179587147021493e-9, 0, 1, 0);
                    s[3] = vec4(0, 0, 0, 1);

                    //vec3 p=h(modelViewMatrix,vPosition).xyz;
                    //gl_Position=u(s,p);
                    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                    
                    gl_Position.z-=(1.0/65535.0)*gl_Position.w;

                    #if SKYMODE == 3
                    //jJ=vPosition;
                    jJ = position;

                    jJ.xy+=1e-20*vTexCoord;
                    #else
                    //d=vTexCoord;
                    d=uv;
                    #endif
                }

            `,

            fragmentShader: this.fragmentShader(skyMode)
        })

    }


    private fragmentShader(skyMode: number) {

        let fs = ``;

        if(skyMode == 3) {

            fs = `

                precision mediump float;

                uniform vec4 uSkyCoefficients[9];
                uniform float uAlpha;

                varying vec3 jJ;

                void main(void)
                {
                    vec3 G=normalize(jJ);

                    vec3 r=uSkyCoefficients[0].xyz;
                    r+=uSkyCoefficients[1].xyz*G.y;
                    r+=uSkyCoefficients[2].xyz*G.z;
                    r+=uSkyCoefficients[3].xyz*G.x;

                    vec3 swz=G.yyz*G.xzx;
                    r+=uSkyCoefficients[4].xyz*swz.x;
                    r+=uSkyCoefficients[5].xyz*swz.y;
                    r+=uSkyCoefficients[7].xyz*swz.z;

                    vec3 sqr=G*G;
                    r+=uSkyCoefficients[6].xyz*(3.0*sqr.z-1.0);
                    r+=uSkyCoefficients[8].xyz*(sqr.x-sqr.y);
                    
                    gl_FragColor.xyz=r;
                    gl_FragColor.w=uAlpha;
                }

            `
        } else {

            fs = `

                precision highp float;

                uniform sampler2D tSkyTexture;
                uniform float uAlpha;
                
                varying vec2 d;
                
                void main(void)
                {
                    vec3 r=texture2D(tSkyTexture,d).xyz;
                    gl_FragColor.xyz= r * r * 60.0;
                
                   
                    gl_FragColor.w=uAlpha;
                }
            
            `
        }

        

        return fs;
    }


    build(skyParameter: SkyAsset, files:Map<string, MviewAsset>) {

        this.brightness = skyParameter.backgroundBrightness || 1;

        this.diffuseCoefficients = new Float32Array(skyParameter.diffuseCoefficients);
        
        let skyData = files.get("sky.dat");

        this.buildMaterial(skyParameter.backgroundMode)
       
        if(skyData) {

            let skyBuffer = new Uint8Array(skyData.data)

            if(!skyBuffer) { throw ("解压失败") };
            let textureBuffer = new Uint8Array(skyData.data.byteLength);
            for (var d = skyBuffer.length, e = d / 4, g = 0, h = 0; g < d; ++h)
                textureBuffer[g++] = skyBuffer[h + 2 * e],
                textureBuffer[g++] = skyBuffer[h + e],
                textureBuffer[g++] = skyBuffer[h],
                textureBuffer[g++] = skyBuffer[h + 3 * e];

            let texture = new THREE.DataTexture( textureBuffer, 256, 2048, THREE.RGBAFormat);
                texture.generateMipmaps = true;
                texture.premultiplyAlpha = true
                texture.magFilter = THREE.LinearMipMapLinearFilter
                texture.minFilter = THREE.LinearMipMapLinearFilter
                texture.flipY = true;

            this.specularTexture = texture;

            this.exportBackgroundTexture( texture );

           
        }

        this.material.uniforms['uSkyCoefficients'].value = this.diffuseCoefficients
    }


    exportBackgroundTexture( specularTexture: THREE.Texture ) {

        let rt = new THREE.WebGLRenderTarget(256,256, {  wrapT: THREE.ClampToEdgeWrapping, format: THREE.RGBAFormat })
        
        let renderer = new THREE.WebGLRenderer();
            renderer.domElement.style.zIndex = "1000"
            renderer.domElement.style.position = "fixed"
            renderer.autoClear = false;
            renderer.setSize(rt.width, rt.height)
            //document.body.append(renderer.domElement);


       
        let camera = new THREE.OrthographicCamera(-10,10,10,-10,0.1,100);
            camera.updateProjectionMatrix()
            camera.position.set(0, 0, 10)
        let scene = new THREE.Scene();
    
        let geometry = new THREE.BufferGeometry()
            geometry.setAttribute('p', new THREE.Float32BufferAttribute([-1, -1, 0.5, 1,  3, -1, 0.5, 1,  -1, 3, 0.5, 1], 4))
            geometry.setIndex([0,1,2])
        let material = new THREE.ShaderMaterial({
            side: THREE.DoubleSide,
            uniforms: {
              tex: { value: specularTexture },
              b: { value:  10 },
              lod: { value: 7 }
            }, 
            vertexShader: `
              precision highp float;
      
              
              attribute vec4 p;
      
              uniform float lod;
      
              varying vec2 tc;
      
              void main(){ 
      
                  gl_Position = p;
                  tc = vec2(0.5, 0.5/8.0) * p.xy + vec2(0.5, (lod + .5)/8.0); 
                  
              }
      
            `,
            fragmentShader: `
              
              precision highp float;
      
              varying vec2 tc;
      
              uniform sampler2D tex;
              uniform float b;
      
              void main() {    
                  
                  vec4 s = texture2D(tex,tc);
                  gl_FragColor.xyz = s.xyz*(b*s.w);
                  
              }
            `
          
        });

        let mesh = new THREE.Mesh(geometry, material);
        
        scene.add(mesh)



        renderer.setRenderTarget(rt);
        renderer.clear()
        renderer.render( scene, camera);
      
        let arr = new Uint8Array(rt.width*rt.height*4);
        renderer.readRenderTargetPixels( rt, 0,0, rt.width, rt.height, arr)


        let texture = new THREE.DataTexture(arr, rt.width, rt.height, THREE.RGBAFormat, THREE.UnsignedByteType);
            texture.minFilter = THREE.LinearFilter;
            texture.magFilter = THREE.LinearFilter;
            
  
        this.material.uniforms['tSkyTexture'].value = texture 
        this.material.needsUpdate = true
        
    

    }

}