import { Box3, Vector3, Group } from "three";
class MeshUtils {
    constructor(engine) {
        this.engine = engine;
    }

    assignParams(mesh, params){
        ['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
        [
            'visible', 'name', 'fontSize', 'color', 'lineHeight', 
            'maxWidth', 'anchorX', 'anchorY', 'outlineColor', 'outlineWidth', 'textAlign'
        ].forEach(p=>{
            if (params[p]!==undefined) mesh[p] = params[p];
        });
    }
    
    assignMaterial(mesh, params){
        if (params.name && params.material){
            //let mp = params.material.metalness ? new MeshStandardMaterial(params.material) : new MeshBasicMaterial(params.material)
            Object.assign(mesh.material, params.material)
            if (params.dm){
                this.engine.loadTexture(params.dm, params.path, undefined, [mesh.material, 'map'])
                // var dm = new TextureLoader().setPath(params.path).load(params.dm);
                // mesh.material.map = dm;
            }
            if (params.nm){
                this.engine.loadTexture(params.nm, params.path, undefined, [mesh.material, 'normalMap'])
                //mesh.material.normalMap = new TextureLoader().setPath(params.path).load(params.nm);
            }
            if (params.em) {
                this.engine.loadTexture(params.em, params.path, undefined, [mesh.material, 'emissiveMap'])
                //mesh.material.emissiveMap = new TextureLoader().setPath(params.path).load(params.em);
            }
            //mesh.material = mp;
            mesh.material.needsUpdate = true;
        }
    }
    
    getBoundingBox(object){
        let bb = new Box3().setFromObject(object);
        let scale = new Vector3(1,1,1);
        object.traverseAncestors(o=>{
            scale.multiply(o.scale);
        });
        bb.min.divide(scale);
        bb.max.divide(scale);
        return bb;
    }
    
    getBoundingBoxSize(bb){
        return new Vector3(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
    }
    
    getBoundingBoxMaxLength(bb){
        let size = this.getBoundingBoxSize(bb)
        return Math.max(size.x, size.y, size.z)
    }
    
    getBoundingBoxCenterPoint(bb, relativeTo){
        relativeTo = relativeTo || new Vector3(0,0,0)
        let size = this.getBoundingBoxSize(bb)
        return new Vector3(
            bb.min.x + (size.x)/2 - relativeTo.x, 
            bb.min.y + (size.y)/2 - relativeTo.y, 
            bb.min.z + (size.z)/2 - relativeTo.z
        )
    }
    
    autoScale(object, mk = 1) {
        if (mk === null) return;
        let bb = this.getBoundingBox(object);
        let k = this.getBoundingBoxMaxLength(bb);
        object.scale.multiplyScalar(mk / k);
    }
    
    wrapInGroup(object){
        if (object.isWrapper) return object;
        let group = new Group();
        group.userData.bbox = this.getBoundingBox(object);
        group.add(object);
        group.isWrapper = true;
        return group;
    }
    
    centerOrigin(object){
        let group = this.wrapInGroup(object);
        let position = this.getBoundingBoxCenterPoint(group.userData.bbox, object.position).negate();
        object.position.copy(position)
        return group;
    }
    
    bottomOrigin(object){
        let group = this.centerOrigin(object);
        group.children[0].position.y = -group.userData.bbox.min.y
        return group;
    }
    
    clearObject(o){
        let disposables = []
        o.traverse(object => {
            if (object.isMesh) {
                disposables.push(object);
            }
            if (object.isLight){
                //console.log('Disposeing light', object)
                object.dispose();
                object.shadow?.dispose();
                object.shadow?.map?.dispose();
            }
            if (object.userData?._io){
                object.userData._io.dispose?.();
                delete object.userData._io;
            }
        });
        disposables.forEach(object=>{
            object.removeFromParent();
            object.geometry.dispose();
            if (object.material.isMaterial) {
                this.clearMaterial(object.material)
            } else {
                for (const material of object.material) this.clearMaterial(material)
            }
        })
        o.clear();
    }
    
    clearMaterial(material) {
        material.dispose();
        for (const key of Object.keys(material)) {
            const value = material[key]
            if (value && typeof value == 'object' && 'minFilter' in value) {
                //console.log('Disposing', value.name, this.renderer.info.memory.textures );
                value.dispose();
                //console.log('Disposed', value.name, this.renderer.info.memory.textures );
            }
        }
    }
}

export { MeshUtils }