/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { bufferToString, IsoBuffer } from "@fluidframework/common-utils";
import { SummaryTreeBuilder } from "@fluidframework/runtime-utils";
import { initializeForest, cachedValue, recordDependency, mapCursorField, moveToDetachedField, } from "../core";
import { jsonableTreeFromCursor, singleTextCursor } from "./treeTextCursor";
/**
 * The storage key for the blob in the summary containing tree data
 */
const treeBlobKey = "ForestTree";
/**
 * Provides methods for summarizing and loading a forest.
 */
export class ForestSummarizer {
    constructor(runtime, forest) {
        this.runtime = runtime;
        this.forest = forest;
        this.key = "Forest";
        this.cursor = this.forest.allocateCursor();
        this.treeBlob = cachedValue(async (observer) => {
            // TODO: could optimize to depend on tree only, not also schema.
            recordDependency(observer, this.forest);
            const treeText = this.getTreeString();
            // For now we are not chunking the data, and instead put it in a single blob:
            // TODO: use lower level API to avoid blob manager?
            return this.runtime.uploadBlob(IsoBuffer.from(treeText));
        });
    }
    /**
     * Synchronous monolithic summarization of tree content.
     *
     * TODO: when perf matters, this should be replaced with a chunked async version using a binary format.
     *
     * @returns a snapshot of the forest's tree as a string.
     */
    getTreeString() {
        // TODO: maybe assert there are no other roots
        // (since we don't save them, and they should not exist outside transactions).
        moveToDetachedField(this.forest, this.cursor);
        const roots = mapCursorField(this.cursor, jsonableTreeFromCursor);
        this.cursor.clear();
        return JSON.stringify(roots);
    }
    getAttachSummary(stringify, fullTree, trackState, telemetryContext) {
        return this.summarizeCore(stringify, this.getTreeString());
    }
    async summarize(stringify, fullTree, trackState, telemetryContext) {
        const treeBlobHandle = await this.treeBlob.get();
        return this.summarizeCore(stringify, treeBlobHandle);
    }
    summarizeCore(stringify, tree) {
        const builder = new SummaryTreeBuilder();
        const serializedTreeBlobHandle = stringify(tree);
        builder.addBlob(treeBlobKey, serializedTreeBlobHandle);
        return builder.getSummaryTree();
    }
    getGCData(fullGC) {
        // TODO: Properly implement garbage collection. Right now, garbage collection is performed automatically
        // by the code in SharedObject (from which SharedTreeCore extends). The `runtime.uploadBlob` API delegates
        // to the `BlobManager`, which automatically populates the summary with ISummaryAttachment entries for each
        // blob.
        return {
            gcNodes: {},
        };
    }
    async load(services, parse) {
        if (await services.contains(treeBlobKey)) {
            const treeBuffer = await services.readBlob(treeBlobKey);
            const treeBufferString = bufferToString(treeBuffer, "utf8");
            const tree = parse(treeBufferString);
            const jsonableTree = JSON.parse(tree);
            initializeForest(this.forest, jsonableTree.map(singleTextCursor));
        }
    }
}
//# sourceMappingURL=forestSummarizer.js.map