/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { unreachableCase } from "@fluidframework/core-utils/internal";
import { tagChange, visitDelta, } from "../core/index.js";
import { chunkTree, defaultChunkPolicy, intoDelta, relevantRemovedRoots, updateRefreshers as updateDataChangeRefreshers, } from "../feature-libraries/index.js";
import { disposeSymbol } from "../util/index.js";
import { updateRefreshers } from "./sharedTreeChangeFamily.js";
export class SharedTreeChangeEnricher {
    /**
     * @param borrowedForest - The state based on which to enrich changes.
     * Not owned by the constructed instance.
     * @param schema - The schema that corresponds to the forest.
     * @param borrowedRemovedRoots - The set of removed roots based on which to enrich changes.
     * Not owned by the constructed instance.
     * @param idCompressor - The id compressor to use when chunking trees.
     * @param onEnrichCommit - Optional callback invoked whenever a commit is enriched.
     * @param onRefresherAdded - Optional callback invoked whenever a refresher is added during enrichment.
     * @param onForkState - Optional callback invoked whenever the enricher forks its state.
     * @param onApplyChange - Optional callback invoked whenever a change is applied to the owned state.
     */
    constructor(borrowedForest, borrowedRemovedRoots, schema, idCompressor, onEnrichCommit, onRefresherAdded, onForkState, onApplyChange) {
        this.schema = schema;
        this.idCompressor = idCompressor;
        this.onEnrichCommit = onEnrichCommit;
        this.onRefresherAdded = onRefresherAdded;
        this.onForkState = onForkState;
        this.onApplyChange = onApplyChange;
        /**
         * Queue of changes to be applied before querying for detached roots.
         */
        this.changeQueue = [];
        this.borrowed = {
            forest: borrowedForest,
            removedRoots: borrowedRemovedRoots,
        };
    }
    enrich(change) {
        this.onEnrichCommit?.();
        return updateRefreshers(change, (id) => this.getDetachedRoot(id), relevantRemovedRoots, updateDataChangeRefreshers);
    }
    getDetachedRoot(id) {
        this.purgeChangeQueue();
        const state = this.owned ?? this.borrowed;
        const root = state.removedRoots.tryGetEntry(id);
        if (root !== undefined) {
            const cursor = state.forest.getCursorAboveDetachedFields();
            const parentField = state.removedRoots.toFieldKey(root);
            cursor.enterField(parentField);
            cursor.enterNode(0);
            this.onRefresherAdded?.();
            return chunkTree(cursor, {
                policy: defaultChunkPolicy,
                idCompressor: this.idCompressor,
            });
        }
        return undefined;
    }
    enqueueChange(change) {
        this.changeQueue.push(typeof change === "function" ? change : () => change);
    }
    purgeChangeQueue() {
        if (this.changeQueue.length === 0) {
            return;
        }
        if (this.owned === undefined) {
            this.onForkState?.();
            this.owned = {
                forest: this.borrowed.forest.clone(this.schema),
                removedRoots: this.borrowed.removedRoots.clone(),
            };
        }
        for (const getChange of this.changeQueue) {
            const { change, revision } = getChange();
            this.onApplyChange?.();
            for (const dataOrSchemaChange of change.changes) {
                const type = dataOrSchemaChange.type;
                switch (type) {
                    case "data": {
                        const delta = intoDelta(tagChange(dataOrSchemaChange.innerChange, revision));
                        const visitor = this.owned.forest.acquireVisitor();
                        visitDelta(delta, visitor, this.owned.removedRoots, revision);
                        visitor.free();
                        break;
                    }
                    case "schema": {
                        // This enricher doesn't need to maintain schema information.
                        // Note that the refreshers being generated through `updateChangeEnrichments` will be encoded using
                        // the schema that was used in the input context of the data changeset these refreshers are on.
                        // See the encoding logic in SharedTreeCore for details.
                        break;
                    }
                    default: {
                        unreachableCase(type);
                    }
                }
            }
        }
        this.changeQueue.length = 0;
    }
    [disposeSymbol]() {
        // TODO: in the future, forest and/or its AnchorSet may require disposal.
    }
}
//# sourceMappingURL=sharedTreeChangeEnricher.js.map