// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Platform from '../../core/platform/platform.js';

import {TextCursor} from './TextCursor.js';
import {SourceRange, TextRange} from './TextRange.js';

export class Text {
  readonly #value: string;
  #lineEndings?: number[];

  constructor(value: string) {
    this.#value = value;
  }

  lineEndings(): number[] {
    if (!this.#lineEndings) {
      this.#lineEndings = Platform.StringUtilities.findLineEndingIndexes(this.#value);
    }
    return this.#lineEndings;
  }

  value(): string {
    return this.#value;
  }

  lineCount(): number {
    const lineEndings = this.lineEndings();
    return lineEndings.length;
  }

  offsetFromPosition(lineNumber: number, columnNumber: number): number {
    return (lineNumber ? this.lineEndings()[lineNumber - 1] + 1 : 0) + columnNumber;
  }

  positionFromOffset(offset: number): Position {
    const lineEndings = this.lineEndings();
    const lineNumber =
        Platform.ArrayUtilities.lowerBound(lineEndings, offset, Platform.ArrayUtilities.DEFAULT_COMPARATOR);
    return {lineNumber, columnNumber: offset - (lineNumber && (lineEndings[lineNumber - 1] + 1))};
  }

  lineAt(lineNumber: number): string {
    const lineEndings = this.lineEndings();
    const lineStart = lineNumber > 0 ? lineEndings[lineNumber - 1] + 1 : 0;
    const lineEnd = lineEndings[lineNumber];
    let lineContent = this.#value.substring(lineStart, lineEnd);
    if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === '\r') {
      lineContent = lineContent.substring(0, lineContent.length - 1);
    }
    return lineContent;
  }

  toSourceRange(range: TextRange): SourceRange {
    const start = this.offsetFromPosition(range.startLine, range.startColumn);
    const end = this.offsetFromPosition(range.endLine, range.endColumn);
    return new SourceRange(start, end - start);
  }

  toTextRange(sourceRange: SourceRange): TextRange {
    const cursor = new TextCursor(this.lineEndings());
    const result = TextRange.createFromLocation(0, 0);

    cursor.resetTo(sourceRange.offset);
    result.startLine = cursor.lineNumber();
    result.startColumn = cursor.columnNumber();

    cursor.advance(sourceRange.offset + sourceRange.length);
    result.endLine = cursor.lineNumber();
    result.endColumn = cursor.columnNumber();
    return result;
  }

  replaceRange(range: TextRange, replacement: string): string {
    const sourceRange = this.toSourceRange(range);
    return this.#value.substring(0, sourceRange.offset) + replacement +
        this.#value.substring(sourceRange.offset + sourceRange.length);
  }

  extract(range: TextRange): string {
    const sourceRange = this.toSourceRange(range);
    return this.#value.substr(sourceRange.offset, sourceRange.length);
  }
}
export interface Position {
  lineNumber: number;
  columnNumber: number;
}
