/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { BaseSegment } from "./mergeTreeNodes";
import { LocalReferenceCollection } from "./localReference";
// Maximum length of text segment to be considered to be merged with other segment.
// Maximum segment length is at least 2x of it (not taking into account initial segment creation).
// The bigger it is, the more expensive it is to break segment into sub-segments (on edits)
// The smaller it is, the more segments we have in snapshots (and in memory) - it's more expensive to load snapshots.
// Small number also makes ReplayTool produce false positives ("same" snapshots have slightly different binary
// representations).  More measurements needs to be done, but it's very likely the right spot is somewhere between
// 1K-2K mark.  That said, we also break segments on newline and there are very few segments that are longer than 256
// because of it.  Must be an even number
const TextSegmentGranularity = 256;
export class TextSegment extends BaseSegment {
    constructor(text) {
        super();
        this.text = text;
        this.type = TextSegment.type;
        this.cachedLength = text.length;
    }
    static is(segment) {
        return segment.type === TextSegment.type;
    }
    static make(text, props) {
        const seg = new TextSegment(text);
        if (props) {
            seg.addProperties(props);
        }
        return seg;
    }
    static fromJSONObject(spec) {
        if (typeof spec === "string") {
            return new TextSegment(spec);
        }
        else if (spec && typeof spec === "object" && "text" in spec) {
            const textSpec = spec;
            return TextSegment.make(textSpec.text, textSpec.props);
        }
        return undefined;
    }
    toJSONObject() {
        // To reduce snapshot/ops size, we serialize a TextSegment as a plain 'string' if it is
        // not annotated.
        return this.properties
            ? { text: this.text, props: this.properties }
            : this.text;
    }
    clone(start = 0, end) {
        const text = this.text.substring(start, end);
        const b = TextSegment.make(text, this.properties);
        this.cloneInto(b);
        return b;
    }
    canAppend(segment) {
        return !this.text.endsWith("\n")
            && TextSegment.is(segment)
            && (this.cachedLength <= TextSegmentGranularity ||
                segment.cachedLength <= TextSegmentGranularity);
    }
    toString() {
        return this.text;
    }
    append(segment) {
        if (TextSegment.is(segment)) {
            // Note: Must call 'appendLocalRefs' before modifying this segment's length as
            // 'this.cachedLength' is used to adjust the offsets of the local refs.
            LocalReferenceCollection.append(this, segment);
            this.text += segment.text;
            this.cachedLength = this.text.length;
        }
        else {
            throw new Error("can only append text segment");
        }
    }
    // TODO: retain removed text for undo
    // returns true if entire string removed
    removeRange(start, end) {
        let remnantString = "";
        const len = this.text.length;
        if (start > 0) {
            remnantString += this.text.substring(0, start);
        }
        if (end < len) {
            remnantString += this.text.substring(end);
        }
        this.text = remnantString;
        this.cachedLength = remnantString.length;
        return (remnantString.length === 0);
    }
    createSplitSegmentAt(pos) {
        if (pos > 0) {
            const remainingText = this.text.substring(pos);
            this.text = this.text.substring(0, pos);
            this.cachedLength = this.text.length;
            const leafSegment = new TextSegment(remainingText);
            return leafSegment;
        }
    }
}
TextSegment.type = "TextSegment";
//# sourceMappingURL=textSegment.js.map