/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { InMemoryStoredSchemaRepository, AnchorSet, symbolFromKey, } from "../core";
import { SharedTreeCore } from "../shared-tree-core";
import { defaultSchemaPolicy, ForestSummarizer, SchemaSummarizer as SchemaSummarizer, defaultChangeFamily, buildForest, ForestRepairDataStoreProvider, SchemaEditor, IdentifierIndex, } from "../feature-libraries";
import { createEmitter } from "../events";
import { brand } from "../util";
import { createSharedTreeView, } from "./sharedTreeView";
/**
 * The key for the special identifier field, which allows nodes to be given identifiers that can be used
 * to find the nodes via the identifier index
 * @alpha
 */
export const identifierKey = brand("identifier");
/**
 * The global field key symbol that corresponds to {@link identifierKey}
 * @alpha
 */
export const identifierKeySymbol = symbolFromKey(identifierKey);
/**
 * Shared tree, configured with a good set of indexes and field kinds which will maintain compatibility over time.
 * TODO: node identifier index.
 *
 * TODO: detail compatibility requirements.
 */
export class SharedTree extends SharedTreeCore {
    constructor(id, runtime, attributes, telemetryContextPrefix) {
        const schema = new InMemoryStoredSchemaRepository(defaultSchemaPolicy);
        const forest = buildForest(schema, new AnchorSet());
        const schemaSummarizer = new SchemaSummarizer(runtime, schema);
        const forestSummarizer = new ForestSummarizer(runtime, forest);
        const repairProvider = new ForestRepairDataStoreProvider(forest, schema);
        super([schemaSummarizer, forestSummarizer], defaultChangeFamily, forest.anchors, repairProvider, id, runtime, attributes, telemetryContextPrefix);
        this.schema = new SchemaEditor(schema, (op) => this.submitLocalMessage(op));
        this.identifierIndex = new IdentifierIndex(identifierKey);
        this.view = createSharedTreeView({
            branch: this.getLocalBranch(),
            schema,
            forest,
            repairProvider,
            identifierIndex: this.identifierIndex,
        });
        this.events = createEmitter();
        this.getLocalBranch().on("change", () => this.finishBatch());
    }
    get rootEvents() {
        return this.view.rootEvents;
    }
    get storedSchema() {
        return this.schema;
    }
    get forest() {
        return this.view.forest;
    }
    get identifiedNodes() {
        return this.view.identifiedNodes;
    }
    get root() {
        return this.view.root;
    }
    set root(data) {
        this.view.root = data;
    }
    get context() {
        return this.view.context;
    }
    locate(anchor) {
        return this.view.locate(anchor);
    }
    schematize(config) {
        return this.view.schematize(config);
    }
    get transaction() {
        return this.view.transaction;
    }
    fork() {
        return this.view.fork();
    }
    merge(fork) {
        this.view.merge(fork);
    }
    rebase(fork) {
        fork.rebaseOnto(this.view);
    }
    getLocalBranch() {
        return super.getLocalBranch();
    }
    /**
     * 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.schema.tryHandleOp(message)) {
            super.processCore(message, local, localOpMetadata);
        }
    }
    reSubmitCore(content, localOpMetadata) {
        if (!this.schema.tryResubmitOp(content)) {
            super.reSubmitCore(content, localOpMetadata);
        }
    }
    async loadCore(services) {
        await super.loadCore(services);
        this.finishBatch();
    }
    /** Finish a batch (see {@link ViewEvents}) */
    finishBatch() {
        this.identifierIndex.scanIdentifiers(this.context);
        this.events.emit("afterBatch");
    }
}
/**
 * A channel factory that creates {@link ISharedTree}s.
 * @alpha
 */
export class SharedTreeFactory {
    constructor() {
        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, "SharedTree");
        await tree.load(services);
        return tree;
    }
    create(runtime, id) {
        const tree = new SharedTree(id, runtime, this.attributes, "SharedTree");
        tree.initializeLocal();
        return tree;
    }
}
//# sourceMappingURL=sharedTree.js.map