import { IfcFileIndex } from './BimIfcIndex';
import { HttpResponseType } from '../http';
import { Fail } from '../fail';
import { Matrix, Quaternion, Vector3 } from './babylonjs-maths-import';
import { setMax, setMin } from '../math';
import { coordinateSystems } from './babylon-coordinate-system';
import { BimTransformsRepository } from './bim-transforms-repository';
/**
 * Handles fetching and parsing .idx files to create IfcFileIndex instances.
 */
export class IfcFileIndexReader {
    bimApi;
    static _tmp = {
        scale: Vector3.Zero(),
        rotation: Quaternion.Identity(),
        translate: Vector3.Zero(),
        matrix: Matrix.Identity(),
        points: [
            Vector3.Zero(),
            Vector3.Zero(),
            Vector3.Zero(),
            Vector3.Zero(),
            Vector3.Zero(),
            Vector3.Zero(),
            Vector3.Zero(),
            Vector3.Zero()
        ],
        min: Vector3.Zero(),
        max: Vector3.Zero()
    };
    constructor(bimApi) {
        this.bimApi = bimApi;
    }
    /**
     * Fetches an .idx file from the given URL and creates an IfcFileIndex instance.
     *
     * @param indexUrl - The URL of the .idx file to fetch
     * @param discipline - The discipline to associate with the index
     * @param transform - Optional transform to apply to all transformations in the IFC
     * @returns A Promise that resolves to an IfcFileIndex instance or a failure result
     */
    async read(indexUrl, discipline, transform) {
        const ifcIndexResponse = await this.bimApi.get(indexUrl, HttpResponseType.json);
        if (ifcIndexResponse.status !== 200) {
            return Fail({
                httpStatus: ifcIndexResponse.status,
                reason: 'Failed to fetch .idx file.',
                url: indexUrl.toString()
            });
        }
        const ifcIndexDTO = await ifcIndexResponse.value;
        const ifcIndex = new IfcFileIndex(ifcIndexDTO, discipline);
        // Process transforms and regions with defaults and coordinate system conversions
        this.processIndexData(ifcIndex, transform);
        // Clean up the DTO to free memory since IfcFileIndex has copied its references
        Object.keys(ifcIndexDTO).forEach((key) => delete ifcIndexDTO[key]);
        return ifcIndex;
    }
    processIndexData(index, transform) {
        const k = 10000.0;
        const toMeterScale = 1.0 / index.modelFactors.oneMeter;
        const tmp = IfcFileIndexReader._tmp;
        // Create a temporary transforms repository to use for matrix conversions
        const tempTransformsRepo = {
            convertBimTransformToMatrixToRef: BimTransformsRepository.prototype.convertBimTransformToMatrixToRef
        };
        for (const wt of index.transforms) {
            if (!wt)
                continue;
            // Apply default values and scaling
            wt.x = (wt.x ?? 0) * toMeterScale;
            wt.y = (wt.y ?? 0) * toMeterScale;
            wt.z = (wt.z ?? 0) * toMeterScale;
            wt.qx = (wt.qx ?? 0) / k;
            wt.qy = (wt.qy ?? 0) / k;
            wt.qz = (wt.qz ?? 0) / k;
            wt.qw = (wt.qw ?? k) / k;
            wt.sx = (wt.sx ?? 1) * toMeterScale;
            wt.sy = (wt.sy ?? 1) * toMeterScale;
            wt.sz = (wt.sz ?? 1) * toMeterScale;
            const ifcTransform = tempTransformsRepo.convertBimTransformToMatrixToRef.call({ index }, wt, tmp.matrix);
            // Convert transforms from IFC space to Babylon.js space
            const bjsTransform = coordinateSystems.convertIfcTransformToBabylonTransformInPlace(ifcTransform);
            if (transform) {
                // bjsTransform = transform * bjsTransform
                bjsTransform.multiplyToRef(transform, bjsTransform);
            }
            bjsTransform.decompose(tmp.scale, tmp.rotation, tmp.translate);
            wt.x = tmp.translate.x;
            wt.y = tmp.translate.y;
            wt.z = tmp.translate.z;
            wt.qx = tmp.rotation.x;
            wt.qy = tmp.rotation.y;
            wt.qz = tmp.rotation.z;
            wt.qw = tmp.rotation.w;
            wt.sx = tmp.scale.x;
            wt.sy = tmp.scale.y;
            wt.sz = tmp.scale.z;
        }
        // Process region bounding boxes
        for (const region of index.regions) {
            this.getBabylonBboxPointsFromIfcMinMaxToRef({ x: region.bbox[0], y: region.bbox[1], z: region.bbox[2] }, { x: region.bbox[3], y: region.bbox[4], z: region.bbox[5] }, tmp.points, toMeterScale, transform);
            setMax(tmp.min);
            setMin(tmp.max);
            tmp.points.forEach((point) => {
                tmp.min.minimizeInPlace(point);
                tmp.max.maximizeInPlace(point);
            });
            region.bbox[0] = tmp.min.x;
            region.bbox[1] = tmp.min.y;
            region.bbox[2] = tmp.min.z;
            region.bbox[3] = tmp.max.x;
            region.bbox[4] = tmp.max.y;
            region.bbox[5] = tmp.max.z;
        }
    }
    getBabylonBboxPointsFromIfcMinMaxToRef(min, max, out, scale = 1, transform) {
        out[0].set(min.x, min.z, min.y).scaleInPlace(scale);
        out[1].set(max.x, min.z, min.y).scaleInPlace(scale);
        out[2].set(min.x, min.z, max.y).scaleInPlace(scale);
        out[3].set(min.x, max.z, min.y).scaleInPlace(scale);
        out[4].set(max.x, min.z, max.y).scaleInPlace(scale);
        out[5].set(max.x, max.z, min.y).scaleInPlace(scale);
        out[6].set(min.x, max.z, max.y).scaleInPlace(scale);
        out[7].set(max.x, max.z, max.y).scaleInPlace(scale);
        if (transform) {
            out.forEach((p) => {
                Vector3.TransformCoordinatesToRef(p, transform, p);
            });
        }
    }
}
//# sourceMappingURL=IfcFileIndexReader.js.map