import { Discipline } from './loader/discipline';
import { BimIfcClass } from './loader/bim-ifc-class';
import { ModelProductClass } from '@twinfinity/t8-client-model';
/** Factory class for various prebuilt Bim API loader option configurations */
export class BimApiLoadOptionsFactory {
    /** Loads IFC files matching the floor filter (must contain one of the floors) and all objects in them.
     * @param floors List of floors a IFC file must have at least one reference to in ordered to be considered for loading.
     * If length = 0 then all files will be matched.
     */
    static all(floors = []) {
        const uniqueFloorNames = new Set(floors.map((f) => f.toLowerCase()));
        return new BimApiLoadOptionsInternal((c) => true, (d) => true, (f) => uniqueFloorNames.size === 0 || !!(f !== undefined && uniqueFloorNames.has(f.toLowerCase())));
    }
    /** Only loads IFC files which match SimpleBuilding criteria. In those files only IFC objects
     * which match Simplebuilding criterias will keep their geometries. Any IFC objects that
     * have no children and where {@link BimIfcObject.hasGeometry} === false will be removed from
     * the IFC hierarchy.
     * @param floors List of floors a IFC file must have at least one reference to in ordered to be considered for loading. If 0 length then all files will be matched
     */
    static simpleBuilding(floors = []) {
        const simpleBuildingIfcClassTypes = new Set([
            'walls',
            'floor',
            'doors',
            'windows',
            'stairs',
            'space',
            'roof',
            'columns',
            'covering'
        ]);
        const simpleBuildingIfcClasses = new Set();
        simpleBuildingIfcClasses.add(BimIfcClass.getOrAdd('IfcPlate'));
        simpleBuildingIfcClasses.add(BimIfcClass.getOrAdd('IfcBuildingElementProxy'));
        const simpleBuildingDisciplines = new Set([
            Discipline.getOrAdd('A'),
            Discipline.getOrAdd('AR') /*,
            Discipline.getOrAdd('YE')*/
        ]);
        const uniqueFloorNames = new Set(floors.map((f) => f.toLowerCase()));
        return new BimApiLoadOptionsInternal((c) => simpleBuildingIfcClasses.has(c) || simpleBuildingIfcClassTypes.has(c.type), (d) => simpleBuildingDisciplines.has(d), (f) => uniqueFloorNames.size === 0 || !!(f !== undefined && uniqueFloorNames.has(f.toLowerCase())));
    }
    /** Only loads IFC files which match the architecture criteria. In those files only IFC objects
     * which match architecture criteria will keep their geometries. Any IFC objects that
     * have no children and where {@link BimIfcObject.hasGeometry} === false will be removed from
     * the IFC hiearchy.
     * @param floors List of floors a IFC file must have at least one reference to in ordered to be considered for loading. If 0 length then all files will be matched
     */
    static architecture(floors = []) {
        const architectureDisciplines = new Set([Discipline.getOrAdd('A'), Discipline.getOrAdd('AR')]);
        const uniqueFloorNames = new Set(floors.map((f) => f.toLowerCase()));
        return new BimApiLoadOptionsInternal((c) => true, (d) => architectureDisciplines.has(d), (f) => uniqueFloorNames.size === 0 || !!(f !== undefined && uniqueFloorNames.has(f.toLowerCase())));
    }
    /** Only loads IFC files cotaining spaces from disciplines A and AR. In those files, only IFC objects
     * which are spaces will keep their geometries. Any IFC objects that have no children and
     * where {@link BimIfcObject.hasGeometry} === false will be removed from the IFC hierarchy.
     * @param floors List of floors a IFC file must have at least one reference to in ordered to be considered for loading.
     * If length = 0, then all files will be matched.
     */
    static space(floors = []) {
        const spaceIfcClassTypes = new Set(['space']);
        const spaceDisciplines = new Set([Discipline.getOrAdd('A'), Discipline.getOrAdd('AR')]);
        const uniqueFloorNames = new Set(floors.map((f) => f.toLowerCase()));
        return new BimApiLoadOptionsInternal((c) => spaceIfcClassTypes.has(c.type), (d) => spaceDisciplines.has(d), (f) => uniqueFloorNames.size === 0 || (f !== undefined && uniqueFloorNames.has(f.toLowerCase())));
    }
    /** Only loads IFC files which contain the given floors and disciplines criteria. In those files, only IFC objects
     * which above criterias will keep their geometries. Any IFC objects that
     * have no children and where {@link BimIfcObject.hasGeometry} === false will be removed from
     * the IFC hierarchy.
     * @param disciplines List of disciplines that each IFC file is matched against. IFC file will only be loaded if it has matching
     * discipline. If length = 0 length, it will match all disciplines.
     * @param floors List of floors to which an IFC file must include at least one floor reference to be eligible for loading.
     * If length = 0 then all files will be matched.
     */
    static discipline(disciplines = [], floors = [], removeLeafIfcObjectsWithNoGeometry = true) {
        const uniqueDisciplines = new Set(disciplines);
        const uniqueFloorNames = new Set(floors.map((f) => f.toLowerCase()));
        return new BimApiLoadOptionsInternal((c) => true, (d) => uniqueDisciplines.size === 0 || uniqueDisciplines.has(d), (f) => uniqueFloorNames.size === 0 || !!(f !== undefined && uniqueFloorNames.has(f.toLowerCase())));
    }
}
class BimApiLoadOptionsDefaultObjectVisibility {
    // 0 = IfcSpaces. `true` if ifcspaces shall be visible
    // 1 = `true` if other objects are visible
    _ifcClassesWithGeometryVisibility = [false, false];
    constructor(loader) {
        // Code below is needlessly complex if all we need to do is
        // hide spaces if other types of objects are visible. However
        // it is easy to extend it (by adding items to ifcClassesWithGeometryStatistics and
        // _ifcClassesWithGeometryVisibility) with other conditions. At one time we wanted
        // to hide all coverings of disicpline A for example.
        const ifcClassesWithGeometryStatistics = [0, 0];
        for (const lE of loader.loaderElements) {
            for (const c of lE.classes) {
                if (c.objectsWithGeometryCount <= 0) {
                    continue;
                }
                if (c === ModelProductClass.predefined.space) {
                    ifcClassesWithGeometryStatistics[0]++;
                }
                else {
                    ifcClassesWithGeometryStatistics[1]++;
                }
            }
        }
        // Spaces are only visible if no other type of objects exist
        this._ifcClassesWithGeometryVisibility[0] = ifcClassesWithGeometryStatistics[1] <= 0;
        // Other objects are visible if other objects exist.
        this._ifcClassesWithGeometryVisibility[1] = ifcClassesWithGeometryStatistics[1] > 0;
    }
    isObjectVisible(o) {
        if (o.class === ModelProductClass.predefined.space) {
            // if no ifcoverings with discipline A and no other objects then spaces must be visible
            // otherwise they are invisible
            return this._ifcClassesWithGeometryVisibility[0];
        }
        return this._ifcClassesWithGeometryVisibility[1];
    }
}
/** Internal helper class used to specify valid load options. */
class BimApiLoadOptionsInternal {
    _isValidClass;
    _isValidDiscipline;
    _isValidFloor;
    removeLeafIfcObjectsWithNoGeometry;
    loadPropertySets;
    loadGeometry;
    throwOnNoIfcChanges;
    _ifcObjectVisibility;
    constructor(_isValidClass, _isValidDiscipline, _isValidFloor, removeLeafIfcObjectsWithNoGeometry = false, loadPropertySets = true, loadGeometry = true, throwOnNoIfcChanges = true) {
        this._isValidClass = _isValidClass;
        this._isValidDiscipline = _isValidDiscipline;
        this._isValidFloor = _isValidFloor;
        this.removeLeafIfcObjectsWithNoGeometry = removeLeafIfcObjectsWithNoGeometry;
        this.loadPropertySets = loadPropertySets;
        this.loadGeometry = loadGeometry;
        this.throwOnNoIfcChanges = throwOnNoIfcChanges;
    }
    ifcObjectHasGeometryPredicate(o) {
        // We dont include floor here on purpose. May change in the future but currently it is difficult
        // for users to know if a object is actually placed on a specific floor or not. It may look like it
        // is placed on a floor visually but in reality it can be placed on another floor.
        // Elevator shafts often starts on first floor and extends through many floors. However they still only
        // belong to a single floor (the first one).
        // We therefore include objects from all floors but in this.visible() we hide those that do not match
        // the floor filter. That way it is possible to make those objects visible again in client applications
        // so users can still use the UI and understand that the objects they were looking for may be
        // available, just in another location.
        return (this._isValidClass(o.class) &&
            this._isValidDiscipline(o.discipline) &&
            this._isValidFloor(o.enclosingFloor?.name));
    }
    ifcChangePredicate(ifcChange) {
        // If no floors in file then simulate a floor by using a dummy one.
        // Otherwise this._isValidFloor wont be executed since
        // floor array is zero length. So even if _isValidFloor would always return true it wont be executed.
        // Fixes issue with IFC files with no floors not being loaded.
        const floors = ifcChange.floors?.length > 0 ? ifcChange.floors : [{ name: 'unknown floor [123XA#"A]' }];
        return (this._isValidDiscipline(ifcChange.discipline) &&
            !!ifcChange.classes.find(this._isValidClass) &&
            !!floors.find((f) => this._isValidFloor(f?.name)));
    }
    isIfcObjectVisiblePredicate(o) {
        if (this._ifcObjectVisibility === undefined) {
            this._ifcObjectVisibility = new BimApiLoadOptionsDefaultObjectVisibility(o.ifcLoaderElement.loader);
        }
        const isVisible = this.ifcObjectHasGeometryPredicate(o) &&
            this._isValidFloor(o.enclosingFloor?.name) &&
            this._ifcObjectVisibility.isObjectVisible(o);
        return isVisible;
    }
    toExplicitLoadOptions(ifcChanges) {
        const tmp = ifcChanges.map((c) => {
            return { change: c, load: this.ifcChangePredicate(c) };
        });
        return {
            ifcObjectHasGeometryPredicate: this.ifcObjectHasGeometryPredicate.bind(this),
            removeLeafIfcObjectsWithNoGeometry: this.removeLeafIfcObjectsWithNoGeometry,
            loadPropertySets: this.loadPropertySets,
            loadGeometry: this.loadGeometry,
            isIfcObjectVisiblePredicate: this.isIfcObjectVisiblePredicate.bind(this),
            ifcChanges: tmp
        };
    }
}
//# sourceMappingURL=BimApiLoadOptionsFactory.js.map