import * as THREE from 'three';
import * as OBC from '@thatopen/components';
import * as OBF from '@thatopen/components-front';
import * as WEBIFC from 'web-ifc';

/**
 * @namespace BIM
 */
const BIM = {};
BIM.clipper = undefined;
BIM.classifier = undefined;
BIM.WEBIFC = WEBIFC;

/**
 * 
 * @param {HTMLElement} container The scene container element
 * @returns {object} {components, world}
 */
BIM.init = function (container) {
    const components = new OBC.Components();
    const worlds = components.get(OBC.Worlds);

    const world = worlds.create();
    
    world.scene = new OBC.SimpleScene(components);
    world.renderer = new OBC.SimpleRenderer(components, container);
    world.camera = new OBC.SimpleCamera(components);

    // Starts the app and updates components at 60 fps
    components.init();
    this.world = world;
    this.components = components;
}
/**
 * @param {HTMLElement} container The scene container element
 */
BIM.createScene = function (container) {
    this.init(container);
    // Add a grid to the scene
    const grids = this.components.get(OBC.Grids);
    const grid = grids.create(this.world);
    // (zoom_level, position)
    this.world.camera.controls.setLookAt(15, 15, 15, 0, 0, 0);

    this.world.scene.setup();
}
/**
 * Enable the clipping plane
 */
BIM.activateClipper = function () {
    const casters = this.components.get(OBC.Raycasters);
    casters.get(this.world);
    // Enable plane clipper
    const clipper = this.components.get(OBC.Clipper);
    clipper.enabled = true;
    clipper.create(this.world);
    clipper.visible = true;
    clipper.config.color = new THREE.Color('#eaeaea');
    clipper.config.size = 10;
    BIM.clipper = clipper;
}
/**
 * Delete a selected clipping plane 
 */
BIM.deleteClipper = function () {
    if (this.clipper.enabled) {
        this.clipper.delete(this.world);
    }
}
/**
 * @todo Serve web-ifc.wasm locally via AJAX
 * @param {ArrayBuffer} buffer The uploaded IFC file 
 * @param {string} name The filename to be used as the model's name
 * @returns {OBC.FragmentsGroup} model
 */
BIM.loadIfc = async function (buffer, name) {
    if (this.fragments) {
        this.fragments.dispose();
    }

    const fragments = this.components.get(OBC.FragmentsManager);
    const fragmentIfcLoader = this.components.get(OBC.IfcLoader);
    const classifier = this.components.get(OBC.Classifier);

    // NOTE: loads web-ifc WASM from https://unpkg.com/web-ifc@0.0.53/
    await fragmentIfcLoader.setup();

    /*
    fragmentIfcLoader.settings.wasm = {
      path: "./vendor/web-ifc/",
      absolute: false
    };
    */
    // Excludes IFC categories from the fragment
    const excludedCats = [
        WEBIFC.IFCTENDONANCHOR,
        WEBIFC.IFCREINFORCINGBAR,
        WEBIFC.IFCREINFORCINGELEMENT,
    ];

    for (const cat of excludedCats) {
        fragmentIfcLoader.settings.excludedCategories.add(cat);
    }

    fragmentIfcLoader.settings.webIfc.COORDINATE_TO_ORIGIN = true;
    fragmentIfcLoader.settings.webIfc.OPTIMIZE_PROFILES = true;

    const model = await fragmentIfcLoader.load(buffer);
    model.name = name;
    this.world.scene.three.add(model);

    // To actually add the model to the scene...
    for (const fragment of model.items) {
        this.world.meshes.add(fragment.mesh);
    }

    // Useful?
    this.fragments = fragments;
    this.model = model;

    BIM.classifier = classifier;

    return model;
}

/**
 * @param {OBC.FragmentsGroup} model The loaded IFC model
 */
BIM.setupHighligther = async function (model) {
    const world = this.world;
    const indexer = this.components.get(OBC.IfcRelationsIndexer);
    await indexer.process(model);

    let highlighter = null;

    if (!this.highlighter) {
        highlighter = this.components.get(OBF.Highlighter);
        highlighter.setup({ world });
    } else {
        highlighter = this.highlighter;
    }

    const li = document.querySelector('#selected-prop');
    highlighter.events.select.onHighlight.add(async (property) => {
        const set = property[Object.keys(property)[0]]
        const expressID = Array.from(set.entries())[0][0];
        
        let testProp = await model.getProperties(Number.parseInt(expressID)); 

        // BAD just for testing
        if (testProp !== null) {
            li.innerHTML = `
                <ul>
                    <li><strong>Name</strong>: ${testProp['Name'].value}</span>
                    <li><strong>Type</strong>: ${testProp['ObjectType']?.value ?? 'N/A'}</span>
                    <li><strong>Tag</strong>: ${testProp['Tag']?.value ?? 'N/A'}</li>
                </ul>
            `;
        }
    });

    highlighter.events.select.onClear.add(() => {
        li.innerHTML = '';
    });

    this.highlighter = highlighter;
}

export default BIM;
