/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { CompressedId64Set, Logger, StopWatch } from "@itwin/core-bentley";
import { Range3d } from "@itwin/core-geometry";
import { BackendLoggerCategory } from "./BackendLoggerCategory";
const loggerCategory = BackendLoggerCategory.CustomViewState3dCreator;
/**
 * Class which helps to generate a custom ViewState3d.
 * @internal
 */
export class CustomViewState3dCreator {
    _imodel;
    constructor(iModel) {
        this._imodel = iModel;
    }
    /** Gets default view state data such as category Ids and modelextents. If no model ids are passed in, all 3D models in the iModel are used.
     * @param [modelIds] Ids of models to display in the view.
     * @throws [IModelError]($common) If no 3d models are found in the iModel.
     */
    async getCustomViewState3dData(options) {
        let decompressedModelIds;
        if (options?.modelIds !== undefined)
            decompressedModelIds = CompressedId64Set.decompressArray(options.modelIds);
        const models = decompressedModelIds ?? await this._getAllModels();
        const categories = await this._getAllCategories();
        const modelExtents = await this._getModelExtents(models);
        return {
            modelIds: CompressedId64Set.sortAndCompress(models),
            categoryIds: CompressedId64Set.sortAndCompress(categories),
            modelExtents: modelExtents.toJSON(),
        };
    }
    async _getAllCategories() {
        // Only use categories with elements in them
        Logger.logInfo(loggerCategory, "Starting getAllCategories query.");
        const query = `SELECT DISTINCT Category.Id AS id FROM BisCore.GeometricElement3d WHERE Category.Id IN (SELECT ECInstanceId FROM BisCore.SpatialCategory)`;
        const categories = await this._executeQuery(query);
        Logger.logInfo(loggerCategory, "Finished getAllCategories query.");
        return categories;
    }
    /** Compute the union of the extents of all the specified models. */
    async _getModelExtents(modelIds) {
        if (modelIds.length === 0)
            return new Range3d();
        const timer = new StopWatch("getModelExtents query", true);
        const range = await this._imodel.models.queryRange(modelIds);
        timer.stop();
        Logger.logInfo(loggerCategory, "Finished getModelExtents query.", { timeElapsedMs: timer.elapsed });
        return range;
    }
    /** Get the Ids of all spatially-located, non-template 3d models in the iModel. */
    async _getAllModels() {
        // Note: IsNotSpatiallyLocated was introduced in a later version of the BisCore ECSchema.
        // If the iModel has an earlier version, the statement will throw because the property does not exist.
        // If the iModel was created from an earlier version and later upgraded to a newer version, the property may be NULL for models created prior to the upgrade.
        const select = "SELECT ECInstanceId FROM Bis.GeometricModel3D WHERE IsPrivate = false AND IsTemplate = false";
        const spatialCriterion = "AND (IsNotSpatiallyLocated IS NULL OR IsNotSpatiallyLocated = false)";
        let models = [];
        Logger.logInfo(loggerCategory, "Starting getAllModels query.");
        try {
            models = await this._executeQuery(`${select} ${spatialCriterion}`);
        }
        catch {
            models = await this._executeQuery(select);
        }
        Logger.logInfo(loggerCategory, "Finished getAllModels query.");
        return models;
    }
    _executeQuery = async (query) => {
        const rows = [];
        for await (const row of this._imodel.createQueryReader(query))
            rows.push(row.id);
        return rows;
    };
}
//# sourceMappingURL=CustomViewState3dCreator.js.map