/** @internal NOTE: Internal APIs. Subject to change. Use of these APIs in production applications is not supported. */ /** */

import { FastTransform } from '../math/FastTransform';
import { IBimIfcLoaderElement } from './bim-ifc-loader-element';
import { BimProductMesh } from './BimProductMesh';
import { Quaternion, Matrix, Vector3 } from './babylonjs-maths-import';
import { BimTransform } from './bim-format-types';
import { IndexedMap } from '../IndexedMap';

/**
 * Represents a repository for calculating and caching transforms for
 * a IFC file.
 */
export class BimTransformsRepository {
    private _fastTransformsLookup: IndexedMap<FastTransform>;
    private _matrixLookup: IndexedMap<Matrix>;

    private static readonly _tmp = {
        vector0: Vector3.Zero(),
        vector1: Vector3.Zero(),
        quaternion0: new Quaternion()
    };

    /**
     * Constructs a new instance of the {@link BimTransformsRepository} class.
     * @param _loaderElement The {@link IBimIfcLoaderElement} used for loading IFC data.
     */
    public constructor(private readonly _loaderElement: IBimIfcLoaderElement) {
        const initialCapacity = _loaderElement.index.transforms.length;
        this._fastTransformsLookup = new IndexedMap<FastTransform>(initialCapacity);
        this._matrixLookup = new IndexedMap<Matrix>(initialCapacity);
    }

    /**
     * Retrieves the {@link FastTransform} for the given {@link BimProductMesh}.
     * If the {@link FastTransform} is already cached, it is returned directly.
     * Otherwise, a new {@link FastTransform} is created, cached, and returned.
     * @param m The {@link BimProductMesh} for which to retrieve the {@link FastTransform}.
     * @param noCache If `true`, the created {@link FastTransform} will not be cached for future use. Defaults to `true`. Call {@link clear} to remove cached transforms.
     * @returns The {@link FastTransform} for the given {@link BimProductMesh}.
     */
    public getTransform(m: BimProductMesh, noCache = false): FastTransform {
        const t = this._fastTransformsLookup.get(m.wt);
        if (t !== undefined) {
            return t;
        }

        const wt = this._loaderElement.index.transforms[m.wt]!;
        const fastTransform = new FastTransform(wt);
        !noCache && this._fastTransformsLookup.set(m.wt, fastTransform);
        return fastTransform;
    }

    /**
     * Converts a {@link BimTransform} to a Matrix and assigns the result to the provided outMatrix.
     * @param wt - The {@link BimTransform} to convert.
     * @param outMatrix - The Matrix to assign the converted result to.
     * @returns The converted Matrix. Same as `outMatrix`.
     */
    public convertBimTransformToMatrixToRef(wt: BimTransform, outMatrix: Matrix): Matrix {
        const scale = BimTransformsRepository._tmp.vector0.copyFromFloats(wt.sx, wt.sy, wt.sz);
        const rotation = BimTransformsRepository._tmp.quaternion0.copyFromFloats(wt.qx, wt.qy, wt.qz, wt.qw);
        const translation = BimTransformsRepository._tmp.vector1.copyFromFloats(wt.x, wt.y, wt.z);
        return Matrix.ComposeToRef(scale, rotation, translation, outMatrix);
    }

    /**
     * Retrieves the matrix associated with the given {@link BimProductMesh}.
     * If the matrix is already cached, it is returned directly.
     * Otherwise, the matrix is calculated, cached, and returned.
     * @param m The {@link BimProductMesh} for which to retrieve the matrix.
     * @param noCache If `true`, the calculated matrix will be not be cached for future use. Defaults to `true`. Call {@link clear} to remove cached matrices.
     * @returns The matrix associated with the given BimProductMesh.
     */
    public getMatrix(m: BimProductMesh, noCache = true): Matrix {
        const t = this._matrixLookup.get(m.wt);
        if (t !== undefined) {
            return t;
        }

        const wt = this._loaderElement.index.transforms[m.wt]!;
        const matrix = this.convertBimTransformToMatrixToRef(wt, Matrix.Identity());
        !noCache && this._matrixLookup.set(m.wt, matrix);
        return matrix;
    }

    /**
     * Clears the transforms repository by removing all cached transforms.
     */
    public clear(): void {
        this._fastTransformsLookup.clear();
        this._matrixLookup.clear();
    }
}
