/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module ModelState
 */
import { Id64, JsonUtils } from "@itwin/core-bentley";
import { RealityDataFormat, RealityModelDisplaySettings, RelatedElement, } from "@itwin/core-common";
import { Point2d, Range3d } from "@itwin/core-geometry";
import { EntityState } from "./EntityState";
import { RealityDataSource } from "./RealityDataSource";
import { createOrbitGtTileTreeReference, createPrimaryTileTreeReference, createRealityTileTreeReference } from "./tile/internal";
import { SpatialClassifiersState } from "./SpatialClassifiersState";
/** Represents the front-end state of a [Model]($backend).
 * @public
 * @extensions
 */
export class ModelState extends EntityState {
    static get className() { return "Model"; }
    constructor(props, iModel, state) {
        super(props, iModel, state);
        this.modeledElement = RelatedElement.fromJSON(props.modeledElement);
        this.name = props.name ? props.name : "";
        this.parentModel = Id64.fromJSON(props.parentModel); // NB! Must always match the model of the modeledElement!
        this.isPrivate = JsonUtils.asBool(props.isPrivate);
        this.isTemplate = JsonUtils.asBool(props.isTemplate);
    }
    /** Add all custom-handled properties of a Model to a json object. */
    toJSON() {
        const val = super.toJSON();
        val.modeledElement = this.modeledElement;
        val.parentModel = this.parentModel;
        val.name = this.name;
        if (this.isPrivate)
            val.isPrivate = this.isPrivate;
        if (this.isTemplate)
            val.isTemplate = this.isTemplate;
        return val;
    }
    /** Determine whether this is a GeometricModel */
    get isGeometricModel() { return false; }
    /** Attempts to cast this model to a geometric model. */
    get asGeometricModel() { return undefined; }
    /** Attempts to cast this model to a 3d geometric model. */
    get asGeometricModel3d() { return undefined; }
    /** Attempts to cast this model to a 2d geometric model. */
    get asGeometricModel2d() { return undefined; }
    /** Attempts to cast this model to a spatial model. */
    get asSpatialModel() { return undefined; }
    /**
     * Return the tool tip for this model. This is called only if the hit does not return a tooltip.
     * @internal
     */
    getToolTip(_hit) { return undefined; }
}
/** Represents the front-end state of a [GeometricModel]($backend).
 * The contents of a GeometricModelState can be rendered inside a [[Viewport]].
 * @public
 * @extensions
 */
export class GeometricModelState extends ModelState {
    static get className() { return "GeometricModel"; }
    constructor(props, iModel, state) {
        super(props, iModel, state);
        this.geometryGuid = props.geometryGuid;
    }
    /** @internal */
    get asGeometricModel() { return this; }
    /** Returns true if this is a 2d model (a [[GeometricModel2dState]]). */
    get is2d() { return !this.is3d; }
    /** @internal */
    get isGeometricModel() { return true; }
    /** @internal */
    get treeModelId() { return this.id; }
    /** Query for the union of the ranges of all the elements in this GeometricModel.
     * @note This value is cached after the first time it is called.
     * @public
     */
    async queryModelRange() {
        if (undefined === this._modelRange) {
            const ranges = await this.iModel.models.queryExtents(this.id);
            this._modelRange = Range3d.fromJSON(ranges[0]?.extents);
        }
        return this._modelRange;
    }
    /** @internal */
    createTileTreeReference(view) {
        // If this is a reality model, its tile tree is obtained from reality data service URL.
        const spatialModel = this.asSpatialModel;
        const rdSourceKey = this.jsonProperties.rdSourceKey;
        const getDisplaySettings = () => view.displayStyle.settings.getRealityModelDisplaySettings(this.id) ?? RealityModelDisplaySettings.defaults;
        if (rdSourceKey) {
            const useOrbitGtTileTreeReference = rdSourceKey.format === RealityDataFormat.OPC;
            const treeRef = (!useOrbitGtTileTreeReference) ?
                createRealityTileTreeReference({
                    rdSourceKey,
                    iModel: this.iModel,
                    source: view,
                    modelId: this.id,
                    // url: tilesetUrl, // If rdSourceKey is defined, url is not used
                    classifiers: undefined !== spatialModel ? spatialModel.classifiers : undefined,
                    getDisplaySettings,
                }) :
                createOrbitGtTileTreeReference({
                    rdSourceKey,
                    iModel: this.iModel,
                    source: view,
                    modelId: this.id,
                    // orbitGtBlob: props.orbitGtBlob!, // If rdSourceKey is defined, orbitGtBlob is not used
                    classifiers: undefined !== spatialModel ? spatialModel.classifiers : undefined,
                    getDisplaySettings,
                });
            return treeRef;
        }
        const orbitGtBlob = this.jsonProperties.orbitGtBlob;
        // If this is an OrbitGt reality model, create it's reference
        if (orbitGtBlob) {
            let orbitGtName = "";
            if (orbitGtBlob.blobFileName !== "") {
                if (orbitGtBlob.blobFileName[0] === "/")
                    orbitGtName = orbitGtBlob.blobFileName.substring(1);
                else
                    orbitGtName = orbitGtBlob.blobFileName;
            }
            // Create rdSourceKey if not provided
            const rdSourceKeyOGT = RealityDataSource.createKeyFromOrbitGtBlobProps(orbitGtBlob);
            return createOrbitGtTileTreeReference({
                rdSourceKey: rdSourceKeyOGT,
                iModel: this.iModel,
                source: view,
                modelId: this.id,
                orbitGtBlob,
                name: orbitGtName,
                classifiers: undefined !== spatialModel ? spatialModel.classifiers : undefined,
                getDisplaySettings,
            });
        }
        // If this is a TileTree reality model, create it's reference
        const tilesetUrl = this.jsonProperties.tilesetUrl;
        if (tilesetUrl) {
            const rdSourceKeyCS = RealityDataSource.createKeyFromUrl(tilesetUrl);
            return createRealityTileTreeReference({
                rdSourceKey: rdSourceKeyCS,
                url: tilesetUrl,
                iModel: this.iModel,
                source: view,
                modelId: this.id,
                tilesetToDbTransform: this.jsonProperties.tilesetToDbTransform,
                classifiers: undefined !== spatialModel ? spatialModel.classifiers : undefined,
                getDisplaySettings,
            });
        }
        return createPrimaryTileTreeReference(view, this);
    }
}
/** Represents the front-end state of a [GeometricModel2d]($backend).
 * @public
 * @extensions
 */
export class GeometricModel2dState extends GeometricModelState {
    static get className() { return "GeometricModel2d"; }
    constructor(props, iModel, state) {
        super(props, iModel, state);
        this.globalOrigin = Point2d.fromJSON(props.globalOrigin);
    }
    /** @internal */
    get is3d() { return false; }
    /** @internal */
    get asGeometricModel2d() { return this; }
    toJSON() {
        const val = super.toJSON();
        val.globalOrigin = this.globalOrigin;
        return val;
    }
}
/** Represents the front-end state of a [GeometricModel3d]($backend).
 * @public
 * @extensions
 */
export class GeometricModel3dState extends GeometricModelState {
    static get className() { return "GeometricModel3d"; }
    constructor(props, iModel, state) {
        super(props, iModel, state);
        this.isNotSpatiallyLocated = JsonUtils.asBool(props.isNotSpatiallyLocated);
        this.isPlanProjection = JsonUtils.asBool(props.isPlanProjection);
    }
    /** @internal */
    toJSON() {
        const val = super.toJSON();
        if (this.isNotSpatiallyLocated)
            val.isNotSpatiallyLocated = true;
        if (this.isPlanProjection)
            val.isPlanProjection = true;
        return val;
    }
    /** @internal */
    get is3d() { return true; }
    /** @internal */
    get asGeometricModel3d() { return this; }
    /** If true, then the elements in this GeometricModel3dState are in real-world coordinates and will be in the spatial index. */
    get isSpatiallyLocated() { return !this.isNotSpatiallyLocated; }
}
/** Represents the front-end state of a [SheetModel]($backend).
 * @public
 * @extensions
 */
export class SheetModelState extends GeometricModel2dState {
    static get className() { return "SheetModel"; }
}
/** Represents the front-end state of a [SpatialModel]($backend).
 * @public
 * @extensions
 */
export class SpatialModelState extends GeometricModel3dState {
    static get className() { return "SpatialModel"; }
    /** @internal */
    get asSpatialModel() { return this; }
    constructor(props, iModel, state) {
        super(props, iModel, state);
        if (this.isRealityModel)
            this.classifiers = SpatialClassifiersState.create(this.jsonProperties);
    }
    /** Return true if this is a reality model (represented by a 3d tile set). */
    get isRealityModel() {
        return !!this.jsonProperties.tilesetUrl || !!this.jsonProperties.rdSourceKey || !!this.jsonProperties.orbitGtBlob;
    }
}
/** Represents the front-end state of a [PhysicalModel]($backend).
 * @public
 * @extensions
 */
export class PhysicalModelState extends SpatialModelState {
    static get className() { return "PhysicalModel"; }
}
/** Represents the front-end state of a [SpatialLocationModel]($backend).
 * @public
 * @extensions
 */
export class SpatialLocationModelState extends SpatialModelState {
    static get className() { return "SpatialLocationModel"; }
}
/** Represents the front-end state of a [DrawingModel]($backend).
 * @public
 * @extensions
 */
export class DrawingModelState extends GeometricModel2dState {
    static get className() { return "DrawingModel"; }
}
/** Represents the front-end state of a [SectionDrawingModel]($backend).
 * @public
 * @extensions
 */
export class SectionDrawingModelState extends DrawingModelState {
    static get className() { return "SectionDrawingModel"; }
}
//# sourceMappingURL=ModelState.js.map