Source: package/mesh/wrapped_mesh.js

import { Reporter } from '../../reporter/reporter.js';
import { WrappedTexture } from '../wrapped_texture.js';
import { WrappedMaterial } from '../material/wrapped_material.js';
import { checkPropTypes } from '../../lib.js';
import { BuildableComponent } from '../component/buildable_component.js';
import { Component } from '../../component/component.js';
import { Geometry } from '../geometry/geometry.js';
import { MaterialSet } from '../material/material_set.js';
import { Project } from '../../project.js';
import { GLTF } from './gltf.js';


import { ObjectSpaceNormalMap, Vector2, DoubleSide, sRGBEncoding, TextureLoader, RepeatWrapping } from '../../../node_modules/three/build/three.module.js';


//NodeMaterials
import { NodeMaterialLoader, NodeMaterialLoaderUtils } from '../../../node_modules/three/examples/jsm/loaders/NodeMaterialLoader.js';
import * as Nodes from '../../../node_modules/three/examples/jsm/nodes/Nodes.js';
import { buildNodeMaterial } from '../../utilities/nodeMaterialBuilder.js';


/** ProductBuilder Mesh */

class WrappedMesh extends BuildableComponent {

    /**
     * @param {Reporter} reporter
     * @param {Object} settings
     * @param {UUID} [settings.id]
     * @param {string} [settings.name]
     * @param {string} settings.meshName
     * @param {GLTF} settings.gltf
     * @param {MaterialSet} [settings.materialVariantGroup]
     * @param {WrappedMaterial} settings.defaultMaterial
     * @param {Geometry} [ settings.geometry ] // deze laten vervallen?
     * @param {Number} [ settings.tilingMultiplier ] 
     * @param {WrappedTexture} [ settings.aoMap ] 
     * @param {Number} [ settings.aoMapIntensity ]
     * @param {WrappedTexture} [ settings.normalMap ] 
     * @param {Object} [ settings.normalMapTiling ] 
     * @param {Number} [ settings.normalMapIntensity ]

     * "materialConfigurable": true,
        "castShadow": true,
        "receiveShadow": true 
     */

    constructor(reporter, settings) {

        super(
            reporter,
            settings
        );

        checkPropTypes(
            settings,
            {
                meshName: 'string',
                gltf: GLTF,
                defaultMaterial: WrappedMaterial,
            },
            {
                materialVariantGroup: MaterialSet,
                tilingMultiplier: 'number',
                aoMap: WrappedTexture,
                aoMapIntensity: 'number',
                normalMap: WrappedTexture,
                normalMapIntensity: 'number'
                //geometry: Geometry,
            }
        );

        // fucking typescript
        if (!Object.getOwnPropertyDescriptor(this,'materialVariantGroup')) {
            /** @type {MaterialSet} */
            this.materialVariantGroup = settings.materialVariantGroup;
        }

        this._tilingMultiplier = settings.tilingMultiplier
        this._aoMap = settings.aoMap
        this._aoMapIntensity = settings.aoMapIntensity
        this._normalMap = settings.normalMap ///-> dit is dubbel
        this._normalMapTiling = settings.normalMapTiling
        this._normalMapScale = settings.normalMapIntensity ///-> dit is dubbel
        this._meshObject = null;
    }


    static _exportName = {
        singular: 'mesh',
        plural: 'meshes'
    };


    /**
     * @param {WrappedMaterial} material 
     */

    async buildMeshMaterial( material ){
     
        //get the product normal map
        const normalMap = this._normalMap.content.main.medium
        normalMap.encoding = 3000
        //normalMap.flipY = true; //deze is (nog) niet nodig
        normalMap.wrapS = RepeatWrapping;
        normalMap.wrapT = RepeatWrapping;
        normalMap.repeat = this._normalMapTiling
        
        const normalMapTiling = this._normalMapTiling

        //create texture node with the product normal map
        // const normalNode 	= normalMap
        //                     ?	new Nodes.TextureNode( normalMap )
        //                     :	undefined

        const normalNode = new Nodes.TextureNode( normalMap )

        //create the node material
        const nodeMaterial = buildNodeMaterial(
            material,   
            normalNode,	
            normalMapTiling
        )

        //copy the id from the currentmaterial
        nodeMaterial.userData = { PB: { origin: material.userData.PB.origin } }

        //add the environment map
        nodeMaterial.envMap = material.envMap

        this._meshObject.mesh.material = nodeMaterial
        this._meshObject.mesh.material.normalScale = { x:2, y:2 }
        this._meshObject.mesh.material.needsUpdate = true;

        return nodeMaterial

    }


    /**
     * @param {ComponentPart} part 
     * @param {LoadingQuality} quality 
     * @param {Object<string,Component>} dependencies
     */

    async _build(part, quality, dependencies) {

        this._status[ part ][ quality ].setState( 'loading');


        


        switch (part) {

            case 'UI':
                if (this._settings.thumbnail) {
                    this._setContent('UI', quality, this._settings.thumbnail.content.main[quality]);
                }
                else {
                    this._setContent('UI', quality, Project.defaultImages.missing.cloneNode(true));
                }
                break;

            case 'main':

                //console.log( dependencies )

                if ( this._settings.materialVariantGroup && ! this._settings.materialVariantGroup.has( this._settings.defaultMaterial)){
                    // console.log( settings.materialVariantGroup )
                    throw new Error( `Can not construct ${this.label} because def mat ${this._settings.defaultMaterial.name || this._settings.defaultMaterial.id} is not in its matVarGrp ${settings.materialVariantGroup.name || settings.materialVariantGroup.id}` )
                }

                this._meshObject = dependencies.gltf.content.main[ quality ].gltf[ this._settings.meshName ];
                this._meshObject.mesh.material = dependencies.defaultMaterial.content.main[ quality ];
                
                //tiling multiplier
                if ( this.tilingMultiplier ){

                   for ( let mapType of [ 'bumpMap', 'emissiveMap', 'map', 'specularMap', 'roughnessMap', 'metalnessMap', 'normalMap' ])
                    {
                        if ( this._meshObject.mesh.material[ mapType ] )
                        {
                            
                            this._meshObject.mesh.material = this._meshObject.mesh.material.clone();
                            this._meshObject.mesh.material[ mapType ] = this._meshObject.mesh.material[ mapType ].clone();
                            this._meshObject.mesh.material[ mapType ].repeat.x *= this._settings.tilingMultiplier;
                            this._meshObject.mesh.material[ mapType ].repeat.y *= this._settings.tilingMultiplier;
                            this._meshObject.mesh.material[ mapType ].needsUpdate = true;
                        }
                    }
                }

                //product maps
                if( this._aoMap ){
                    //console.log( this._aoMap)
                   this._meshObject.mesh.material.aoMap = this._aoMap.content.main[ quality ];
                   this._meshObject.mesh.material.aoMap.needsUpdate = true;
                }

                if( this._aoMapIntensity ){
                    console.log( this._aoMapIntensity )
                    this._meshObject.mesh.material.aoMapIntensity = this._aoMapIntensity
                    this._meshObject.mesh.material.aoMap.needsUpdate = true;
                }

                if( this._normalMap ){

                    this.buildMeshMaterial( this._meshObject.mesh.material )

                }


                this._meshObject.mesh.castShadow = true;
                this._meshObject.mesh.receiveShadow = true;

                // const mesh = new Mesh(
                //     dependencies.geometry.content.main[ quality ],
                //     dependencies.defaultMaterial.content.main[ quality ]
                // );

                this._setContent( 'main', quality, this._meshObject );

                break;

            default:

                this._setContent(part, quality, null);
                
                break;
        }

        return this;
    }
}

export { WrappedMesh };