import marked from 'marked'
import { escape } from 'marked/src/helpers'

function indentCodeCompensation(raw, text) {
  const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
  if (matchIndentToCode === null) {
    return text;
  }
  const indentToCode = matchIndentToCode[1];
  return text
    .split('\n')
    .map(node => {
      const matchIndentInNode = node.match(/^\s+/);
      if (matchIndentInNode === null) {
        return node;
      }
      const [indentInNode] = matchIndentInNode;
      if (indentInNode.length >= indentToCode.length) {
        return node.slice(indentToCode.length);
      }
      return node;
    })
    .join('\n');
}

//兼容之前的 ##标题式写法
let toc = []
let ctx = ["<ul>"]
const renderer = new marked.Renderer()
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/

export function cleanToc() {
  toc = []
  ctx = ["<ul>"]
}

function buildToc(coll, k, level, ctx) {
  if (k >= coll.length || coll[k].level <= level) { return k }
  var node = coll[k]
  ctx.push("<li><a href='#" + node.anchor + "'>" + node.text + "</a>")
  k++
  var childCtx = []
  k = buildToc(coll, k, node.level, childCtx)
  if (childCtx.length > 0) {
    ctx.push("<ul>")
    childCtx.forEach(function (idm) {
      ctx.push(idm)
    });
    ctx.push("</ul>")
  }
  ctx.push("</li>");
  k = buildToc(coll, k, level, ctx)
  return k
}

export function getTocContent() {
  buildToc(toc, 0, 0, ctx);
  ctx.push("</ul>");
  return ctx.join("");
}

const tokenizer = {
  heading(src) {
    const cap = headingRegex.exec(src)
    if (cap) {
      return {
        type: 'heading',
        raw: cap[0],
        depth: cap[1].length,
        text: cap[2]
      }
    }
  },
  fences(src) {
    const cap = this.rules.block.fences.exec(src)
    if (cap) {
      const raw = cap[0]
      let text = indentCodeCompensation(raw, cap[3] || '')
      const lang = cap[2] ? cap[2].trim() : cap[2]
      if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
        const id = next_id()
        const expression = text
        text = id
        math_expressions[id] = { type: 'block', expression }
      }
      return {
        type: 'code',
        raw,
        lang,
        text
      }
    }
  }
}

const latexRegex = /(?:\${2})([^\n`]+?)(?:\${2})/gi
let katex_count = 0
const next_id = () => `__special_katext_id_${katex_count++}__`
let math_expressions = {}

export function getMathExpressions() {
  return math_expressions
}

export function resetMathExpressions() {
  katex_count = 0
  math_expressions = {}
}

function replace_math_with_ids(text) {
  let rs = text.replace(latexRegex, (_match, expression) => {
    const id = next_id()
    math_expressions[id] = { type: 'inline', expression }
    return id
  })

  return rs
}

const original_listitem = renderer.listitem
renderer.listitem = function (text, task, checked) {
  return original_listitem(replace_math_with_ids(text), task, checked)
}

const original_paragraph = renderer.paragraph
renderer.paragraph = function (text) {
  return original_paragraph(replace_math_with_ids(text))
}

const original_tablecell = renderer.tablecell
renderer.tablecell = function (content, flags) {
  return original_tablecell(replace_math_with_ids(content), flags)
}

renderer.code = function (code, infostring, escaped) {
  const lang = (infostring || '').match(/\S*/)[0];
  if (!lang) {
    return '<pre class="prettyprint linenums"><code>'
      + (escaped ? code : escape(code, true))
      + '</code></pre>';
  }

  if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
    return `<p class='editormd-tex'>${code}</p>`
  } else {
    return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
  }

}

renderer.heading = function (text, level, raw) {
  let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-');
  toc.push({
    anchor: anchor,
    level: level,
    text: text
  })
  return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
}
marked.setOptions({
  silent: true,
  smartypants: true,
  gfm: true,
  pedantic: false
})

marked.use({ tokenizer, renderer });

export default marked