"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SharedTreeFactory = exports.defaultSharedTreeOptions = exports.ForestType = exports.SharedTreeFormatVersion = exports.SharedTree = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/telemetry-utils/internal");
const index_js_1 = require("../codec/index.js");
const index_js_2 = require("../core/index.js");
const index_js_3 = require("../events/index.js");
const index_js_4 = require("../feature-libraries/index.js");
const index_js_5 = require("../shared-tree-core/index.js");
const schematizeTree_js_1 = require("./schematizeTree.js");
const schematizingTreeView_js_1 = require("./schematizingTreeView.js");
const sharedTreeChangeEnricher_js_1 = require("./sharedTreeChangeEnricher.js");
const sharedTreeChangeFamily_js_1 = require("./sharedTreeChangeFamily.js");
const treeCheckout_js_1 = require("./treeCheckout.js");
const formatVersionToTopLevelCodecVersions = new Map([
    [
        1,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 1, message: 1, fieldBatch: 1 },
    ],
    [
        2,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 2, message: 2, fieldBatch: 1 },
    ],
    [
        3,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 3, message: 3, fieldBatch: 1 },
    ],
]);
function getCodecVersions(formatVersion) {
    const versions = formatVersionToTopLevelCodecVersions.get(formatVersion);
    (0, internal_1.assert)(versions !== undefined, 0x90e /* Unknown format version */);
    return versions;
}
/**
 * Shared tree, configured with a good set of indexes and field kinds which will maintain compatibility over time.
 *
 * TODO: detail compatibility requirements.
 */
class SharedTree extends index_js_5.SharedTreeCore {
    get storedSchema() {
        return this.checkout.storedSchema;
    }
    constructor(id, runtime, attributes, optionsParam, telemetryContextPrefix = "fluid_sharedTree_") {
        if (runtime.idCompressor === undefined) {
            throw new internal_2.UsageError("IdCompressor must be enabled to use SharedTree");
        }
        const options = { ...exports.defaultSharedTreeOptions, ...optionsParam };
        const codecVersions = getCodecVersions(options.formatVersion);
        const schema = new index_js_2.TreeStoredSchemaRepository();
        const forest = options.forest === ForestType.Optimized
            ? (0, index_js_4.buildChunkedForest)((0, index_js_4.makeTreeChunker)(schema, index_js_4.defaultSchemaPolicy))
            : (0, index_js_4.buildForest)();
        const revisionTagCodec = new index_js_2.RevisionTagCodec(runtime.idCompressor);
        const removedRoots = (0, index_js_2.makeDetachedFieldIndex)("repair", revisionTagCodec, runtime.idCompressor, options);
        const schemaSummarizer = new index_js_4.SchemaSummarizer(runtime, schema, options, {
            getCurrentSeq: () => this.deltaManager.lastSequenceNumber,
        });
        const fieldBatchCodec = (0, index_js_4.makeFieldBatchCodec)(options, codecVersions.fieldBatch);
        const encoderContext = {
            schema: {
                schema,
                policy: index_js_4.defaultSchemaPolicy,
            },
            encodeType: options.treeEncodeType,
            originatorId: runtime.idCompressor.localSessionId,
            idCompressor: runtime.idCompressor,
        };
        const forestSummarizer = new index_js_4.ForestSummarizer(forest, revisionTagCodec, fieldBatchCodec, encoderContext, options, runtime.idCompressor);
        const removedRootsSummarizer = new index_js_4.DetachedFieldIndexSummarizer(removedRoots);
        const innerChangeFamily = new sharedTreeChangeFamily_js_1.SharedTreeChangeFamily(revisionTagCodec, fieldBatchCodec, options, options.treeEncodeType);
        const changeFamily = (0, index_js_4.makeMitigatedChangeFamily)(innerChangeFamily, sharedTreeChangeFamily_js_1.SharedTreeChangeFamily.emptyChange, (error) => {
            // TODO:6344 Add telemetry for these errors.
            // Rethrowing the error has a different effect depending on the context in which the
            // ChangeFamily was invoked:
            // - If the ChangeFamily was invoked as part of incoming op processing, rethrowing the error
            // will cause the runtime to disconnect the client, log a severe error, and not reconnect.
            // This will not cause the host application to crash because it is not on the stack at that time.
            // TODO: let the host application know that the client is now disconnected.
            // - If the ChangeFamily was invoked as part of dealing with a local change, rethrowing the
            // error will cause the host application to crash. This is not ideal, but is better than
            // letting the application either send an invalid change to the server or allowing the
            // application to continue working when its local branches contain edits that cannot be
            // reflected in its views.
            // The best course of action for a host application in such a state is to restart.
            // TODO: let the host application know about this situation and provide a way to
            // programmatically reload the SharedTree container.
            throw error;
        });
        const changeEnricher = new sharedTreeChangeEnricher_js_1.SharedTreeReadonlyChangeEnricher(forest, schema, removedRoots);
        super([schemaSummarizer, forestSummarizer, removedRootsSummarizer], changeFamily, options, codecVersions, id, runtime, attributes, telemetryContextPrefix, schema, index_js_4.defaultSchemaPolicy, new index_js_5.DefaultResubmitMachine(changeFamily.rebaser.invert.bind(changeFamily.rebaser), changeEnricher), changeEnricher);
        this._events = (0, index_js_3.createEmitter)();
        const localBranch = this.getLocalBranch();
        this.checkout = (0, treeCheckout_js_1.createTreeCheckout)(runtime.idCompressor, this.mintRevisionTag, revisionTagCodec, {
            branch: localBranch,
            changeFamily,
            schema,
            forest,
            fieldBatchCodec,
            events: this._events,
            removedRoots,
            chunkCompressionStrategy: options.treeEncodeType,
            logger: this.logger,
        });
    }
    contentSnapshot() {
        const cursor = this.checkout.forest.allocateCursor("contentSnapshot");
        try {
            (0, index_js_2.moveToDetachedField)(this.checkout.forest, cursor);
            return {
                schema: this.storedSchema.clone(),
                tree: (0, index_js_4.jsonableTreeFromFieldCursor)(cursor),
                removed: this.checkout.getRemovedRoots(),
            };
        }
        finally {
            cursor.free();
        }
    }
    schematizeFlexTree(config, onDispose) {
        const viewSchema = new index_js_4.ViewSchema(index_js_4.defaultSchemaPolicy, {}, config.schema);
        if (!(0, schematizeTree_js_1.ensureSchema)(viewSchema, config.allowedSchemaModifications, this.checkout, config)) {
            return undefined;
        }
        return (0, schematizingTreeView_js_1.requireSchema)(this.checkout, viewSchema, onDispose, (0, index_js_4.createNodeKeyManager)(this.runtime.idCompressor));
    }
    schematize(
    // eslint-disable-next-line import/no-deprecated
    config) {
        const view = new schematizingTreeView_js_1.SchematizingSimpleTreeView(this.checkout, config, (0, index_js_4.createNodeKeyManager)(this.runtime.idCompressor));
        // As a subjective API design choice, we initialize the tree here if it is not already initialized.
        if (view.compatibility.canInitialize === true) {
            view.initialize(config.initialTree());
        }
        return view;
    }
    viewWith(config) {
        return new schematizingTreeView_js_1.SchematizingSimpleTreeView(this.checkout, config, (0, index_js_4.createNodeKeyManager)(this.runtime.idCompressor));
    }
    async loadCore(services) {
        await super.loadCore(services);
        this._events.emit("afterBatch");
    }
}
exports.SharedTree = SharedTree;
/**
 * Format versions supported by SharedTree.
 *
 * Each version documents a required minimum version of the \@fluidframework/tree package.
 * @internal
 */
exports.SharedTreeFormatVersion = {
    /**
     * Requires \@fluidframework/tree \>= 2.0.0.
     *
     * @deprecated - FF does not currently plan on supporting this format long-term.
     * Do not write production documents using this format, as they may not be loadable in the future.
     */
    v1: 1,
    /**
     * Requires \@fluidframework/tree \>= 2.0.0.
     */
    v2: 2,
    /**
     * Requires \@fluidframework/tree \>= 2.0.0.
     */
    v3: 3,
};
/**
 * Used to distinguish between different forest types.
 * @internal
 */
var ForestType;
(function (ForestType) {
    /**
     * The "ObjectForest" forest type.
     */
    ForestType[ForestType["Reference"] = 0] = "Reference";
    /**
     * The "ChunkedForest" forest type.
     */
    ForestType[ForestType["Optimized"] = 1] = "Optimized";
})(ForestType || (exports.ForestType = ForestType = {}));
exports.defaultSharedTreeOptions = {
    jsonValidator: index_js_1.noopValidator,
    forest: ForestType.Reference,
    treeEncodeType: index_js_4.TreeCompressionStrategy.Compressed,
    formatVersion: exports.SharedTreeFormatVersion.v3,
};
/**
 * A channel factory that creates {@link ISharedTree}s.
 */
class SharedTreeFactory {
    constructor(options = {}) {
        this.options = options;
        this.type = "https://graph.microsoft.com/types/tree";
        this.attributes = {
            type: this.type,
            snapshotFormatVersion: "0.0.0",
            packageVersion: "0.0.0",
        };
    }
    async load(runtime, id, services, channelAttributes) {
        const tree = new SharedTree(id, runtime, channelAttributes, this.options);
        await tree.load(services);
        return tree;
    }
    create(runtime, id) {
        const tree = new SharedTree(id, runtime, this.attributes, this.options);
        tree.initializeLocal();
        return tree;
    }
}
exports.SharedTreeFactory = SharedTreeFactory;
//# sourceMappingURL=sharedTree.js.map