"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const range = require("../../../common/range.js");
function groupIntersect(range$1, groups) {
  const result = [];
  for (let r of groups) {
    if (range$1.start >= r.range.end) {
      continue;
    }
    if (range$1.end < r.range.start) {
      break;
    }
    const intersection = range.Range.intersect(range$1, r.range);
    if (range.Range.isEmpty(intersection)) {
      continue;
    }
    result.push({
      range: intersection,
      size: r.size
    });
  }
  return result;
}
function shift({ start, end }, much) {
  return { start: start + much, end: end + much };
}
function consolidate(groups) {
  const result = [];
  let previousGroup = null;
  for (let group of groups) {
    const start = group.range.start;
    const end = group.range.end;
    const size = group.size;
    if (previousGroup && size === previousGroup.size) {
      previousGroup.range.end = end;
      continue;
    }
    previousGroup = { range: { start, end }, size };
    result.push(previousGroup);
  }
  return result;
}
function concat(...groups) {
  return consolidate(groups.reduce((r, g) => r.concat(g), []));
}
class RangeMap {
  constructor() {
    this.groups = [];
    this._size = 0;
  }
  splice(index, deleteCount, items = []) {
    const diff = items.length - deleteCount;
    const before = groupIntersect({ start: 0, end: index }, this.groups);
    const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups).map((g) => ({ range: shift(g.range, diff), size: g.size }));
    const middle = items.map((item, i) => ({
      range: { start: index + i, end: index + i + 1 },
      size: item.size
    }));
    this.groups = concat(before, middle, after);
    this._size = this.groups.reduce((t, g) => t + g.size * (g.range.end - g.range.start), 0);
  }
  /**
   * Returns the number of items in the range map.
   */
  get count() {
    const len = this.groups.length;
    if (!len) {
      return 0;
    }
    return this.groups[len - 1].range.end;
  }
  /**
   * Returns the sum of the sizes of all items in the range map.
   */
  get size() {
    return this._size;
  }
  /**
   * Returns the index of the item at the given position.
   */
  indexAt(position) {
    if (position < 0) {
      return -1;
    }
    let index = 0;
    let size = 0;
    for (let group of this.groups) {
      const count = group.range.end - group.range.start;
      const newSize = size + count * group.size;
      if (position < newSize) {
        return index + Math.floor((position - size) / group.size);
      }
      index += count;
      size = newSize;
    }
    return index;
  }
  /**
   * Returns the index of the item right after the item at the
   * index of the given position.
   */
  indexAfter(position) {
    return Math.min(this.indexAt(position) + 1, this.count);
  }
  /**
   * Returns the start position of the item at the given index.
   */
  positionAt(index) {
    if (index < 0) {
      return -1;
    }
    let position = 0;
    let count = 0;
    for (let group of this.groups) {
      const groupCount = group.range.end - group.range.start;
      const newCount = count + groupCount;
      if (index < newCount) {
        return position + (index - count) * group.size;
      }
      position += groupCount * group.size;
      count = newCount;
    }
    return -1;
  }
}
exports.RangeMap = RangeMap;
exports.consolidate = consolidate;
exports.groupIntersect = groupIntersect;
exports.shift = shift;
