"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const strings = require("../../../../base/common/strings.js");
const stringBuilder = require("../../core/stringBuilder.js");
const range = require("../../core/range.js");
class RichEditBracket {
  constructor(languageId, index, open, close, forwardRegex, reversedRegex) {
    this._richEditBracketBrand = void 0;
    this.languageId = languageId;
    this.index = index;
    this.open = open;
    this.close = close;
    this.forwardRegex = forwardRegex;
    this.reversedRegex = reversedRegex;
    this._openSet = RichEditBracket._toSet(this.open);
    this._closeSet = RichEditBracket._toSet(this.close);
  }
  /**
   * Check if the provided `text` is an open bracket in this group.
   */
  isOpen(text) {
    return this._openSet.has(text);
  }
  /**
   * Check if the provided `text` is a close bracket in this group.
   */
  isClose(text) {
    return this._closeSet.has(text);
  }
  static _toSet(arr) {
    const result = /* @__PURE__ */ new Set();
    for (const element of arr) {
      result.add(element);
    }
    return result;
  }
}
function groupFuzzyBrackets(brackets) {
  const N = brackets.length;
  brackets = brackets.map((b) => [b[0].toLowerCase(), b[1].toLowerCase()]);
  const group = [];
  for (let i = 0; i < N; i++) {
    group[i] = i;
  }
  const areOverlapping = (a, b) => {
    const [aOpen, aClose] = a;
    const [bOpen, bClose] = b;
    return aOpen === bOpen || aOpen === bClose || aClose === bOpen || aClose === bClose;
  };
  const mergeGroups = (g1, g2) => {
    const newG = Math.min(g1, g2);
    const oldG = Math.max(g1, g2);
    for (let i = 0; i < N; i++) {
      if (group[i] === oldG) {
        group[i] = newG;
      }
    }
  };
  for (let i = 0; i < N; i++) {
    const a = brackets[i];
    for (let j = i + 1; j < N; j++) {
      const b = brackets[j];
      if (areOverlapping(a, b)) {
        mergeGroups(group[i], group[j]);
      }
    }
  }
  const result = [];
  for (let g = 0; g < N; g++) {
    const currentOpen = [];
    const currentClose = [];
    for (let i = 0; i < N; i++) {
      if (group[i] === g) {
        const [open, close] = brackets[i];
        currentOpen.push(open);
        currentClose.push(close);
      }
    }
    if (currentOpen.length > 0) {
      result.push({
        open: currentOpen,
        close: currentClose
      });
    }
  }
  return result;
}
class RichEditBrackets {
  constructor(languageId, _brackets) {
    this._richEditBracketsBrand = void 0;
    const brackets = groupFuzzyBrackets(_brackets);
    this.brackets = brackets.map((b, index) => {
      return new RichEditBracket(languageId, index, b.open, b.close, getRegexForBracketPair(b.open, b.close, brackets, index), getReversedRegexForBracketPair(b.open, b.close, brackets, index));
    });
    this.forwardRegex = getRegexForBrackets(this.brackets);
    this.reversedRegex = getReversedRegexForBrackets(this.brackets);
    this.textIsBracket = {};
    this.textIsOpenBracket = {};
    this.maxBracketLength = 0;
    for (const bracket of this.brackets) {
      for (const open of bracket.open) {
        this.textIsBracket[open] = bracket;
        this.textIsOpenBracket[open] = true;
        this.maxBracketLength = Math.max(this.maxBracketLength, open.length);
      }
      for (const close of bracket.close) {
        this.textIsBracket[close] = bracket;
        this.textIsOpenBracket[close] = false;
        this.maxBracketLength = Math.max(this.maxBracketLength, close.length);
      }
    }
  }
}
function collectSuperstrings(str, brackets, currentIndex, dest) {
  for (let i = 0, len = brackets.length; i < len; i++) {
    if (i === currentIndex) {
      continue;
    }
    const bracket = brackets[i];
    for (const open of bracket.open) {
      if (open.indexOf(str) >= 0) {
        dest.push(open);
      }
    }
    for (const close of bracket.close) {
      if (close.indexOf(str) >= 0) {
        dest.push(close);
      }
    }
  }
}
function lengthcmp(a, b) {
  return a.length - b.length;
}
function unique(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  const result = [];
  const seen = /* @__PURE__ */ new Set();
  for (const element of arr) {
    if (seen.has(element)) {
      continue;
    }
    result.push(element);
    seen.add(element);
  }
  return result;
}
function getRegexForBracketPair(open, close, brackets, currentIndex) {
  let pieces = [];
  pieces = pieces.concat(open);
  pieces = pieces.concat(close);
  for (let i = 0, len = pieces.length; i < len; i++) {
    collectSuperstrings(pieces[i], brackets, currentIndex, pieces);
  }
  pieces = unique(pieces);
  pieces.sort(lengthcmp);
  pieces.reverse();
  return createBracketOrRegExp(pieces);
}
function getReversedRegexForBracketPair(open, close, brackets, currentIndex) {
  let pieces = [];
  pieces = pieces.concat(open);
  pieces = pieces.concat(close);
  for (let i = 0, len = pieces.length; i < len; i++) {
    collectSuperstrings(pieces[i], brackets, currentIndex, pieces);
  }
  pieces = unique(pieces);
  pieces.sort(lengthcmp);
  pieces.reverse();
  return createBracketOrRegExp(pieces.map(toReversedString));
}
function getRegexForBrackets(brackets) {
  let pieces = [];
  for (const bracket of brackets) {
    for (const open of bracket.open) {
      pieces.push(open);
    }
    for (const close of bracket.close) {
      pieces.push(close);
    }
  }
  pieces = unique(pieces);
  return createBracketOrRegExp(pieces);
}
function getReversedRegexForBrackets(brackets) {
  let pieces = [];
  for (const bracket of brackets) {
    for (const open of bracket.open) {
      pieces.push(open);
    }
    for (const close of bracket.close) {
      pieces.push(close);
    }
  }
  pieces = unique(pieces);
  return createBracketOrRegExp(pieces.map(toReversedString));
}
function prepareBracketForRegExp(str) {
  const insertWordBoundaries = /^[\w ]+$/.test(str);
  str = strings.escapeRegExpCharacters(str);
  return insertWordBoundaries ? `\\b${str}\\b` : str;
}
function createBracketOrRegExp(pieces) {
  const regexStr = `(${pieces.map(prepareBracketForRegExp).join(")|(")})`;
  return strings.createRegExp(regexStr, true);
}
const toReversedString = function() {
  function reverse(str) {
    if (stringBuilder.hasTextDecoder) {
      const arr = new Uint16Array(str.length);
      let offset = 0;
      for (let i = str.length - 1; i >= 0; i--) {
        arr[offset++] = str.charCodeAt(i);
      }
      return stringBuilder.getPlatformTextDecoder().decode(arr);
    } else {
      const result = [];
      let resultLen = 0;
      for (let i = str.length - 1; i >= 0; i--) {
        result[resultLen++] = str.charAt(i);
      }
      return result.join("");
    }
  }
  let lastInput = null;
  let lastOutput = null;
  return function toReversedString2(str) {
    if (lastInput !== str) {
      lastInput = str;
      lastOutput = reverse(lastInput);
    }
    return lastOutput;
  };
}();
class BracketsUtils {
  static _findPrevBracketInText(reversedBracketRegex, lineNumber, reversedText, offset) {
    const m = reversedText.match(reversedBracketRegex);
    if (!m) {
      return null;
    }
    const matchOffset = reversedText.length - (m.index || 0);
    const matchLength = m[0].length;
    const absoluteMatchOffset = offset + matchOffset;
    return new range.Range(lineNumber, absoluteMatchOffset - matchLength + 1, lineNumber, absoluteMatchOffset + 1);
  }
  static findPrevBracketInRange(reversedBracketRegex, lineNumber, lineText, startOffset, endOffset) {
    const reversedLineText = toReversedString(lineText);
    const reversedSubstr = reversedLineText.substring(lineText.length - endOffset, lineText.length - startOffset);
    return this._findPrevBracketInText(reversedBracketRegex, lineNumber, reversedSubstr, startOffset);
  }
  static findNextBracketInText(bracketRegex, lineNumber, text, offset) {
    const m = text.match(bracketRegex);
    if (!m) {
      return null;
    }
    const matchOffset = m.index || 0;
    const matchLength = m[0].length;
    if (matchLength === 0) {
      return null;
    }
    const absoluteMatchOffset = offset + matchOffset;
    return new range.Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength);
  }
  static findNextBracketInRange(bracketRegex, lineNumber, lineText, startOffset, endOffset) {
    const substr = lineText.substring(startOffset, endOffset);
    return this.findNextBracketInText(bracketRegex, lineNumber, substr, startOffset);
  }
}
exports.BracketsUtils = BracketsUtils;
exports.RichEditBracket = RichEditBracket;
exports.RichEditBrackets = RichEditBrackets;
