/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { noopValidator } from "../codec";
import { InMemoryStoredSchemaRepository } from "../core";
import { SharedTreeCore } from "../shared-tree-core";
import { defaultSchemaPolicy, ForestSummarizer, SchemaSummarizer as SchemaSummarizer, DefaultChangeFamily, buildForest, ForestRepairDataStoreProvider, SchemaEditor, NodeKeyIndex, createNodeKeyManager, nodeKeyFieldKey, buildChunkedForest, makeTreeChunker, } from "../feature-libraries";
import { createEmitter } from "../events";
import { brand } from "../util";
import { createSharedTreeView, schematizeView, } from "./sharedTreeView";
/**
 * Shared tree, configured with a good set of indexes and field kinds which will maintain compatibility over time.
 *
 * TODO: detail compatibility requirements.
 */
export class SharedTree extends SharedTreeCore {
    constructor(id, runtime, attributes, optionsParam, telemetryContextPrefix) {
        const options = { ...defaultSharedTreeOptions, ...optionsParam };
        const schema = new InMemoryStoredSchemaRepository();
        const forest = options.forest === ForestType.Optimized
            ? buildChunkedForest(makeTreeChunker(schema, defaultSchemaPolicy))
            : buildForest();
        const schemaSummarizer = new SchemaSummarizer(runtime, schema, options);
        const forestSummarizer = new ForestSummarizer(forest);
        const changeFamily = new DefaultChangeFamily(options);
        const repairProvider = new ForestRepairDataStoreProvider(forest, schema, (change) => changeFamily.intoDelta(change));
        super([schemaSummarizer, forestSummarizer], changeFamily, repairProvider, options, id, runtime, attributes, telemetryContextPrefix);
        this.storedSchema = new SchemaEditor(schema, (op) => this.submitLocalMessage(op), options);
        this.nodeKeyIndex = new NodeKeyIndex(brand(nodeKeyFieldKey));
        this._events = createEmitter();
        this.view = createSharedTreeView({
            branch: this.getLocalBranch(),
            // TODO:
            // This passes in a version of schema thats not wrapped with the editor.
            // This allows editing schema on the view without sending ops, which is incorrect behavior.
            schema,
            forest,
            repairProvider,
            nodeKeyManager: createNodeKeyManager(this.runtime.idCompressor),
            nodeKeyIndex: this.nodeKeyIndex,
            events: this._events,
        });
    }
    schematize(config) {
        // TODO:
        // This should work, but schema editing on views doesn't send ops.
        // this.view.schematize(config);
        // For now, use this as a workaround:
        schematizeView(this.view, config, this.storedSchema);
        return this.view;
    }
    /**
     * TODO: Shared tree needs a pattern for handling non-changeset operations.
     * Whatever pattern is adopted should probably also handle multiple versions of changeset operations.
     * A single top level enum listing all ops (including their different versions),
     * with at least fine grained enough detail to direct them to the correct subsystem would be a good approach.
     * The current use-case (with an op applying to a specific index) is a temporary hack,
     * and its not clear how it would fit into such a system if implemented in shared-tree-core:
     * maybe op dispatch is part of the shared-tree level?
     */
    processCore(message, local, localOpMetadata) {
        if (!this.storedSchema.tryHandleOp(message.contents)) {
            super.processCore(message, local, localOpMetadata);
        }
    }
    reSubmitCore(content, localOpMetadata) {
        if (!this.storedSchema.tryResubmitOp(content)) {
            super.reSubmitCore(content, localOpMetadata);
        }
    }
    applyStashedOp(content) {
        if (!this.storedSchema.tryApplyStashedOp(content)) {
            return super.applyStashedOp(content);
        }
    }
    async loadCore(services) {
        await super.loadCore(services);
        // The identifier index must be populated after both the schema and forest have loaded.
        // TODO: Create an ISummarizer for the identifier index and ensure it loads after the other indexes.
        this.nodeKeyIndex.scanKeys(this.view.context);
        this._events.emit("afterBatch");
    }
}
/**
 * Used to distinguish between different forest types.
 * @public
 */
export var ForestType;
(function (ForestType) {
    /**
     * The "ObjectForest" forest type.
     */
    ForestType[ForestType["Reference"] = 0] = "Reference";
    /**
     * The "ChunkedForest" forest type.
     */
    ForestType[ForestType["Optimized"] = 1] = "Optimized";
})(ForestType || (ForestType = {}));
export const defaultSharedTreeOptions = {
    jsonValidator: noopValidator,
    forest: ForestType.Reference,
};
/**
 * A channel factory that creates {@link ISharedTree}s.
 * @public
 */
export class SharedTreeFactory {
    constructor(options = {}) {
        this.options = options;
        this.type = "SharedTree";
        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;
    }
}
//# sourceMappingURL=sharedTree.js.map