/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module ChangedElementsDb
 */
import { DbResult, IModelStatus, OpenMode } from "@itwin/core-bentley";
import { IModelError } from "@itwin/core-common";
import { BriefcaseManager } from "./BriefcaseManager";
import { ECDbOpenMode } from "./ECDb";
import { IModelHost } from "./IModelHost";
import { IModelNative } from "./internal/NativePlatform";
import { _hubAccess, _nativeDb } from "./internal/Symbols";
/** An ChangedElementsDb file
 * @internal
 */
export class ChangedElementsDb {
    _nativeDb;
    constructor() {
        this._nativeDb = new IModelNative.platform.ChangedElementsECDb();
    }
    [Symbol.dispose]() {
        if (!this._nativeDb)
            return;
        this.closeDb();
        this._nativeDb.dispose();
        this._nativeDb = undefined;
    }
    /** Create a ChangedElementsDb
     * @param pathName The path to the ECDb file to create.
     * @throws [IModelError]($common) if the operation failed.
     */
    _createDb(briefcase, pathName) {
        const status = this.nativeDb.createDb(briefcase[_nativeDb], pathName);
        if (status !== DbResult.BE_SQLITE_OK)
            throw new IModelError(status, "Failed to created ECDb");
    }
    /** Open the Changed Elements Db.
     * @param pathName The path to the ECDb file to open
     * @param openMode Open mode
     * @throws [IModelError]($common) if the operation failed.
     */
    _openDb(pathName, openMode = ECDbOpenMode.Readonly) {
        const nativeOpenMode = openMode === ECDbOpenMode.Readonly ? OpenMode.Readonly : OpenMode.ReadWrite;
        const tryUpgrade = openMode === ECDbOpenMode.FileUpgrade;
        const status = this.nativeDb.openDb(pathName, nativeOpenMode, tryUpgrade);
        if (status !== DbResult.BE_SQLITE_OK)
            throw new IModelError(status, "Failed to open ECDb");
    }
    /** Open the Changed Elements Db.
     * @param pathName The path to the ECDb file to open
     * @param openMode Open mode
     * @returns ChangedElementsDb
     */
    static openDb(pathName, openMode = ECDbOpenMode.Readonly) {
        const cacheDb = new ChangedElementsDb();
        cacheDb._openDb(pathName, openMode);
        return cacheDb;
    }
    /** Create the changed elements cache db
     * @param briefcase IModelDb to use
     * @param pathName The path to the ECDb file to create.
     * @returns The new cache db
     */
    static createDb(briefcase, pathName) {
        const cacheDb = new ChangedElementsDb();
        cacheDb._createDb(briefcase, pathName);
        return cacheDb;
    }
    /** Processes a range of changesets and adds it to the changed elements cache
     * @param briefcase iModel briefcase to use
     * @param options Options for processing
     */
    async processChangesets(accessToken, briefcase, options) {
        const iModelId = briefcase.iModelId;
        const first = (await IModelHost[_hubAccess].queryChangeset({ iModelId, changeset: { id: options.startChangesetId }, accessToken })).index;
        const end = (await IModelHost[_hubAccess].queryChangeset({ iModelId, changeset: { id: options.endChangesetId }, accessToken })).index;
        const changesets = await IModelHost[_hubAccess].downloadChangesets({ accessToken, iModelId, range: { first, end }, targetDir: BriefcaseManager.getChangeSetsPath(iModelId) });
        // ChangeSets need to be processed from newest to oldest
        changesets.reverse();
        const status = this.nativeDb.processChangesets(briefcase[_nativeDb], changesets, options.rulesetId, options.filterSpatial, options.wantParents, options.wantPropertyChecksums, options.rulesetDir, options.tempDir, options.wantChunkTraversal);
        if (status !== DbResult.BE_SQLITE_OK)
            throw new IModelError(status, "Failed to process changesets");
        return status;
    }
    /** Processes a range of changesets and adds it to the changed elements cache
     * This call will close the IModelDb object as it is required for processing and applying changesets
     * @param briefcase iModel briefcase to use
     * @param options options for processing
     */
    async processChangesetsAndRoll(accessToken, briefcase, options) {
        const iModelId = briefcase.iModelId;
        const first = (await IModelHost[_hubAccess].queryChangeset({ iModelId, changeset: { id: options.startChangesetId }, accessToken })).index;
        const end = (await IModelHost[_hubAccess].queryChangeset({ iModelId, changeset: { id: options.endChangesetId }, accessToken })).index;
        const changesets = await IModelHost[_hubAccess].downloadChangesets({ accessToken, iModelId, range: { first, end }, targetDir: BriefcaseManager.getChangeSetsPath(iModelId) });
        // ChangeSets need to be processed from newest to oldest
        changesets.reverse();
        // Close briefcase before doing processing and rolling briefcase
        const dbFilename = briefcase.pathName;
        const dbGuid = briefcase.iModelId;
        briefcase.close();
        // Process changesets
        const status = this.nativeDb.processChangesetsAndRoll(dbFilename, dbGuid, changesets, options.rulesetId, options.filterSpatial, options.wantParents, options.wantPropertyChecksums, options.rulesetDir, options.tempDir, options.wantRelationshipCaching, options.relationshipCacheSize, options.wantChunkTraversal, options.wantBoundingBoxes);
        if (status !== DbResult.BE_SQLITE_OK)
            throw new IModelError(status, "Failed to process changesets");
        return status;
    }
    /** Get changed elements between two changesets
     * @param startChangesetId Start Changeset Id
     * @param endChangesetId End Changeset Id
     * @returns Returns the changed elements between the changesets provided
     * @throws [IModelError]($common) if the operation failed.
     */
    getChangedElements(startChangesetId, endChangesetId) {
        const result = this.nativeDb.getChangedElements(startChangesetId, endChangesetId);
        if (result.error || !result.result)
            throw new IModelError(result.error ? result.error.status : -1, result.error ? result.error.message : "Problem getting changed elements");
        return (result.result.changedElements);
    }
    /** Get changed models between two changesets
     * @param startChangesetId Start Changeset Id
     * @param endChangesetId End Changeset Id
     * @returns Returns the changed models between the changesets provided
     * @throws [IModelError]($common) if the operation failed.
     */
    getChangedModels(startChangesetId, endChangesetId) {
        const result = this.nativeDb.getChangedElements(startChangesetId, endChangesetId);
        if (result.error || !result.result)
            throw new IModelError(result.error ? result.error.status : -1, result.error ? result.error.message : "Problem getting changed models");
        return (result.result.changedModels);
    }
    /** Get changed models between two changesets
     * @param startChangesetId Start Changeset Id
     * @param endChangesetId End Changeset Id
     * @returns Returns the changed models between the changesets provided
     * @throws [IModelError]($common) if the operation failed.
     */
    getChangeData(startChangesetId, endChangesetId) {
        const result = this.nativeDb.getChangedElements(startChangesetId, endChangesetId);
        if (result.error)
            throw new IModelError(result.error.status, result.error.message);
        return result.result;
    }
    /** Returns true if the Changed Elements Db is open */
    get isOpen() { return this.nativeDb.isOpen(); }
    /** Returns true if the cache already contains this changeset Id */
    isProcessed(changesetId) { return this.nativeDb.isProcessed(changesetId); }
    /** Close the Db after saving any uncommitted changes.
     * @throws [IModelError]($common) if the database is not open.
     */
    closeDb() {
        this.nativeDb.closeDb();
    }
    cleanCaches() {
        this.nativeDb.cleanCaches();
    }
    /** @internal */
    get nativeDb() {
        if (!this._nativeDb)
            throw new IModelError(IModelStatus.BadRequest, "ChangedElementsDb object has already been disposed.");
        return this._nativeDb;
    }
}
//# sourceMappingURL=ChangedElementsDb.js.map