import { CharacterClassifier } from "../core/characterClassifier.mjs";
class Uint8Matrix {
  constructor(rows, cols, defaultValue) {
    const data = new Uint8Array(rows * cols);
    for (let i = 0, len = rows * cols; i < len; i++) {
      data[i] = defaultValue;
    }
    this._data = data;
    this.rows = rows;
    this.cols = cols;
  }
  get(row, col) {
    return this._data[row * this.cols + col];
  }
  set(row, col, value) {
    this._data[row * this.cols + col] = value;
  }
}
class StateMachine {
  constructor(edges) {
    let maxCharCode = 0;
    let maxState = 0;
    for (let i = 0, len = edges.length; i < len; i++) {
      const [from, chCode, to] = edges[i];
      if (chCode > maxCharCode) {
        maxCharCode = chCode;
      }
      if (from > maxState) {
        maxState = from;
      }
      if (to > maxState) {
        maxState = to;
      }
    }
    maxCharCode++;
    maxState++;
    const states = new Uint8Matrix(
      maxState,
      maxCharCode,
      0
      /* Invalid */
    );
    for (let i = 0, len = edges.length; i < len; i++) {
      const [from, chCode, to] = edges[i];
      states.set(from, chCode, to);
    }
    this._states = states;
    this._maxCharCode = maxCharCode;
  }
  nextState(currentState, chCode) {
    if (chCode < 0 || chCode >= this._maxCharCode) {
      return 0;
    }
    return this._states.get(currentState, chCode);
  }
}
let _stateMachine = null;
function getStateMachine() {
  if (_stateMachine === null) {
    _stateMachine = new StateMachine([
      [
        1,
        104,
        2
        /* H */
      ],
      [
        1,
        72,
        2
        /* H */
      ],
      [
        1,
        102,
        6
        /* F */
      ],
      [
        1,
        70,
        6
        /* F */
      ],
      [
        2,
        116,
        3
        /* HT */
      ],
      [
        2,
        84,
        3
        /* HT */
      ],
      [
        3,
        116,
        4
        /* HTT */
      ],
      [
        3,
        84,
        4
        /* HTT */
      ],
      [
        4,
        112,
        5
        /* HTTP */
      ],
      [
        4,
        80,
        5
        /* HTTP */
      ],
      [
        5,
        115,
        9
        /* BeforeColon */
      ],
      [
        5,
        83,
        9
        /* BeforeColon */
      ],
      [
        5,
        58,
        10
        /* AfterColon */
      ],
      [
        6,
        105,
        7
        /* FI */
      ],
      [
        6,
        73,
        7
        /* FI */
      ],
      [
        7,
        108,
        8
        /* FIL */
      ],
      [
        7,
        76,
        8
        /* FIL */
      ],
      [
        8,
        101,
        9
        /* BeforeColon */
      ],
      [
        8,
        69,
        9
        /* BeforeColon */
      ],
      [
        9,
        58,
        10
        /* AfterColon */
      ],
      [
        10,
        47,
        11
        /* AlmostThere */
      ],
      [
        11,
        47,
        12
        /* End */
      ]
    ]);
  }
  return _stateMachine;
}
let _classifier = null;
function getClassifier() {
  if (_classifier === null) {
    _classifier = new CharacterClassifier(
      0
      /* None */
    );
    const FORCE_TERMINATION_CHARACTERS = ` 	<>'"、。｡､，．：；‘〈「『〔（［｛｢｣｝］）〕』」〉’｀～…`;
    for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) {
      _classifier.set(
        FORCE_TERMINATION_CHARACTERS.charCodeAt(i),
        1
        /* ForceTermination */
      );
    }
    const CANNOT_END_WITH_CHARACTERS = ".,;";
    for (let i = 0; i < CANNOT_END_WITH_CHARACTERS.length; i++) {
      _classifier.set(
        CANNOT_END_WITH_CHARACTERS.charCodeAt(i),
        2
        /* CannotEndIn */
      );
    }
  }
  return _classifier;
}
class LinkComputer {
  static _createLink(classifier, line, lineNumber, linkBeginIndex, linkEndIndex) {
    let lastIncludedCharIndex = linkEndIndex - 1;
    do {
      const chCode = line.charCodeAt(lastIncludedCharIndex);
      const chClass = classifier.get(chCode);
      if (chClass !== 2) {
        break;
      }
      lastIncludedCharIndex--;
    } while (lastIncludedCharIndex > linkBeginIndex);
    if (linkBeginIndex > 0) {
      const charCodeBeforeLink = line.charCodeAt(linkBeginIndex - 1);
      const lastCharCodeInLink = line.charCodeAt(lastIncludedCharIndex);
      if (charCodeBeforeLink === 40 && lastCharCodeInLink === 41 || charCodeBeforeLink === 91 && lastCharCodeInLink === 93 || charCodeBeforeLink === 123 && lastCharCodeInLink === 125) {
        lastIncludedCharIndex--;
      }
    }
    return {
      range: {
        startLineNumber: lineNumber,
        startColumn: linkBeginIndex + 1,
        endLineNumber: lineNumber,
        endColumn: lastIncludedCharIndex + 2
      },
      url: line.substring(linkBeginIndex, lastIncludedCharIndex + 1)
    };
  }
  static computeLinks(model, stateMachine = getStateMachine()) {
    const classifier = getClassifier();
    const result = [];
    for (let i = 1, lineCount = model.getLineCount(); i <= lineCount; i++) {
      const line = model.getLineContent(i);
      const len = line.length;
      let j = 0;
      let linkBeginIndex = 0;
      let linkBeginChCode = 0;
      let state = 1;
      let hasOpenParens = false;
      let hasOpenSquareBracket = false;
      let inSquareBrackets = false;
      let hasOpenCurlyBracket = false;
      while (j < len) {
        let resetStateMachine = false;
        const chCode = line.charCodeAt(j);
        if (state === 13) {
          let chClass;
          switch (chCode) {
            case 40:
              hasOpenParens = true;
              chClass = 0;
              break;
            case 41:
              chClass = hasOpenParens ? 0 : 1;
              break;
            case 91:
              inSquareBrackets = true;
              hasOpenSquareBracket = true;
              chClass = 0;
              break;
            case 93:
              inSquareBrackets = false;
              chClass = hasOpenSquareBracket ? 0 : 1;
              break;
            case 123:
              hasOpenCurlyBracket = true;
              chClass = 0;
              break;
            case 125:
              chClass = hasOpenCurlyBracket ? 0 : 1;
              break;
            case 39:
              chClass = linkBeginChCode === 34 || linkBeginChCode === 96 ? 0 : 1;
              break;
            case 34:
              chClass = linkBeginChCode === 39 || linkBeginChCode === 96 ? 0 : 1;
              break;
            case 96:
              chClass = linkBeginChCode === 39 || linkBeginChCode === 34 ? 0 : 1;
              break;
            case 42:
              chClass = linkBeginChCode === 42 ? 1 : 0;
              break;
            case 124:
              chClass = linkBeginChCode === 124 ? 1 : 0;
              break;
            case 32:
              chClass = inSquareBrackets ? 0 : 1;
              break;
            default:
              chClass = classifier.get(chCode);
          }
          if (chClass === 1) {
            result.push(LinkComputer._createLink(classifier, line, i, linkBeginIndex, j));
            resetStateMachine = true;
          }
        } else if (state === 12) {
          let chClass;
          if (chCode === 91) {
            hasOpenSquareBracket = true;
            chClass = 0;
          } else {
            chClass = classifier.get(chCode);
          }
          if (chClass === 1) {
            resetStateMachine = true;
          } else {
            state = 13;
          }
        } else {
          state = stateMachine.nextState(state, chCode);
          if (state === 0) {
            resetStateMachine = true;
          }
        }
        if (resetStateMachine) {
          state = 1;
          hasOpenParens = false;
          hasOpenSquareBracket = false;
          hasOpenCurlyBracket = false;
          linkBeginIndex = j + 1;
          linkBeginChCode = chCode;
        }
        j++;
      }
      if (state === 13) {
        result.push(LinkComputer._createLink(classifier, line, i, linkBeginIndex, len));
      }
    }
    return result;
  }
}
function computeLinks(model) {
  if (!model || typeof model.getLineCount !== "function" || typeof model.getLineContent !== "function") {
    return [];
  }
  return LinkComputer.computeLinks(model);
}
export {
  LinkComputer,
  StateMachine,
  Uint8Matrix,
  computeLinks
};
