/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert } from "@fluidframework/common-utils";
import { ChildLogger } from "@fluidframework/telemetry-utils";
import { SummaryTreeBuilder } from "@fluidframework/runtime-utils";
import { NonCollabClient, UnassignedSequenceNumber } from "./constants";
import { matchProperties } from "./properties";
import { serializeAsMinSupportedVersion, } from "./snapshotChunks";
/**
 * @deprecated  for internal use only. public export will be removed.
 * @internal
 */
export class SnapshotLegacy {
    constructor(mergeTree, logger, filename, onCompletion) {
        var _a, _b;
        this.mergeTree = mergeTree;
        this.filename = filename;
        this.onCompletion = onCompletion;
        this.logger = ChildLogger.create(logger, "Snapshot");
        this.chunkSize = (_b = (_a = mergeTree === null || mergeTree === void 0 ? void 0 : mergeTree.options) === null || _a === void 0 ? void 0 : _a.mergeTreeSnapshotChunkSize) !== null && _b !== void 0 ? _b : SnapshotLegacy.sizeOfFirstChunk;
    }
    getSeqLengthSegs(allSegments, allLengths, approxSequenceLength, startIndex = 0) {
        const segs = [];
        let sequenceLength = 0;
        let segCount = 0;
        while ((sequenceLength < approxSequenceLength) && ((startIndex + segCount) < allSegments.length)) {
            const pseg = allSegments[startIndex + segCount];
            segs.push(pseg);
            sequenceLength += allLengths[startIndex + segCount];
            segCount++;
        }
        return {
            version: undefined,
            chunkStartSegmentIndex: startIndex,
            chunkSegmentCount: segCount,
            chunkLengthChars: sequenceLength,
            totalLengthChars: this.header.segmentsTotalLength,
            totalSegmentCount: allSegments.length,
            chunkSequenceNumber: this.header.seq,
            segmentTexts: segs,
        };
    }
    /**
     * Emits the snapshot to an ISummarizeResult. If provided the optional IFluidSerializer will be used when
     * serializing the summary data rather than JSON.stringify.
     */
    emit(catchUpMsgs, serializer, bind) {
        var _a, _b;
        const chunk1 = this.getSeqLengthSegs(this.segments, this.segmentLengths, this.chunkSize);
        let length = chunk1.chunkLengthChars;
        let segments = chunk1.chunkSegmentCount;
        const builder = new SummaryTreeBuilder();
        builder.addBlob(SnapshotLegacy.header, serializeAsMinSupportedVersion(SnapshotLegacy.header, chunk1, this.logger, this.mergeTree.options, serializer, bind));
        if (chunk1.chunkSegmentCount < chunk1.totalSegmentCount) {
            const chunk2 = this.getSeqLengthSegs(this.segments, this.segmentLengths, this.header.segmentsTotalLength, chunk1.chunkSegmentCount);
            length += chunk2.chunkLengthChars;
            segments += chunk2.chunkSegmentCount;
            builder.addBlob(SnapshotLegacy.body, serializeAsMinSupportedVersion(SnapshotLegacy.body, chunk2, this.logger, this.mergeTree.options, serializer, bind));
        }
        assert(length === this.header.segmentsTotalLength, 0x05d /* "emit: mismatch in segmentsTotalLength" */);
        assert(segments === chunk1.totalSegmentCount, 0x05e /* "emit: mismatch in totalSegmentCount" */);
        if (catchUpMsgs !== undefined && catchUpMsgs.length > 0) {
            builder.addBlob((_b = (_a = this.mergeTree.options) === null || _a === void 0 ? void 0 : _a.catchUpBlobName) !== null && _b !== void 0 ? _b : SnapshotLegacy.catchupOps, serializer ? serializer.stringify(catchUpMsgs, bind) : JSON.stringify(catchUpMsgs));
        }
        return builder.getSummaryTree();
    }
    extractSync() {
        const collabWindow = this.mergeTree.getCollabWindow();
        this.seq = collabWindow.minSeq;
        this.header = {
            segmentsTotalLength: this.mergeTree.getLength(this.mergeTree.collabWindow.minSeq, NonCollabClient),
            seq: this.mergeTree.collabWindow.minSeq,
        };
        const segs = [];
        let prev;
        const extractSegment = 
        // eslint-disable-next-line max-len
        (segment, pos, refSeq, clientId, start, end) => {
            if ((segment.seq !== UnassignedSequenceNumber) && (segment.seq <= this.seq) &&
                ((segment.removedSeq === undefined) || (segment.removedSeq === UnassignedSequenceNumber) ||
                    (segment.removedSeq > this.seq))) {
                if ((prev === null || prev === void 0 ? void 0 : prev.canAppend(segment))
                    && matchProperties(prev.properties, segment.properties)) {
                    prev = prev.clone();
                    prev.append(segment.clone());
                }
                else {
                    if (prev) {
                        segs.push(prev);
                    }
                    prev = segment;
                }
            }
            return true;
        };
        this.mergeTree.map({ leaf: extractSegment }, this.seq, NonCollabClient, undefined);
        if (prev) {
            segs.push(prev);
        }
        this.segments = [];
        this.segmentLengths = [];
        let totalLength = 0;
        segs.map((segment) => {
            totalLength += segment.cachedLength;
            this.segments.push(segment.toJSONObject());
            this.segmentLengths.push(segment.cachedLength);
        });
        // We observed this.header.segmentsTotalLength < totalLength to happen in some cases
        // When this condition happens, we might not write out all segments in getSeqLengthSegs()
        // when writing out "body". Issue #1995 tracks following up on the core of the problem.
        // In the meantime, this code makes sure we will write out all segments properly
        if (this.header.segmentsTotalLength !== totalLength) {
            this.logger.sendErrorEvent({
                eventName: "SegmentsTotalLengthMismatch",
                totalLength,
                segmentsTotalLength: this.header.segmentsTotalLength,
            });
            this.header.segmentsTotalLength = totalLength;
        }
        return this.segments;
    }
}
SnapshotLegacy.header = "header";
SnapshotLegacy.body = "body";
SnapshotLegacy.catchupOps = "catchupOps";
// Split snapshot into two entries - headers (small) and body (overflow) for faster loading initial content
// Please note that this number has no direct relationship to anything other than size of raw text (characters).
// As we produce json for the blob (and then send over the wire compressed), this number
// is really hard to correlate with any actual metric that matters (like bytes over the wire).
// For test with small number of chunks it would be closer to blob size,
// for very chunky text, blob size can easily be 4x-8x of that number.
SnapshotLegacy.sizeOfFirstChunk = 10000;
//# sourceMappingURL=snapshotlegacy.js.map