// 1、js-encode-plugin.js 文件（webpack的js加密插件）
const fs = require('fs')// node的文件系统模块，用于读写及操作文件
const path = require('path')// node提供的一些用于处理文件路径的小工具
var chalk = require('chalk')// 用于向控制台输出带颜色的问题提示
// 2、模块对外暴露的 js 函数
function JsEncodePlugin(pluginOptions) {
  this.options = pluginOptions
}

// 3、原型定义一个 apply 函数，并注入了 compiler 对象
JsEncodePlugin.prototype.apply = function(compiler) {
  const _this = this
  // 4、挂载 webpack 事件钩子（这里挂载的是 after-emit 事件,在将内存中 assets 内容写到磁盘文件夹之后触发的webpack生命周期钩子）
  compiler.plugin('after-emit', function(compilation, callback) {
    // ... 内部进行自定义的编译操作
    // 5、操作 compilation 对象的内部数据
    console.log(chalk.cyan('\n jsencode start.\n'))
    var filePath = path.resolve(__dirname, _this.options.assetsPath)// 设置需要加密的js文件路径，_this.options.assetsPath为插件配置中传过来的需要加密的js文件路径
    filterFile(filePath)
    function filterFile(fp) {
      fs.readdir(fp, (err, files) => { // 读取该文件路径
        if (err) {
          console.log(chalk.yellow(
            '读取js文件夹异常：\n' +
            err.message + '\n'
          ))
          return
        }
        files.forEach((filename) => { // 遍历该路径下所有文件
          if (_this.options.jsReg.test(filename)) { // 利用正则匹配我们要加密的文件,_this.options.jsReg为插件中传过来的需要加密的js文件正则，用以筛选出我们需要加密的js文件。
            var filedir = path.resolve(fp, filename)
            fs.readFile(filedir, 'utf-8', (err, data) => { // 读取文件源码
              if (err) {
                console.log(chalk.yellow(
                  '读取js文件异常：\n' +
                  err.message + '\n'
                ))
                return
              }
              // 调用jjencode函数对源码进行jjencode加密，_this.options.global为插件配置中传过来的加密使用的全局变量名，将在jjencode函数中作为第一个参数传入
              const result = jjencode(_this.options.global, data)
              fs.writeFile(filedir, result, (err) => { // 将加密后的代码写回文件中
                if (err) {
                  console.log(chalk.yellow(
                    '写入加密后的js文件异常：\n' +
                    err.message + '\n'
                  ))
                  return
                }
                console.log(chalk.cyan('  jsencode complete.\n'))
              })
            })
          }
        })
      })
    }
    // js加密函数
    function jjencode(gv, text) {
      var r = ''
      var n
      var t
      var b = ['___', '__$', '_$_', '_$$', '$__', '$_$', '$$_', '$$$', '$___', '$__$', '$_$_', '$_$$', '$$__', '$$_$', '$$$_', '$$$$']
      var s = ''
      for (var i = 0; i < text.length; i++) {
        n = text.charCodeAt(i)
        if (n == 0x22 || n == 0x5c) {
          s += '\\\\\\' + text.charAt(i).toString(16)
        } else if ((n >= 0x20 && n <= 0x2f) || (n >= 0x3A == 0x40) || (n >= 0x5b && n <= 0x60) || (n >= 0x7b && n <= 0x7f)) {
          s += text.charAt(i)
        } else if ((n >= 0x30 && n <= 0x39) || (n >= 0x61 && n <= 0x66)) {
          if (s) r += '"' + s + '"+'
          r += gv + '.' + b[n < 0x40 ? n - 0x30 : n - 0x57] + '+'
          s = ''
        } else if (n == 0x6c) { // 'l'
          if (s) r += '"' + s + '"+'
          r += '(![]+"")[' + gv + '._$_]+'
          s = ''
        } else if (n == 0x6f) { // 'o'
          if (s) r += '"' + s + '"+'
          r += gv + '._$+'
          s = ''
        } else if (n == 0x74) { // 'u'
          if (s) r += '"' + s + '"+'
          r += gv + '.__+'
          s = ''
        } else if (n == 0x75) { // 'u'
          if (s) r += '"' + s + '"+'
          r += gv + '._+'
          s = ''
        } else if (n < 128) {
          if (s) r += '"' + s
          else r += '"'
          r += '\\\\"+' + n.toString(8).replace(/[0-7]/g, function(c) { return gv + '.' + b[c] + '+' })
          s = ''
        } else {
          if (s) r += '"' + s
          else r += '"'
          r += '\\\\"+' + gv + '._+' + n.toString(16).replace(/[0-9a-f]/gi, function(c) { return gv + '.' + b[parseInt(c, 16)] + '+' })
          s = ''
        }
      }
      if (s) r += '"' + s + '"+'

      r =
        gv + '=~[];' +
        gv + '={___:++' + gv + ',$$$$:(![]+"")[' + gv + '],__$:++' + gv + ',$_$_:(![]+"")[' + gv + '],_$_:++' +
        gv + ',$_$$:({}+"")[' + gv + '],$$_$:(' + gv + '[' + gv + ']+"")[' + gv + '],_$$:++' + gv + ',$$$_:(!""+"")[' +
        gv + '],$__:++' + gv + ',$_$:++' + gv + ',$$__:({}+"")[' + gv + '],$$_:++' + gv + ',$$$:++' + gv + ',$___:++' + gv + ',$__$:++' + gv + '};' +
        gv + '.$_=' +
        '(' + gv + '.$_=' + gv + '+"")[' + gv + '.$_$]+' +
        '(' + gv + '._$=' + gv + '.$_[' + gv + '.__$])+' +
        '(' + gv + '.$$=(' + gv + '.$+"")[' + gv + '.__$])+' +
        '((!' + gv + ')+"")[' + gv + '._$$]+' +
        '(' + gv + '.__=' + gv + '.$_[' + gv + '.$$_])+' +
        '(' + gv + '.$=(!""+"")[' + gv + '.__$])+' +
        '(' + gv + '._=(!""+"")[' + gv + '._$_])+' +
        gv + '.$_[' + gv + '.$_$]+' +
        gv + '.__+' +
        gv + '._$+' +
        gv + '.$;' +
        gv + '.$$=' +
        gv + '.$+' +
        '(!""+"")[' + gv + '._$$]+' +
        gv + '.__+' +
        gv + '._+' +
        gv + '.$+' +
        gv + '.$$;' +
        gv + '.$=(' + gv + '.___)[' + gv + '.$_][' + gv + '.$_];' +
        gv + '.$(' + gv + '.$(' + gv + '.$$+"\\""+' + r + '"\\"")())();'
      // console.log(r);
      return r
    }
    // 6、执行 callback 回调
    callback()
  })
}

// 暴露 js 函数
module.exports = JsEncodePlugin
