import { computeIndentLevel } from "../../../common/model/utils.mjs";
import { MAX_LINE_NUMBER, FoldingRegions } from "./foldingRanges.mjs";
const MAX_FOLDING_REGIONS_FOR_INDENT_DEFAULT = 5e3;
const ID_INDENT_PROVIDER = "indent";
class IndentRangeProvider {
  constructor(editorModel, languageConfigurationService, maxFoldingRegions) {
    this.editorModel = editorModel;
    this.languageConfigurationService = languageConfigurationService;
    this.maxFoldingRegions = maxFoldingRegions;
    this.id = ID_INDENT_PROVIDER;
  }
  dispose() {
  }
  compute(cancelationToken, notifyTooManyRegions) {
    let foldingRules = this.languageConfigurationService.getLanguageConfiguration(this.editorModel.getLanguageId()).foldingRules;
    let offSide = foldingRules && !!foldingRules.offSide;
    let markers = foldingRules && foldingRules.markers;
    return Promise.resolve(computeRanges(this.editorModel, offSide, markers, this.maxFoldingRegions, notifyTooManyRegions));
  }
}
class RangesCollector {
  constructor(foldingRangesLimit, _notifyTooManyRegions) {
    this._notifyTooManyRegions = _notifyTooManyRegions;
    this._startIndexes = [];
    this._endIndexes = [];
    this._indentOccurrences = [];
    this._length = 0;
    this._foldingRangesLimit = foldingRangesLimit;
  }
  insertFirst(startLineNumber, endLineNumber, indent) {
    if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
      return;
    }
    let index = this._length;
    this._startIndexes[index] = startLineNumber;
    this._endIndexes[index] = endLineNumber;
    this._length++;
    if (indent < 1e3) {
      this._indentOccurrences[indent] = (this._indentOccurrences[indent] || 0) + 1;
    }
  }
  toIndentRanges(model) {
    if (this._length <= this._foldingRangesLimit) {
      let startIndexes = new Uint32Array(this._length);
      let endIndexes = new Uint32Array(this._length);
      for (let i = this._length - 1, k = 0; i >= 0; i--, k++) {
        startIndexes[k] = this._startIndexes[i];
        endIndexes[k] = this._endIndexes[i];
      }
      return new FoldingRegions(startIndexes, endIndexes);
    } else {
      if (this._notifyTooManyRegions) {
        this._notifyTooManyRegions(this._foldingRangesLimit);
      }
      let entries = 0;
      let maxIndent = this._indentOccurrences.length;
      for (let i = 0; i < this._indentOccurrences.length; i++) {
        let n = this._indentOccurrences[i];
        if (n) {
          if (n + entries > this._foldingRangesLimit) {
            maxIndent = i;
            break;
          }
          entries += n;
        }
      }
      const tabSize = model.getOptions().tabSize;
      let startIndexes = new Uint32Array(this._foldingRangesLimit);
      let endIndexes = new Uint32Array(this._foldingRangesLimit);
      for (let i = this._length - 1, k = 0; i >= 0; i--) {
        let startIndex = this._startIndexes[i];
        let lineContent = model.getLineContent(startIndex);
        let indent = computeIndentLevel(lineContent, tabSize);
        if (indent < maxIndent || indent === maxIndent && entries++ < this._foldingRangesLimit) {
          startIndexes[k] = startIndex;
          endIndexes[k] = this._endIndexes[i];
          k++;
        }
      }
      return new FoldingRegions(startIndexes, endIndexes);
    }
  }
}
function computeRanges(model, offSide, markers, foldingRangesLimit, notifyTooManyRegions) {
  const tabSize = model.getOptions().tabSize;
  foldingRangesLimit = foldingRangesLimit !== null && foldingRangesLimit !== void 0 ? foldingRangesLimit : MAX_FOLDING_REGIONS_FOR_INDENT_DEFAULT;
  let result = new RangesCollector(foldingRangesLimit, notifyTooManyRegions);
  let pattern = void 0;
  if (markers) {
    pattern = new RegExp(`(${markers.start.source})|(?:${markers.end.source})`);
  }
  let previousRegions = [];
  let line = model.getLineCount() + 1;
  previousRegions.push({ indent: -1, endAbove: line, line });
  for (let line2 = model.getLineCount(); line2 > 0; line2--) {
    let lineContent = model.getLineContent(line2);
    let indent = computeIndentLevel(lineContent, tabSize);
    let previous = previousRegions[previousRegions.length - 1];
    if (indent === -1) {
      if (offSide) {
        previous.endAbove = line2;
      }
      continue;
    }
    let m;
    if (pattern && (m = lineContent.match(pattern))) {
      if (m[1]) {
        let i = previousRegions.length - 1;
        while (i > 0 && previousRegions[i].indent !== -2) {
          i--;
        }
        if (i > 0) {
          previousRegions.length = i + 1;
          previous = previousRegions[i];
          result.insertFirst(line2, previous.line, indent);
          previous.line = line2;
          previous.indent = indent;
          previous.endAbove = line2;
          continue;
        }
      } else {
        previousRegions.push({ indent: -2, endAbove: line2, line: line2 });
        continue;
      }
    }
    if (previous.indent > indent) {
      do {
        previousRegions.pop();
        previous = previousRegions[previousRegions.length - 1];
      } while (previous.indent > indent);
      let endLineNumber = previous.endAbove - 1;
      if (endLineNumber - line2 >= 1) {
        result.insertFirst(line2, endLineNumber, indent);
      }
    }
    if (previous.indent === indent) {
      previous.endAbove = line2;
    } else {
      previousRegions.push({ indent, endAbove: line2, line: line2 });
    }
  }
  return result.toIndentRanges(model);
}
export {
  ID_INDENT_PROVIDER,
  IndentRangeProvider,
  RangesCollector,
  computeRanges
};
