"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.SharedTree = void 0;
const core_utils_1 = require("@fluidframework/core-utils");
const index_js_1 = require("../codec/index.js");
const index_js_2 = require("../core/index.js");
const index_js_3 = require("../shared-tree-core/index.js");
const index_js_4 = require("../feature-libraries/index.js");
const index_js_5 = require("../events/index.js");
const index_js_6 = require("../util/index.js");
const schematizeTree_js_1 = require("./schematizeTree.js");
const treeCheckout_js_1 = require("./treeCheckout.js");
const sharedTreeChangeFamily_js_1 = require("./sharedTreeChangeFamily.js");
const schematizingTreeView_js_1 = require("./schematizingTreeView.js");
/**
 * 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_3.SharedTreeCore {
    get storedSchema() {
        return this.checkout.storedSchema;
    }
    constructor(id, runtime, attributes, optionsParam, telemetryContextPrefix) {
        (0, core_utils_1.assert)(runtime.idCompressor !== undefined, 0x883 /* IdCompressor must be enabled to use SharedTree */);
        const options = { ...exports.defaultSharedTreeOptions, ...optionsParam };
        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, options);
        const schemaSummarizer = new index_js_4.SchemaSummarizer(runtime, schema, options, {
            getCurrentSeq: () => this.runtime.deltaManager.lastSequenceNumber,
        });
        const fieldBatchCodec = (0, index_js_4.makeFieldBatchCodec)(options);
        const encoderContext = {
            schema: {
                schema,
                policy: index_js_4.defaultSchemaPolicy,
            },
            encodeType: options.treeEncodeType,
        };
        const forestSummarizer = new index_js_4.ForestSummarizer(forest, revisionTagCodec, fieldBatchCodec, encoderContext, options);
        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;
        });
        super([schemaSummarizer, forestSummarizer, removedRootsSummarizer], changeFamily, options, id, runtime, attributes, telemetryContextPrefix, { schema, policy: index_js_4.defaultSchemaPolicy });
        this._events = (0, index_js_5.createEmitter)();
        const localBranch = this.getLocalBranch();
        this.checkout = (0, treeCheckout_js_1.createTreeCheckout)(runtime.idCompressor, revisionTagCodec, {
            branch: localBranch,
            changeFamily,
            schema,
            forest,
            fieldBatchCodec,
            events: this._events,
            removedRoots,
            chunkCompressionStrategy: options.treeEncodeType,
        });
    }
    contentSnapshot() {
        const cursor = this.checkout.forest.allocateCursor();
        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), (0, index_js_6.brand)(index_js_4.nodeKeyFieldKey));
    }
    schematize(config) {
        const view = new schematizingTreeView_js_1.SchematizingSimpleTreeView(this.checkout, config, (0, index_js_4.createNodeKeyManager)(this.runtime.idCompressor), (0, index_js_6.brand)(index_js_4.nodeKeyFieldKey));
        // As a subjective API design choice, we initialize the tree here if it is not already initialized.
        if (view.error?.canInitialize === true) {
            view.upgradeSchema();
        }
        return view;
    }
    async loadCore(services) {
        await super.loadCore(services);
        this._events.emit("afterBatch");
    }
}
exports.SharedTree = SharedTree;
/**
 * 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,
};
/**
 * A channel factory that creates {@link ISharedTree}s.
 * @internal
 */
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, "SharedTree");
        await tree.load(services);
        return tree;
    }
    create(runtime, id) {
        const tree = new SharedTree(id, runtime, this.attributes, this.options, "SharedTree");
        tree.initializeLocal();
        return tree;
    }
}
exports.SharedTreeFactory = SharedTreeFactory;
//# sourceMappingURL=sharedTree.js.map