const { condense } = require('./util')

var validDivisionCharRE = /[\w).+\-_$\]]/

function parseFilters (exp) {
  var inSingle = false
  var inDouble = false
  var inTemplateString = false
  var inRegex = false
  var curly = 0
  var square = 0
  var paren = 0
  var lastFilterIndex = 0
  var c, prev, i, expression, filters

  for (i = 0; i < exp.length; i++) {
    prev = c
    c = exp.charCodeAt(i)
    if (inSingle) {
      if (c === 0x27 && prev !== 0x5C) {
        inSingle = false
      }
    } else if (inDouble) {
      if (c === 0x22 && prev !== 0x5C) {
        inDouble = false
      }
    } else if (inTemplateString) {
      if (c === 0x60 && prev !== 0x5C) {
        inTemplateString = false
      }
    } else if (inRegex) {
      if (c === 0x2f && prev !== 0x5C) {
        inRegex = false
      }
    } else if (
      c === 0x7C && // pipe
            exp.charCodeAt(i + 1) !== 0x7C &&
            exp.charCodeAt(i - 1) !== 0x7C &&
            !curly && !square && !paren
    ) {
      if (expression === undefined) {
        // first filter, end of expression
        lastFilterIndex = i + 1
        expression = exp.slice(0, i).trim()
      } else {
        pushFilter()
      }
    } else {
      switch (c) {
        case 0x22:
          inDouble = true
          break // "
        case 0x27:
          inSingle = true
          break // '
        case 0x60:
          inTemplateString = true
          break // `
        case 0x28:
          paren++
          break // (
        case 0x29:
          paren--
          break // )
        case 0x5B:
          square++
          break // [
        case 0x5D:
          square--
          break // ]
        case 0x7B:
          curly++
          break // {
        case 0x7D:
          curly--
          break // }
      }
      if (c === 0x2f) { // /
        var j = i - 1
        /* eslint-disable no-void */
        var p = (void 0)
        // find first non-whitespace prev char
        for (; j >= 0; j--) {
          p = exp.charAt(j)
          if (p !== ' ') {
            break
          }
        }
        if (!p || !validDivisionCharRE.test(p)) {
          inRegex = true
        }
      }
    }
  }

  if (expression === undefined) {
    expression = exp.slice(0, i).trim()
  } else if (lastFilterIndex !== 0) {
    pushFilter()
  }

  function pushFilter () {
    (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
    lastFilterIndex = i + 1
  }

  if (filters) {
    for (i = 0; i < filters.length; i++) {
      expression = wrapFilter(expression, filters[i])
    }
  }

  return expression
}

function wrapFilter (exp, filter) {
  var i = filter.indexOf('(')
  if (i < 0) {
    // _f: resolveFilter
    return ('_f("' + filter + '")(' + exp + ')')
  } else {
    var name = filter.slice(0, i)
    var args = filter.slice(i + 1)
    return ('_f("' + name + '")(' + exp + (args !== ')' ? ',' + args : args))
  }
}
const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g

function parseText (
  text
) {
  const tokens = []
  const rawTokens = []

  let lastIndex = defaultTagRE.lastIndex = 0
  let match, index, tokenValue

  while ((match = defaultTagRE.exec(text))) {
    index = match.index
    // push text token
    if (index > lastIndex) {
      rawTokens.push(tokenValue = text.slice(lastIndex, index))
      tokens.push(JSON.stringify(tokenValue))
    }
    // tag token
    var exp = parseFilters(match[1].trim())
    tokens.push(('_s(' + exp + ')'))
    rawTokens.push({
      '@binding': exp
    })
    lastIndex = index + match[0].length
  }
  if (lastIndex < text.length) {
    rawTokens.push(tokenValue = text.slice(lastIndex))
    tokens.push(JSON.stringify(tokenValue))
  }
  return {
    expression: tokens.join('+'),
    tokens: rawTokens
  }
}

module.exports = {
  postTransformNode (el) { // 重新格式化 text 节点,应该使用postTransformNode,但 mpvue 使用的 template-compiler 较老，导致 postTransformNode 时机不对
    const children = el.children
    if (children && children.length) {
      children.forEach(childEl => {
        if (childEl.text) {
          // fix: 和 vue3 一致压缩空白行为，防止去除 &nbsp; 等空白符
          const text = condense(childEl.text)
          if (childEl.type === 2) {
            try {
              const {
                expression,
                tokens
              } = parseText(text)
              childEl.expression = expression
              childEl.tokens = tokens
              childEl.text = text
            } catch (e) {
              console.log(e)
            }
          } else {
            childEl.text = text
          }
        }
      })
    }
    return ''
  }
}
