/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module iModels
 */
import { Id64, JsonUtils } from "@itwin/core-bentley";
import { BisCodeSpec, Code, Rank, SubCategoryAppearance, } from "@itwin/core-common";
import { DefinitionElement } from "./Element";
import { EditTxn } from "./EditTxn";
import { IModelDb } from "./IModelDb";
import { CategoryOwnsSubCategories } from "./NavigationRelationship";
import { _implicitTxn } from "./internal/Symbols";
/** Defines the appearance for graphics in Geometric elements
 * @public @preview
 */
export class SubCategory extends DefinitionElement {
    static get className() { return "SubCategory"; }
    /** The Appearance parameters for this SubCategory */
    appearance;
    /** Optional description of this SubCategory. */
    description;
    constructor(props, iModel) {
        super(props, iModel);
        this.appearance = new SubCategoryAppearance(props.appearance);
        this.description = JsonUtils.asString(props.description);
    }
    /**
     * SubCategory custom HandledProps include 'description' and 'properties'.
     * @inheritdoc
     * @beta
     */
    static _customHandledProps = [
        { propertyName: "description", source: "Class" },
        { propertyName: "properties", source: "Class" },
    ];
    /**
     * SubCategory deserializes 'description' and 'properties'.
     * @inheritdoc
     * @beta
     */
    static deserialize(props) {
        const elProps = super.deserialize(props);
        elProps.description = JsonUtils.asString(props.row.description);
        if (props.row.properties !== '') {
            elProps.appearance = JSON.parse(props.row.properties);
        }
        else {
            elProps.appearance = undefined;
        }
        return elProps;
    }
    /**
     * SubCategory serialize 'description' and 'properties'.
     * @inheritdoc
     * @beta
     */
    static serialize(props, iModel) {
        const inst = super.serialize(props, iModel);
        if (props.description !== undefined) {
            inst.description = props.description;
        }
        if (props.appearance !== undefined) {
            inst.properties = JSON.stringify(props.appearance);
        }
        return inst;
    }
    toJSON() {
        const val = super.toJSON();
        val.appearance = this.appearance.toJSON();
        if (this.description && this.description.length > 0)
            val.description = this.description;
        return val;
    }
    /** Get the SubCategory's name (its Code value). */
    getSubCategoryName() { return this.code.value; }
    /** Get the Id of the SubCategory. */
    getSubCategoryId() { return this.id; }
    /** Get the Id of this SubCategory's parent Category. */
    getCategoryId() { return this.parent ? this.parent.id : Id64.invalid; }
    /** Check if this is the default SubCategory of its parent Category. */
    get isDefaultSubCategory() { return IModelDb.getDefaultSubCategoryId(this.getCategoryId()) === this.getSubCategoryId(); }
    /** Create a Code for a SubCategory given a name that is meant to be unique within the scope of the specified parent Category.
     * @param iModel  The IModel
     * @param parentCategoryId The Id of the parent Category that owns the SubCategory and provides the scope for its name.
     * @param codeValue The name of the SubCategory
     */
    static createCode(iModel, parentCategoryId, codeValue) {
        const codeSpec = iModel.codeSpecs.getByName(BisCodeSpec.subCategory);
        return new Code({ spec: codeSpec.id, scope: parentCategoryId, value: codeValue });
    }
    /** Create a new SubCategory
     * @param iModelDb The iModel
     * @param parentCategoryId Create the new SubCategory as a child of this [[Category]]
     * @param name The name of the SubCategory
     * @param appearance The appearance settings to use for this SubCategory
     * @returns The newly constructed SubCategory element.
     * @throws [[IModelError]] if unable to create the element.
     */
    static create(iModelDb, parentCategoryId, name, appearance) {
        if (appearance instanceof SubCategoryAppearance)
            appearance = appearance.toJSON();
        const parentCategory = iModelDb.elements.getElement(parentCategoryId);
        const subCategoryProps = {
            classFullName: this.classFullName,
            model: parentCategory.model,
            parent: new CategoryOwnsSubCategories(parentCategoryId),
            code: this.createCode(iModelDb, parentCategoryId, name),
            appearance,
        };
        return new SubCategory(subCategoryProps, iModelDb);
    }
    static insert(txnOrDb, parentCategoryId, name, appearance) {
        const txn = txnOrDb instanceof EditTxn ? txnOrDb : txnOrDb[_implicitTxn];
        const subCategory = this.create(txn.iModel, parentCategoryId, name, appearance);
        return subCategory.insert(txn);
    }
}
/** A Category element is the target of the `category` member of [[GeometricElement]].
 * @public @preview
 */
export class Category extends DefinitionElement {
    static get className() { return "Category"; }
    rank = Rank.User;
    description;
    constructor(props, iModel) {
        super(props, iModel);
        this.rank = JsonUtils.asInt(props.rank);
        this.description = JsonUtils.asString(props.description);
    }
    /**
     * Category custom HandledProps include 'rank' and 'description'.
     * @inheritdoc
     * @beta
     */
    static _customHandledProps = [
        { propertyName: "rank", source: "Class" },
        { propertyName: "description", source: "Class" },
    ];
    /**
     * Category deserializes 'rank' and 'description'.
     * @inheritdoc
     * @beta
     */
    static deserialize(props) {
        const elProps = super.deserialize(props);
        elProps.description = JsonUtils.asString(props.row.description);
        elProps.rank = JsonUtils.asInt(props.row.rank);
        return elProps;
    }
    /**
     * Category serialize 'rank' and 'description'.
     * @inheritdoc
     * @beta
     */
    static serialize(props, iModel) {
        const inst = super.serialize(props, iModel);
        if (undefined !== props.description) {
            inst.description = props.description;
        }
        inst.rank = props.rank;
        return inst;
    }
    toJSON() {
        const val = super.toJSON();
        val.rank = this.rank;
        if (this.description && this.description.length > 0)
            val.description = this.description;
        return val;
    }
    /** Get the Id of the default SubCategory for this Category. */
    myDefaultSubCategoryId() { return IModelDb.getDefaultSubCategoryId(this.id); }
    setDefaultAppearance(txnOrProps, props) {
        let txn;
        let appearance;
        if (txnOrProps instanceof EditTxn) {
            txn = txnOrProps;
            if (props === undefined)
                throw new Error("Invalid argument");
            appearance = props;
        }
        else {
            txn = this.iModel[_implicitTxn];
            appearance = txnOrProps;
        }
        if (appearance instanceof SubCategoryAppearance)
            appearance = appearance.toJSON();
        const subCat = this.iModel.elements.getElement(this.myDefaultSubCategoryId());
        subCat.appearance = new SubCategoryAppearance(appearance);
        subCat.update(txn);
    }
}
/** Categorizes 2d GeometricElements.
 * @public @preview
 */
export class DrawingCategory extends Category {
    static get className() { return "DrawingCategory"; }
    constructor(opts, iModel) { super(opts, iModel); }
    /** Get the name of the CodeSpec that is used by DrawingCategory objects. */
    static getCodeSpecName() { return BisCodeSpec.drawingCategory; }
    /** Looks up the CategoryId of a DrawingCategory by model and name */
    static queryCategoryIdByName(iModel, scopeModelId, categoryName) {
        const code = DrawingCategory.createCode(iModel, scopeModelId, categoryName);
        return iModel.elements.queryElementIdByCode(code);
    }
    /** Create a Code for a DrawingCategory given a name that is meant to be unique within the scope of the specified DefinitionModel.
     * @param iModel  The IModel
     * @param scopeModelId The Id of the DefinitionModel that contains the DrawingCategory and provides the scope for its name.
     * @param codeValue The name of the category
     * @return A drawing category Code
     */
    static createCode(iModel, scopeModelId, codeValue) {
        const codeSpec = iModel.codeSpecs.getByName(DrawingCategory.getCodeSpecName());
        return new Code({ spec: codeSpec.id, scope: scopeModelId, value: codeValue });
    }
    /** Create a new DrawingCategory
     * @param iModelDb The iModel
     * @param definitionModelId The [[DefinitionModel]]
     * @param name The name of the DrawingCategory
     * @returns The newly constructed DrawingCategory element.
     * @throws [[IModelError]] if unable to create the element.
     */
    static create(iModelDb, definitionModelId, name) {
        const categoryProps = {
            classFullName: this.classFullName,
            model: definitionModelId,
            code: this.createCode(iModelDb, definitionModelId, name),
            isPrivate: false,
        };
        return new DrawingCategory(categoryProps, iModelDb);
    }
    static insert(txnOrDb, definitionModelId, name, defaultAppearance) {
        const txn = txnOrDb instanceof EditTxn ? txnOrDb : txnOrDb[_implicitTxn];
        const category = this.create(txn.iModel, definitionModelId, name);
        category.id = category.insert(txn);
        category.setDefaultAppearance(txn, defaultAppearance);
        return category.id;
    }
}
/** Categorizes SpatialElements. See [how to create a SpatialCategory]($docs/learning/backend/CreateElements.md#SpatialCategory).
 * @public @preview
 */
export class SpatialCategory extends Category {
    static get className() { return "SpatialCategory"; }
    constructor(opts, iModel) { super(opts, iModel); }
    /** Get the name of the CodeSpec that is used by SpatialCategory objects. */
    static getCodeSpecName() { return BisCodeSpec.spatialCategory; }
    /** Looks up the CategoryId of a SpatialCategory by model and name */
    static queryCategoryIdByName(iModel, scopeModelId, categoryName) {
        const code = SpatialCategory.createCode(iModel, scopeModelId, categoryName);
        return iModel.elements.queryElementIdByCode(code);
    }
    /** Create a Code for a SpatialCategory given a name that is meant to be unique within the scope of the specified DefinitionModel.
     * @param iModel  The IModel
     * @param scopeModelId The Id of the DefinitionModel that contains the SpatialCategory and provides the scope for its name.
     * @param codeValue The name of the category
     * @return A spatial category Code
     */
    static createCode(iModel, scopeModelId, codeValue) {
        const codeSpec = iModel.codeSpecs.getByName(SpatialCategory.getCodeSpecName());
        return new Code({ spec: codeSpec.id, scope: scopeModelId, value: codeValue });
    }
    /** Create a new SpatialCategory
     * @param iModelDb The iModel
     * @param definitionModelId The [[DefinitionModel]]
     * @param name The name/CodeValue of the SpatialCategory
     * @returns The newly constructed SpatialCategory element.
     * @throws [[IModelError]] if unable to create the element.
     */
    static create(iModelDb, definitionModelId, name) {
        const categoryProps = {
            classFullName: this.classFullName,
            model: definitionModelId,
            code: this.createCode(iModelDb, definitionModelId, name),
            isPrivate: false,
        };
        return new SpatialCategory(categoryProps, iModelDb);
    }
    static insert(txnOrDb, definitionModelId, name, defaultAppearance) {
        const txn = txnOrDb instanceof EditTxn ? txnOrDb : txnOrDb[_implicitTxn];
        const category = this.create(txn.iModel, definitionModelId, name);
        category.id = category.insert(txn);
        category.setDefaultAppearance(txn, defaultAppearance);
        return category.id;
    }
}
//# sourceMappingURL=Category.js.map