import { findLastIdxMonotonous, findLastMonotonous, findFirstMonotonous } from '../../../../base/common/arraysFind.js';
import { CharCode } from '../../../../base/common/charCode.js';
import { OffsetRange } from '../../core/offsetRange.js';
import { Position } from '../../core/position.js';
import { Range } from '../../core/range.js';
import { isSpace } from './utils.js';
class LinesSliceCharSequence {
    constructor(lines, lineRange, considerWhitespaceChanges) {
        this.lines = lines;
        this.considerWhitespaceChanges = considerWhitespaceChanges;
        this.elements = [];
        this.firstCharOffsetByLine = [];
        this.additionalOffsetByLine = [];
        let trimFirstLineFully = false;
        if (lineRange.start > 0 && lineRange.endExclusive >= lines.length) {
            lineRange = ( new OffsetRange(lineRange.start - 1, lineRange.endExclusive));
            trimFirstLineFully = true;
        }
        this.lineRange = lineRange;
        this.firstCharOffsetByLine[0] = 0;
        for (let i = this.lineRange.start; i < this.lineRange.endExclusive; i++) {
            let line = lines[i];
            let offset = 0;
            if (trimFirstLineFully) {
                offset = line.length;
                line = '';
                trimFirstLineFully = false;
            }
            else if (!considerWhitespaceChanges) {
                const trimmedStartLine = line.trimStart();
                offset = line.length - trimmedStartLine.length;
                line = trimmedStartLine.trimEnd();
            }
            this.additionalOffsetByLine.push(offset);
            for (let i = 0; i < line.length; i++) {
                this.elements.push(line.charCodeAt(i));
            }
            if (i < lines.length - 1) {
                this.elements.push('\n'.charCodeAt(0));
                this.firstCharOffsetByLine[i - this.lineRange.start + 1] = this.elements.length;
            }
        }
        this.additionalOffsetByLine.push(0);
    }
    toString() {
        return `Slice: "${this.text}"`;
    }
    get text() {
        return this.getText(( new OffsetRange(0, this.length)));
    }
    getText(range) {
        return ( this.elements.slice(range.start, range.endExclusive).map(e => String.fromCharCode(e))).join('');
    }
    getElement(offset) {
        return this.elements[offset];
    }
    get length() {
        return this.elements.length;
    }
    getBoundaryScore(length) {
        const prevCategory = getCategory(length > 0 ? this.elements[length - 1] : -1);
        const nextCategory = getCategory(length < this.elements.length ? this.elements[length] : -1);
        if (prevCategory === CharBoundaryCategory.LineBreakCR && nextCategory === CharBoundaryCategory.LineBreakLF) {
            return 0;
        }
        if (prevCategory === CharBoundaryCategory.LineBreakLF) {
            return 150;
        }
        let score = 0;
        if (prevCategory !== nextCategory) {
            score += 10;
            if (prevCategory === CharBoundaryCategory.WordLower && nextCategory === CharBoundaryCategory.WordUpper) {
                score += 1;
            }
        }
        score += getCategoryBoundaryScore(prevCategory);
        score += getCategoryBoundaryScore(nextCategory);
        return score;
    }
    translateOffset(offset) {
        if (this.lineRange.isEmpty) {
            return ( new Position(this.lineRange.start + 1, 1));
        }
        const i = findLastIdxMonotonous(this.firstCharOffsetByLine, (value) => value <= offset);
        return ( new Position(
            this.lineRange.start + i + 1,
            offset - this.firstCharOffsetByLine[i] + this.additionalOffsetByLine[i] + 1
        ));
    }
    translateRange(range) {
        return Range.fromPositions(this.translateOffset(range.start), this.translateOffset(range.endExclusive));
    }
    findWordContaining(offset) {
        if (offset < 0 || offset >= this.elements.length) {
            return undefined;
        }
        if (!isWordChar(this.elements[offset])) {
            return undefined;
        }
        let start = offset;
        while (start > 0 && isWordChar(this.elements[start - 1])) {
            start--;
        }
        let end = offset;
        while (end < this.elements.length && isWordChar(this.elements[end])) {
            end++;
        }
        return ( new OffsetRange(start, end));
    }
    countLinesIn(range) {
        return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber;
    }
    isStronglyEqual(offset1, offset2) {
        return this.elements[offset1] === this.elements[offset2];
    }
    extendToFullLines(range) {
        const start = findLastMonotonous(this.firstCharOffsetByLine, x => x <= range.start) ?? 0;
        const end = findFirstMonotonous(this.firstCharOffsetByLine, x => range.endExclusive <= x) ?? this.elements.length;
        return ( new OffsetRange(start, end));
    }
}
function isWordChar(charCode) {
    return charCode >= CharCode.a && charCode <= CharCode.z
        || charCode >= CharCode.A && charCode <= CharCode.Z
        || charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9;
}
var CharBoundaryCategory;
( (function(CharBoundaryCategory) {
    CharBoundaryCategory[CharBoundaryCategory["WordLower"] = 0] = "WordLower";
    CharBoundaryCategory[CharBoundaryCategory["WordUpper"] = 1] = "WordUpper";
    CharBoundaryCategory[CharBoundaryCategory["WordNumber"] = 2] = "WordNumber";
    CharBoundaryCategory[CharBoundaryCategory["End"] = 3] = "End";
    CharBoundaryCategory[CharBoundaryCategory["Other"] = 4] = "Other";
    CharBoundaryCategory[CharBoundaryCategory["Separator"] = 5] = "Separator";
    CharBoundaryCategory[CharBoundaryCategory["Space"] = 6] = "Space";
    CharBoundaryCategory[CharBoundaryCategory["LineBreakCR"] = 7] = "LineBreakCR";
    CharBoundaryCategory[CharBoundaryCategory["LineBreakLF"] = 8] = "LineBreakLF";
})(CharBoundaryCategory || (CharBoundaryCategory = {})));
const score = {
    [CharBoundaryCategory.WordLower]: 0,
    [CharBoundaryCategory.WordUpper]: 0,
    [CharBoundaryCategory.WordNumber]: 0,
    [CharBoundaryCategory.End]: 10,
    [CharBoundaryCategory.Other]: 2,
    [CharBoundaryCategory.Separator]: 30,
    [CharBoundaryCategory.Space]: 3,
    [CharBoundaryCategory.LineBreakCR]: 10,
    [CharBoundaryCategory.LineBreakLF]: 10,
};
function getCategoryBoundaryScore(category) {
    return score[category];
}
function getCategory(charCode) {
    if (charCode === CharCode.LineFeed) {
        return CharBoundaryCategory.LineBreakLF;
    }
    else if (charCode === CharCode.CarriageReturn) {
        return CharBoundaryCategory.LineBreakCR;
    }
    else if (isSpace(charCode)) {
        return CharBoundaryCategory.Space;
    }
    else if (charCode >= CharCode.a && charCode <= CharCode.z) {
        return CharBoundaryCategory.WordLower;
    }
    else if (charCode >= CharCode.A && charCode <= CharCode.Z) {
        return CharBoundaryCategory.WordUpper;
    }
    else if (charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9) {
        return CharBoundaryCategory.WordNumber;
    }
    else if (charCode === -1) {
        return CharBoundaryCategory.End;
    }
    else if (charCode === CharCode.Comma || charCode === CharCode.Semicolon) {
        return CharBoundaryCategory.Separator;
    }
    else {
        return CharBoundaryCategory.Other;
    }
}
export { LinesSliceCharSequence };
