// inspired by https://github.com/classeur/markdown-it-mathjax/blob/master/markdown-it-mathjax.js
//
//
//

function isSafeBoundary(character_code, delimiter_code, md) {
  if (character_code === delimiter_code) {
    return false;
  }

  if (md.utils.isWhiteSpace(character_code)) {
    return true;
  }

  if (md.utils.isMdAsciiPunct(character_code)) {
    return true;
  }

  if (md.utils.isPunctChar(character_code)) {
    return true;
  }

  return false;
}

function math_input(state, silent, delimiter_code) {
  let pos = state.pos,
    posMax = state.posMax;

  if (
    silent ||
    state.src.charCodeAt(pos) !== delimiter_code ||
    posMax < pos + 2
  ) {
    return false;
  }

  // too short
  if (state.src.charCodeAt(pos + 1) === delimiter_code) {
    return false;
  }

  if (pos > 0) {
    let prev = state.src.charCodeAt(pos - 1);
    if (!isSafeBoundary(prev, delimiter_code, state.md)) {
      return false;
    }
  }

  let found;
  for (let i = pos + 1; i < posMax; i++) {
    let code = state.src.charCodeAt(i);
    if (code === delimiter_code && state.src.charCodeAt(i - 1) !== 92 /* \ */) {
      found = i;
      break;
    }
  }

  if (!found) {
    return false;
  }

  if (found + 1 <= posMax) {
    let next = state.src.charCodeAt(found + 1);
    if (next && !isSafeBoundary(next, delimiter_code, state.md)) {
      return false;
    }
  }

  let data = state.src.slice(pos + 1, found);
  let token = state.push("html_raw", "", 0);

  const escaped = state.md.utils.escapeHtml(data);
  let math_class = delimiter_code === 36 ? "'math'" : "'asciimath'";
  token.content = `<span class=${math_class}>${escaped}</span>`;
  state.pos = found + 1;
  return true;
}

function inlineMath(state, silent) {
  return math_input(state, silent, 36 /* $ */);
}

function asciiMath(state, silent) {
  return math_input(state, silent, 37 /* % */);
}

function isBlockMarker(state, start, max, md) {
  if (state.src.charCodeAt(start) !== 36 /* $ */) {
    return false;
  }

  start++;

  if (state.src.charCodeAt(start) !== 36 /* $ */) {
    return false;
  }

  start++;

  // ensure we only have newlines after our $$
  for (let i = start; i < max; i++) {
    if (!md.utils.isSpace(state.src.charCodeAt(i))) {
      return false;
    }
  }

  return true;
}

function blockMath(state, startLine, endLine, silent) {
  let start = state.bMarks[startLine] + state.tShift[startLine],
    max = state.eMarks[startLine];

  if (!isBlockMarker(state, start, max, state.md)) {
    return false;
  }

  if (silent) {
    return true;
  }

  let nextLine = startLine;
  let closed = false;
  for (;;) {
    nextLine++;

    // unclosed $$ is considered math
    if (nextLine >= endLine) {
      break;
    }

    if (
      isBlockMarker(
        state,
        state.bMarks[nextLine] + state.tShift[nextLine],
        state.eMarks[nextLine],
        state.md
      )
    ) {
      closed = true;
      break;
    }
  }

  let token = state.push("html_raw", "", 0);

  let endContent = closed ? state.eMarks[nextLine - 1] : state.eMarks[nextLine];
  let content = state.src.slice(
    state.bMarks[startLine + 1] + state.tShift[startLine + 1],
    endContent
  );

  const escaped = state.md.utils.escapeHtml(content);
  token.content = `<div class='math'>\n${escaped}\n</div>\n`;

  state.line = closed ? nextLine + 1 : nextLine;

  return true;
}

export function setup(helper) {
  if (!helper.markdownIt) {
    return;
  }

  let enable_asciimath;
  helper.registerOptions((opts, siteSettings) => {
    opts.features.math = siteSettings.discourse_math_enabled;
    enable_asciimath = siteSettings.discourse_math_enable_asciimath;
  });

  helper.registerPlugin((md) => {
    if (enable_asciimath) {
      md.inline.ruler.after("escape", "asciimath", asciiMath);
    }
    md.inline.ruler.after("escape", "math", inlineMath);
    md.block.ruler.after("code", "math", blockMath, {
      alt: ["paragraph", "reference", "blockquote", "list"],
    });
  });
}
