"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IModelElementCloneContext = void 0;
/*---------------------------------------------------------------------------------------------
* 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
 */
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const Category_1 = require("./Category");
const NativePlatform_1 = require("./internal/NativePlatform");
const Symbols_1 = require("./internal/Symbols");
/** The context for transforming a *source* Element to a *target* Element and remapping internal identifiers to the target iModel.
 * @beta
 */
class IModelElementCloneContext {
    /** The source IModelDb. */
    sourceDb;
    /** The target IModelDb. */
    targetDb;
    /** The native import context */
    _nativeContext;
    /** Construct a new IModelElementCloneContext. It must be initialized with `initialize`, consider using [[IModelElementCloneContext.create]] instead
     * @param sourceDb The source IModelDb.
     * @param targetDb If provided the target IModelDb. If not provided, the source and target are the same IModelDb.
     */
    constructor(sourceDb, targetDb) {
        this.sourceDb = sourceDb;
        this.targetDb = (undefined !== targetDb) ? targetDb : sourceDb;
        this._nativeContext = new NativePlatform_1.IModelNative.platform.ImportContext(this.sourceDb[Symbols_1._nativeDb], this.targetDb[Symbols_1._nativeDb]);
    }
    /** perform necessary initialization to use a clone context, namely caching the reference types in the source's schemas */
    async initialize() {
    }
    /** construct and initialize an IModelElementCloneContext at once, for where you construct in an async context */
    static async create(...args) {
        const instance = new this(...args);
        await instance.initialize();
        return instance;
    }
    /** Returns `true` if this context is for transforming between 2 iModels and `false` if it for transforming within the same iModel. */
    get isBetweenIModels() { return this.sourceDb !== this.targetDb; }
    /** Dispose any native resources associated with this IModelElementCloneContext. */
    [Symbol.dispose]() { this._nativeContext.dispose(); }
    /** @deprecated in 5.0 - will not be removed until after 2026-06-13. Use [Symbol.dispose] instead. */
    dispose() {
        this[Symbol.dispose]();
    }
    /** Debugging aid that dumps the Id remapping details and other information to the specified output file.
     * @internal
     */
    dump(outputFileName) { this._nativeContext.dump(outputFileName); }
    /** Add a rule that remaps the specified source [CodeSpec]($common) to the specified target [CodeSpec]($common).
     * @param sourceCodeSpecName The name of the CodeSpec from the source iModel.
     * @param targetCodeSpecName The name of the CodeSpec from the target iModel.
     * @throws [[IModelError]] if either CodeSpec could not be found.
     */
    remapCodeSpec(sourceCodeSpecName, targetCodeSpecName) {
        const sourceCodeSpec = this.sourceDb.codeSpecs.getByName(sourceCodeSpecName);
        const targetCodeSpec = this.targetDb.codeSpecs.getByName(targetCodeSpecName);
        this._nativeContext.addCodeSpecId(sourceCodeSpec.id, targetCodeSpec.id);
    }
    /** Add a rule that remaps the specified source class to the specified target class. */
    remapElementClass(sourceClassFullName, targetClassFullName) {
        this._nativeContext.addClass(sourceClassFullName, targetClassFullName);
    }
    /** Add a rule that remaps the specified source Element to the specified target Element. */
    remapElement(sourceId, targetId) {
        this._nativeContext.addElementId(sourceId, targetId);
    }
    /** Remove a rule that remaps the specified source Element. */
    removeElement(sourceId) {
        this._nativeContext.removeElementId(sourceId);
    }
    /** Look up a target CodeSpecId from the source CodeSpecId.
     * @returns the target CodeSpecId or [Id64.invalid]($bentley) if a mapping not found.
     */
    findTargetCodeSpecId(sourceId) {
        if (core_bentley_1.Id64.invalid === sourceId) {
            return core_bentley_1.Id64.invalid;
        }
        return this._nativeContext.findCodeSpecId(sourceId);
    }
    /** Look up a target ElementId from the source ElementId.
     * @returns the target ElementId or [Id64.invalid]($bentley) if a mapping not found.
     */
    findTargetElementId(sourceElementId) {
        if (core_bentley_1.Id64.invalid === sourceElementId) {
            return core_bentley_1.Id64.invalid;
        }
        return this._nativeContext.findElementId(sourceElementId);
    }
    /** Filter out geometry entries in the specified SubCategory from GeometryStreams in the target iModel.
     * @note It is not possible to filter out a *default* SubCategory. A request to do so will be ignored.
     * @see [SubCategory.isDefaultSubCategory]($backend)
     */
    filterSubCategory(sourceSubCategoryId) {
        const sourceSubCategory = this.sourceDb.elements.tryGetElement(sourceSubCategoryId, Category_1.SubCategory);
        if (sourceSubCategory && !sourceSubCategory.isDefaultSubCategory) {
            this._nativeContext.filterSubCategoryId(sourceSubCategoryId);
        }
    }
    /** Returns `true` if there are any SubCategories being filtered. */
    get hasSubCategoryFilter() {
        return this._nativeContext.hasSubCategoryFilter();
    }
    /** Returns `true` if this SubCategory is being filtered. */
    isSubCategoryFiltered(subCategoryId) {
        return this._nativeContext.isSubCategoryFiltered(subCategoryId);
    }
    /** Import the specified font from the source iModel into the target iModel.
     * @internal
     */
    importFont(sourceFontNumber) {
        this.targetDb.clearFontMap(); // so it will be reloaded with new font info
        this._nativeContext.importFont(sourceFontNumber);
    }
    /** Import a single CodeSpec from the source iModel into the target iModel.
     * @internal
     */
    importCodeSpec(sourceCodeSpecId) {
        this._nativeContext.importCodeSpec(sourceCodeSpecId);
    }
    /** Clone the specified source Element into ElementProps for the target iModel.
     * @internal
     */
    cloneElement(sourceElement, cloneOptions) {
        const targetElementProps = this._nativeContext.cloneElement(sourceElement.id, cloneOptions);
        // Ensure that all NavigationProperties in targetElementProps have a defined value so "clearing" changes will be part of the JSON used for update
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        sourceElement.forEachProperty((propertyName, meta) => {
            if ((meta.isNavigation) && (undefined === sourceElement[propertyName])) {
                targetElementProps[propertyName] = core_common_1.RelatedElement.none;
            }
        }, false); // exclude custom because C++ has already handled them
        if (this.isBetweenIModels) {
            // The native C++ cloneElement strips off federationGuid, want to put it back if transformation is between iModels
            targetElementProps.federationGuid = sourceElement.federationGuid;
            if (core_common_1.CodeScopeSpec.Type.Repository === this.targetDb.codeSpecs.getById(targetElementProps.code.spec).scopeType) {
                targetElementProps.code.scope = core_common_1.IModel.rootSubjectId;
            }
        }
        // unlike other references, code cannot be null. If it is null, use an empty code instead
        if (targetElementProps.code.scope === core_bentley_1.Id64.invalid || targetElementProps.code.spec === core_bentley_1.Id64.invalid) {
            targetElementProps.code = core_common_1.Code.createEmpty();
        }
        const jsClass = this.sourceDb.getJsClass(sourceElement.classFullName);
        // eslint-disable-next-line @typescript-eslint/dot-notation
        jsClass["onCloned"](this, sourceElement.toJSON(), targetElementProps);
        return targetElementProps;
    }
    /**
     * serialize state to a sqlite database at a given path
     * assumes the database has not already had any context state serialized to it
     * @internal
     */
    saveStateToDb(db) {
        this._nativeContext.saveStateToDb(db[Symbols_1._nativeDb]);
    }
    /**
     * load state from a sqlite database at a given path
     * @internal
     */
    loadStateFromDb(db) {
        this._nativeContext.loadStateFromDb(db[Symbols_1._nativeDb]);
    }
}
exports.IModelElementCloneContext = IModelElementCloneContext;
//# sourceMappingURL=IModelElementCloneContext.js.map