import * as THREE from 'three'
import BufferReader from '../assets/marmosets/parser/BufferReader';

import { MviewAsset, Scene as MviewScene, Mesh as MviewMesh, Material as MviewMaterial } from '../assets/marmosets/MviewFiles';
import { MeshMaterial } from '../materials/marmoset'
import BasicMaterial from '../materials/marmoset/BasicMaterial';

class MarmosetModel extends THREE.Object3D {

    meshs: THREE.Mesh[]

    constructor() {

        super()

        this.meshs = [];
            
    }


    private compressVector3( vectorIn: THREE.Vector2 )  
    {
        let vectorOut = new THREE.Vector3();

        let revert = vectorIn.y > 32767.1/65535.0;

        vectorIn.y = revert ? vectorIn.y - 32768.0/65535.0 : vectorIn.y;

        vectorOut.x = (2.0*65535.0/32767.0)* vectorIn.x - 1.0;
        vectorOut.y = (2.0*65535.0/32767.0)* vectorIn.y - 1.0;
        vectorOut.z = Math.sqrt( THREE.MathUtils.clamp( 1 - (vectorOut.x * vectorOut.x + vectorOut.y * vectorOut.y), 0, 1) );

        vectorOut.z = revert ? -vectorOut.z : vectorOut.z;
        
        return vectorOut;

    }


    buildMeshes( meshes: MviewMesh[], files: Map<string, MviewAsset>, materials: Map<string, MeshMaterial> ) {

        if(meshes.length > 0) {


            for( let i = 0; i < meshes.length; i++ ) {

                let item = meshes[i];

                let file = files.get( item.file )
    
                if( !file ) {
    
                    console.warn(`找不到[Mesh]${ item.name }的数据`)
    
                } else {
        
                    this.buildMesh(item, file, materials)
                    
                }
                
            }


        }

    }


    buildMesh( mviewMesh: MviewMesh, asset: MviewAsset, materialAssets: Map<string, MeshMaterial>) {

        let bufferReader = new BufferReader(asset.data);

        let stride = 32; //8 字节   position 3 * 4, uv 2 * 4, tangent 1 * 4, bitTangent 1 * 4, normal 1 *4 

        let indexBuffer = bufferReader.readBytes(mviewMesh.indexCount * mviewMesh.indexTypeSize);
        let indexBufferAttribute = mviewMesh.indexTypeSize == 2 ? new THREE.Uint16BufferAttribute(indexBuffer, 1) : new THREE.Uint32BufferAttribute(indexBuffer, 1);
       

        let wireBuffer = bufferReader.readBytes(mviewMesh.wireCount * mviewMesh.indexTypeSize);

        let vertexBuffer = bufferReader.readBytes(mviewMesh.vertexCount * stride);
        let useTHREE = true;
        let positionAttribute: THREE.InterleavedBufferAttribute | THREE.Float32BufferAttribute
        let uvAttribute: THREE.InterleavedBufferAttribute | THREE.Float32BufferAttribute
        let normalAttribute: THREE.InterleavedBufferAttribute | THREE.Float32BufferAttribute
        let tangentAttribute: THREE.InterleavedBufferAttribute  | THREE.Uint16BufferAttribute | undefined
        let bitTangentAttribute: THREE.InterleavedBufferAttribute  | THREE.Uint16BufferAttribute | undefined

        if( useTHREE ) {

            let dataView = new DataView(vertexBuffer);

            let positionAttributeArray = new Float32Array(mviewMesh.vertexCount * 3);
            let uvAttributeArray = new Float32Array(mviewMesh.vertexCount * 2);
            let normalAttributeArray = new Float32Array(mviewMesh.vertexCount * 3);

            for( let i = 0; i < mviewMesh.vertexCount; i++ ) {

                positionAttributeArray[3 * i + 0] = dataView.getFloat32(32 * i + 0, true);
                positionAttributeArray[3 * i + 1] = dataView.getFloat32(32 * i + 4, true);
                positionAttributeArray[3 * i + 2] = dataView.getFloat32(32 * i + 8, true);

                uvAttributeArray[2 * i + 0] = dataView.getFloat32(32 * i + 12, true);
                uvAttributeArray[2 * i + 1] = dataView.getFloat32(32 * i + 16, true); 

                let normal = new THREE.Vector2()
                    normal.x = dataView.getUint16( 32 * i + 28, true ) / 65535
                    normal.y = dataView.getUint16( 32 * i + 30, true ) / 65535
            

                let normal3 = this.compressVector3(normal)

                normalAttributeArray[3 * i + 0] = normal3.x;
                normalAttributeArray[3 * i + 1] = normal3.y;
                normalAttributeArray[3 * i + 2] = normal3.z;
               
            }
            
            positionAttribute = new THREE.Float32BufferAttribute(positionAttributeArray, 3)
            uvAttribute = new THREE.Float32BufferAttribute(uvAttributeArray, 2)
            normalAttribute = new THREE.Float32BufferAttribute(normalAttributeArray, 3)
            tangentAttribute = undefined
            bitTangentAttribute = undefined

        } else {

           
            let dataView = new DataView(vertexBuffer);

            let positionAttributeArray = new Float32Array(mviewMesh.vertexCount * 3);
            let uvAttributeArray = new Float32Array(mviewMesh.vertexCount * 2);
            let tangentAttributeArray = new Uint16Array(mviewMesh.vertexCount * 2);
            let bitTangentAttributeArray = new Uint16Array(mviewMesh.vertexCount * 2)
            let normalAttributeArray = new Uint16Array(mviewMesh.vertexCount * 2);

            for( let i = 0; i < mviewMesh.vertexCount; i++ ) {

                positionAttributeArray[3 * i + 0] = dataView.getFloat32(32 * i + 0, true);
                positionAttributeArray[3 * i + 1] = dataView.getFloat32(32 * i + 4, true);
                positionAttributeArray[3 * i + 2] = dataView.getFloat32(32 * i + 8, true);

                uvAttributeArray[2 * i + 0] = dataView.getFloat32(32 * i + 12, true);
                uvAttributeArray[2 * i + 1] = dataView.getFloat32(32 * i + 16, true); 

                tangentAttributeArray[2 * i + 0] = dataView.getUint16(32 * i + 20, true);
                tangentAttributeArray[2 * i + 1] = dataView.getUint16(32 * i + 22, true);
            
                bitTangentAttributeArray[2 * i + 0] = dataView.getUint16(32 * i + 24, true);
                bitTangentAttributeArray[2 * i + 1] = dataView.getUint16(32 * i + 26, true);

                normalAttributeArray[2 * i + 0] = dataView.getUint16(32 * i + 28, true);
                normalAttributeArray[2 * i + 1] = dataView.getUint16(32 * i + 30, true);
            }

            positionAttribute = new THREE.Float32BufferAttribute(positionAttributeArray, 3)
            uvAttribute = new THREE.Float32BufferAttribute(uvAttributeArray, 2)
            tangentAttribute = new THREE.Uint16BufferAttribute( tangentAttributeArray, 2);
            bitTangentAttribute = new THREE.Uint16BufferAttribute( bitTangentAttributeArray, 2);
            normalAttribute = new THREE.Uint16BufferAttribute(normalAttributeArray, 2, true);

        }
        


        let geometry = new THREE.BufferGeometry();
            geometry.setAttribute("position", positionAttribute);
            geometry.setAttribute("uv", uvAttribute);
            tangentAttribute && geometry.setAttribute("aTangent", tangentAttribute);
            bitTangentAttribute && geometry.setAttribute("aBitangent", bitTangentAttribute);
            useTHREE ? geometry.setAttribute("normal", normalAttribute) : geometry.setAttribute("aNormal", normalAttribute);
            geometry.setIndex(indexBufferAttribute);

            geometry.computeBoundingBox()
            geometry.computeBoundingSphere()
            
        
        for(let i = 0; i < mviewMesh.subMeshes.length; i++ ) {

            let subMesh = mviewMesh.subMeshes[i];
            geometry.addGroup(subMesh.firstIndex, subMesh.indexCount, i)
            
        }

        let materials = mviewMesh.subMeshes.map( item => materialAssets.get(item.material) || new BasicMaterial())
    
        let mesh = new THREE.Mesh(geometry, materials );
            mesh.name = mviewMesh.name

        // mesh = new THREE.Mesh(
        //     new THREE.SphereBufferGeometry(30, 50, 50),
        //     materialAssets.get("Sonya_Exosuit1")
        // )

        this.add(mesh);
        
        return mesh;
    }

    private buildGeometry( ) {


    }
}

export default MarmosetModel