/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module IModelConnection
 */
import { BeEvent } from "@itwin/core-bentley";
import { EcefLocation, GeographicCRS, ipcAppChannels, } from "@itwin/core-common";
import { Point3d, Range3d } from "@itwin/core-geometry";
import { IpcApp, NotificationHandler } from "./IpcApp";
import { EntityChanges } from "./TxnEntityChanges";
/**
 * Base class for notification handlers for events from the backend that are specific to a [[BriefcaseConnection]].
 * @see [[BriefcaseTxns]].
 * @public
 */
export class BriefcaseNotificationHandler extends NotificationHandler {
    constructor(_key) {
        super();
        this._key = _key;
    }
    get channelName() { return `${this.briefcaseChannelName}/${this._key}`; }
}
/** Manages local changes to a [[BriefcaseConnection]] via [Txns]($docs/learning/InteractiveEditing.md).
 * @see [[BriefcaseConnection.txns]].
 * @see [TxnManager]($backend) for the backend counterpart.
 * @public
 */
export class BriefcaseTxns extends BriefcaseNotificationHandler {
    /** @internal */
    get briefcaseChannelName() {
        return ipcAppChannels.txns;
    }
    /** @internal */
    constructor(iModel) {
        super(iModel.key);
        /** Event raised after Txn validation or changeset application to indicate the set of changed elements.
         * @note If there are many changed elements in a single Txn, the notifications are sent in batches so this event *may be called multiple times* per Txn.
         */
        this.onElementsChanged = new BeEvent();
        /** Event raised after Txn validation or changeset application to indicate the set of changed models.
         * @note If there are many changed models in a single Txn, the notifications are sent in batches so this event *may be called multiple times* per Txn.
         */
        this.onModelsChanged = new BeEvent();
        /** Event raised after the geometry within one or more [[GeometricModelState]]s is modified by applying a changeset or validation of a transaction.
         * A model's geometry can change as a result of:
         *  - Insertion or deletion of a geometric element within the model; or
         *  - Modification of an existing element's geometric properties; or
         *  - An explicit request to flag it as changed via [IModelDb.Models.updateModel]($backend).
         */
        this.onModelGeometryChanged = new BeEvent();
        /** Event raised before a commit operation is performed. Initiated by a call to [[BriefcaseConnection.saveChanges]], unless there are no changes to save.
         * @see [[onCommitted]] for the event raised after the operation.
         */
        this.onCommit = new BeEvent();
        /** Event raised after a commit operation is performed. Initiated by a call to [[BriefcaseConnection.saveChanges]], even if there were no changes to save.
         * The event supplies the following information:
         *  - `hasPendingTxns`: true if the briefcase has local changes not yet pushed to the server.
         *  - `time`: the time at which changes were saved on the backend (obtained via `Date.now()`).
         * @see [[onCommit]] for the event raised before the operation.
         */
        this.onCommitted = new BeEvent();
        /** Event raised for a read-only briefcase that was opened with the `watchForChanges` flag enabled when changes made by another connection are applied to the briefcase.
         * @see [[onReplayedExternalTxns]] for the event raised after all such changes have been applied.
         */
        this.onReplayExternalTxns = new BeEvent();
        /** Event raised for a read-only briefcase that was opened with the `watchForChanges` flag enabled when changes made by another connection are applied to the briefcase.
         * @see [[onReplayExternalTxns]] for the event raised before the changes are applied.
         */
        this.onReplayedExternalTxns = new BeEvent();
        /** Event raised after a changeset has been applied to the briefcase.
         * Changesets may be applied as a result of [[BriefcaseConnection.pullChanges]], or by undo/redo operations.
         */
        this.onChangesApplied = new BeEvent();
        /** Event raised before an undo/redo operation is performed.
         * @see [[onAfterUndoRedo]] for the event raised after the operation.
         */
        this.onBeforeUndoRedo = new BeEvent();
        /** Event raised after an undo/redo operation is performed.
         * @see [[onBeforeUndoRedo]] for the event raised before to the operation.
         */
        this.onAfterUndoRedo = new BeEvent();
        /** Event raised after changes are pulled and merged into the briefcase.
         * @see [[BriefcaseConnection.pullAndMergeChanges]].
         */
        this.onChangesPulled = new BeEvent();
        /** Event raised after the briefcase's local changes are pushed.
         * @see [[BriefcaseConnection.pushChanges]].
         */
        this.onChangesPushed = new BeEvent();
        this._iModel = iModel;
        this._cleanup = this.registerImpl();
    }
    /** @internal */
    dispose() {
        if (this._cleanup) {
            this._cleanup();
            this._cleanup = undefined;
            this.onElementsChanged.clear();
            this.onModelsChanged.clear();
            this.onModelGeometryChanged.clear();
            this.onCommit.clear();
            this.onCommitted.clear();
            this.onChangesApplied.clear();
            this.onBeforeUndoRedo.clear();
            this.onAfterUndoRedo.clear();
            this.onChangesPulled.clear();
            this.onChangesPushed.clear();
        }
    }
    /** Query if the briefcase has any pending Txns waiting to be pushed. */
    async hasPendingTxns() {
        return IpcApp.appFunctionIpc.hasPendingTxns(this._iModel.key);
    }
    /** Determine if any reversible (undoable) changes exist.
     * @see [[reverseSingleTxn]] or [[reverseAll]] to undo changes.
     */
    async isUndoPossible() {
        return IpcApp.appFunctionIpc.isUndoPossible(this._iModel.key);
    }
    /** Determine if any reinstatable (redoable) changes exist.
     * @see [[reinstateTxn]] to redo changes.
     */
    async isRedoPossible() {
        return IpcApp.appFunctionIpc.isRedoPossible(this._iModel.key);
    }
    /** Get the description of the operation that would be reversed by calling [[reverseTxns]]`(1)`.
     * This is useful for showing the operation that would be undone, for example in a menu.
     */
    async getUndoString() {
        return IpcApp.appFunctionIpc.getUndoString(this._iModel.key);
    }
    /** Get a description of the operation that would be reinstated by calling [[reinstateTxn]].
     * This is useful for showing the operation that would be redone, in a pull-down menu for example.
     */
    async getRedoString() {
        return IpcApp.appFunctionIpc.getRedoString(this._iModel.key);
    }
    /** Reverse (undo) the most recent operation.
     * @see [[reinstateTxn]] to redo operations.
     * @see [[reverseAll]] to undo all operations.
     * @see [[isUndoPossible]] to determine if any reversible operations exist.
     */
    async reverseSingleTxn() {
        return this.reverseTxns(1);
    }
    /** Reverse (undo) the most recent operation(s) to the briefcase in the current session.
     * @param numOperations the number of operations to reverse. If this is greater than 1, the entire set of operations will
     *  be reinstated together when/if [[reinstateTxn]] is called.
     * @note If there are any outstanding uncommitted changes, they are reversed.
     * @note The term "operation" is used rather than Txn, since multiple Txns can be grouped together via [TxnManager.beginMultiTxnOperation]($backend). So,
     * even if numOperations is 1, multiple Txns may be reversed if they were grouped together when they were made.
     * @note If numOperations is too large only the number of reversible operations are reversed.
     */
    async reverseTxns(numOperations) {
        return IpcApp.appFunctionIpc.reverseTxns(this._iModel.key, numOperations);
    }
    /** Reverse (undo) all changes back to the beginning of the session.
     * @see [[reinstateTxn]] to redo changes.
     * @see [[reverseSingleTxn]] to undo only the most recent operation.
     * @see [[isUndoPossible]] to determine if any reversible operations exist.
     */
    async reverseAll() {
        return IpcApp.appFunctionIpc.reverseAllTxn(this._iModel.key);
    }
    /** Reinstate (redo) the most recently reversed transaction. Since at any time multiple transactions can be reversed, it
     * may take multiple calls to this method to reinstate all reversed operations.
     * @returns Success if a reversed transaction was reinstated, error status otherwise.
     * @note If there are any outstanding uncommitted changes, they are canceled before the Txn is reinstated.
     * @see [[isRedoPossible]] to determine if any reinstatable operations exist.
     * @see [[reverseSingleTxn]] or [[reverseAll]] to undo changes.
     */
    async reinstateTxn() {
        return IpcApp.appFunctionIpc.reinstateTxn(this._iModel.key);
    }
    /** Restart the current TxnManager session. This causes all Txns in the current session to no longer be undoable (as if the file was closed
     * and reopened.)
     * @note This can be quite disconcerting to the user expecting to be able to undo previously made changes. It should only be used
     * under extreme circumstances where damage to the file or session could happen if the currently committed are reversed. Use sparingly and with care.
     * Probably a good idea to alert the user it happened.
     */
    async restartTxnSession() {
        await IpcApp.appFunctionIpc.restartTxnSession(this._iModel.key);
    }
    /** @internal */
    notifyElementsChanged(changed) {
        this.onElementsChanged.raiseEvent(new EntityChanges(changed));
    }
    /** @internal */
    notifyModelsChanged(changed) {
        this.onModelsChanged.raiseEvent(new EntityChanges(changed));
    }
    /** @internal */
    notifyGeometryGuidsChanged(changes) {
        this.onModelGeometryChanged.raiseEvent(changes);
    }
    /** @internal */
    notifyCommit() {
        this.onCommit.raiseEvent();
    }
    /** @internal */
    notifyCommitted(hasPendingTxns, time) {
        this.onCommitted.raiseEvent(hasPendingTxns, time);
    }
    /** @internal */
    notifyReplayExternalTxns() {
        this.onReplayExternalTxns.raiseEvent();
    }
    /** @internal */
    notifyReplayedExternalTxns() {
        this.onReplayedExternalTxns.raiseEvent();
    }
    /** @internal */
    notifyChangesApplied() {
        this.onChangesApplied.raiseEvent();
    }
    /** @internal */
    notifyBeforeUndoRedo(isUndo) {
        this.onBeforeUndoRedo.raiseEvent(isUndo);
    }
    /** @internal */
    notifyAfterUndoRedo(isUndo) {
        this.onAfterUndoRedo.raiseEvent(isUndo);
    }
    /** @internal */
    notifyPulledChanges(parentChangeset) {
        this.onChangesPulled.raiseEvent(parentChangeset);
    }
    /** @internal */
    notifyPushedChanges(parentChangeset) {
        this.onChangesPushed.raiseEvent(parentChangeset);
    }
    /** @internal */
    notifyIModelNameChanged(name) {
        this._iModel.name = name;
    }
    /** @internal */
    notifyRootSubjectChanged(subject) {
        this._iModel.rootSubject = subject;
    }
    /** @internal */
    notifyProjectExtentsChanged(range) {
        this._iModel.projectExtents = Range3d.fromJSON(range);
    }
    /** @internal */
    notifyGlobalOriginChanged(origin) {
        this._iModel.globalOrigin = Point3d.fromJSON(origin);
    }
    /** @internal */
    notifyEcefLocationChanged(ecef) {
        this._iModel.ecefLocation = ecef ? new EcefLocation(ecef) : undefined;
    }
    /** @internal */
    notifyGeographicCoordinateSystemChanged(gcs) {
        this._iModel.geographicCoordinateSystem = gcs ? new GeographicCRS(gcs) : undefined;
    }
}
//# sourceMappingURL=BriefcaseTxns.js.map