/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { BaseSegment, glc, MergeTree } from "./mergeTree";
import { LocalReferenceCollection } from "./localReference";
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 tseg = new TextSegment(text);
        if (props) {
            tseg.addProperties(props);
        }
        return tseg;
    }
    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 <= MergeTree.TextSegmentGranularity ||
                segment.cachedLength <= MergeTree.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";
function isTextAndMarkerAccumulator(accum) {
    return accum.parallelArrays === true;
}
export class MergeTreeTextHelper {
    constructor(mergeTree) {
        this.mergeTree = mergeTree;
        this.gatherText = (segment, pos, refSeq, clientId, start, end, accumText) => {
            let _start = start;
            if (TextSegment.is(segment)) {
                if (MergeTree.traceGatherText) {
                    console.log(
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                    `@cli ${this.mergeTree.getLongClientId(this.mergeTree.collabWindow.clientId)} ` +
                        `gather seg seq ${segment.seq} rseq ${segment.removedSeq} text ${segment.text}`);
                }
                let beginTags = "";
                let endTags = "";
                if (isTextAndMarkerAccumulator(accumText)) {
                    // TODO: let clients pass in function to get tag
                    const tags = [];
                    const initTags = [];
                    if (segment.properties && (segment.properties["font-weight"])) {
                        tags.push("b");
                    }
                    if (segment.properties && (segment.properties["text-decoration"])) {
                        tags.push("u");
                    }
                    const remTags = [];
                    if (tags.length > 0) {
                        for (const tag of tags) {
                            if (!accumText.tagsInProgress.includes(tag)) {
                                beginTags += `<${tag}>`;
                                initTags.push(tag);
                            }
                        }
                        for (const accumTag of accumText.tagsInProgress) {
                            if (!tags.includes(accumTag)) {
                                endTags += `</${accumTag}>`;
                                remTags.push(accumTag);
                            }
                        }
                        for (const initTag of initTags.reverse()) {
                            accumText.tagsInProgress.push(initTag);
                        }
                    }
                    else {
                        for (const accumTag of accumText.tagsInProgress) {
                            endTags += `</${accumTag}>`;
                            remTags.push(accumTag);
                        }
                    }
                    for (const remTag of remTags) {
                        const remdex = accumText.tagsInProgress.indexOf(remTag);
                        if (remdex >= 0) {
                            accumText.tagsInProgress.splice(remdex, 1);
                        }
                    }
                }
                accumText.textSegment.text += endTags;
                accumText.textSegment.text += beginTags;
                if ((_start <= 0) && (end >= segment.text.length)) {
                    accumText.textSegment.text += segment.text;
                }
                else {
                    if (_start < 0) {
                        _start = 0;
                    }
                    if (end >= segment.text.length) {
                        accumText.textSegment.text += segment.text.substring(_start);
                    }
                    else {
                        accumText.textSegment.text += segment.text.substring(_start, end);
                    }
                }
            }
            else {
                if (accumText.placeholder && (accumText.placeholder.length > 0)) {
                    if (accumText.placeholder === "*") {
                        const marker = segment;
                        accumText.textSegment.text += `\n${marker.toString()}`;
                    }
                    else {
                        for (let i = 0; i < segment.cachedLength; i++) {
                            accumText.textSegment.text += accumText.placeholder;
                        }
                    }
                }
                else if (isTextAndMarkerAccumulator(accumText)) {
                    const marker = segment;
                    if (marker.hasTileLabel(accumText.parallelMarkerLabel)) {
                        accumText.parallelMarkers.push(marker);
                        accumText.parallelText.push(accumText.textSegment.text);
                        accumText.textSegment.text = "";
                    }
                }
            }
            return true;
        };
    }
    getTextAndMarkers(refSeq, clientId, label, start, end) {
        const range = this.getValidRange(start, end, refSeq, clientId);
        const accum = {
            parallelArrays: true,
            parallelMarkerLabel: label,
            parallelMarkers: [],
            parallelText: [],
            tagsInProgress: [],
            textSegment: new TextSegment(""),
        };
        if (MergeTree.traceGatherText) {
            console.log(`get text on cli ${glc(this.mergeTree, this.mergeTree.collabWindow.clientId)} ` +
                `ref cli ${glc(this.mergeTree, clientId)} refSeq ${refSeq}`);
        }
        this.mergeTree.mapRange({ leaf: this.gatherText }, refSeq, clientId, accum, range.start, range.end);
        return { parallelText: accum.parallelText, parallelMarkers: accum.parallelMarkers };
    }
    getText(refSeq, clientId, placeholder = "", start, end) {
        const range = this.getValidRange(start, end, refSeq, clientId);
        const accum = { textSegment: new TextSegment(""), placeholder };
        if (MergeTree.traceGatherText) {
            console.log(`get text on cli ${glc(this.mergeTree, this.mergeTree.collabWindow.clientId)} ` +
                `ref cli ${glc(this.mergeTree, clientId)} refSeq ${refSeq}`);
        }
        this.mergeTree.mapRange({ leaf: this.gatherText }, refSeq, clientId, accum, range.start, range.end);
        return accum.textSegment.text;
    }
    getValidRange(start, end, refSeq, clientId) {
        const range = {
            end: end !== null && end !== void 0 ? end : this.mergeTree.getLength(refSeq, clientId),
            start: start !== null && start !== void 0 ? start : 0,
        };
        return range;
    }
}
//# sourceMappingURL=textSegment.js.map