/*
  parser 主组件
  github：https://github.com/jin-yufeng/Parser
  docs：https://jin-yufeng.github.io/Parser
  author：JinYufeng
  update：2020/04/25
*/
var cache = {},
  Parser = require('./libs/MpHtmlParser.js'),
  fs = wx.getFileSystemManager && wx.getFileSystemManager();
try {
  var dom = require('./libs/document.js');
} catch (e) {}
// 计算 cache 的 key
function hash(str) {
  for (var i = str.length, val = 5381; i--;)
    val += (val << 5) + str.charCodeAt(i);
  return val;
}
Component({
  options: {
    pureDataPattern: /^[acdgtux]|W/
  },
  properties: {
    'html': {
      type: null,
      observer(html) {
        if (this._refresh) this._refresh = false;
        else this.setContent(html, false, true);
      }
    },
    'autosetTitle': {
      type: Boolean,
      value: true
    },
    'autopause': {
      type: Boolean,
      value: true
    },
    'compress': Number,
    'domain': String,
    'gestureZoom': Boolean,
    'lazyLoad': Boolean,
    'selectable': Boolean,
    'tagStyle': Object,
    'showWithAnimation': Boolean,
    'useAnchor': Boolean,
    'useCache': Boolean,
    'xml': Boolean
  },
  relations: {
    '../parser-group/parser-group': {
      type: 'ancestor'
    }
  },
  created() {
    // 图片数组
    this.imgList = [];
    this.imgList.setItem = function (i, src) {
      if (!i || !src) return;
      // 去重
      if (src.indexOf('http') == 0 && this.includes(src)) {
        var newSrc = '';
        for (var j = 0, c; c = src[j]; j++) {
          if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
          newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
        }
        newSrc += src.substr(j);
        return this[i] = newSrc;
      }
      this[i] = src;
      // 暂存 data src
      if (src.includes('data:image')) {
        var info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
        if (!info) return;
        var filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
        fs && fs.writeFile({
          filePath,
          data: info[3],
          encoding: info[2],
          success: () => this[i] = filePath
        })
      }
    }
    this.imgList.each = function (f) {
      for (var i = 0, len = this.length; i < len; i++)
        this.setItem(i, f(this[i], i, this));
    }
    if (dom) this.document = new dom(this);
  },
  detached() {
    // 删除暂存
    this.imgList.each(src => {
      if (src && src.includes(wx.env.USER_DATA_PATH) && fs)
        fs.unlink({
          filePath: src
        })
    })
    clearInterval(this._timer);
  },
  methods: {
    // 锚点跳转
    navigateTo(obj) {
      if (!this.data.useAnchor)
        return obj.fail && obj.fail({
          errMsg: 'Anchor is disabled'
        })
      this.createSelectorQuery()
        .select('.top' + (obj.id ? '>>>#' + obj.id : '')).boundingClientRect()
        .selectViewport().scrollOffset().exec(res => {
          if (!res[0])
            return this.group ? this.group.navigateTo(this.i, obj) :
              obj.fail && obj.fail({
                errMsg: 'Label not found'
              });
          obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
          wx.pageScrollTo(obj);
        })
    },
    // 获取文本
    getText(ns = this.data.html) {
      var txt = '';
      for (var i = 0, n; n = ns[i++];) {
        if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
        else if (n.type == 'br') txt += '\n';
        else {
          // 块级标签前后加换行
          var br = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] > '0' && n.name[1] < '7');
          if (br && txt && txt[txt.length - 1] != '\n') txt += '\n';
          if (n.children) txt += this.getText(n.children);
          if (br && txt[txt.length - 1] != '\n') txt += '\n';
          else if (n.name == 'td' || n.name == 'th') txt += '\t';
        }
      }
      return txt;
    },
    // 获取视频 context
    getVideoContext(id) {
      if (!id) return this.videoContexts;
      for (var i = this.videoContexts.length; i--;)
        if (this.videoContexts[i].id == id) return this.videoContexts[i];
    },
    // 渲染富文本
    setContent(html, append, _watch) {
      var data = {};
      if (!html) {
        if (_watch || append) return;
        data.html = '';
      } else if (typeof html == 'string') {
        let parser = new Parser(html, this.data);
        // 缓存读取
        if (this.data.useCache) {
          var hashVal = hash(html);
          if (cache[hashVal]) data.html = cache[hashVal];
          else {
            data.html = parser.parse();
            cache[hashVal] = data.html;
          }
        } else data.html = parser.parse();
        this._refresh = true;
        this.triggerEvent('parse', data.html);
      } else if (html.constructor == Array) {
        // 转换不符合格式的 array
        if (html.length && html[0].PoweredBy != 'Parser') {
          let parser = new Parser('', this.data);
          (function f(ns) {
            for (var i = 0, n; n = ns[i]; i++) {
              if (n.type == 'text') continue;
              n.attrs = n.attrs || {};
              for (var key in n.attrs)
                if (typeof n.attrs[key] != 'string') n.attrs[key] = n.attrs[key].toString();
              parser.matchAttr(n);
              if (n.children) {
                parser.STACK.push(n);
                f(n.children);
                parser.popNode(parser.STACK.pop());
              }
            }
          })(html);
          data.html = html;
        }
        if (!_watch) data.html = html;
      } else if (typeof html == 'object' && html.nodes) {
        data.html = html.nodes;
        console.warn('错误的 html 类型：object 类型已废弃');
      } else
        return console.warn('错误的 html 类型：' + typeof html);
      if (append) {
        this._refresh = true;
        data.html = (this.data.html || []).concat(data.html);
      } else if (this.data.showWithAnimation) data.showAm = 'animation: show .5s';
      if (data.html || data.showAm) this.setData(data);
      // 设置标题
      if (this.data.html.length && this.data.html[0].title && this.data.autosetTitle)
        wx.setNavigationBarTitle({
          title: this.data.html[0].title
        })
      this.imgList.length = 0;
      this.videoContexts = [];
      var ns = this.selectAllComponents('.top,.top>>>._node');
      for (let i = 0, n; n = ns[i++];) {
        n.top = this;
        for (var j = 0, item; item = n.data.nodes[j++];) {
          if (item.c) continue;
          // 获取图片列表
          if (item.name == 'img')
            this.imgList.setItem(item.attrs.i, item.attrs.src);
          // 音视频控制
          else if (item.name == 'video' || item.name == 'audio') {
            var ctx;
            if (item.name == 'video') ctx = wx.createVideoContext(item.attrs.id, n);
            else ctx = n.selectComponent('#' + item.attrs.id);
            if (ctx) {
              ctx.id = item.attrs.id;
              this.videoContexts.push(ctx);
            }
          }
        }
      }
      (wx.nextTick || setTimeout)(() => this.triggerEvent('load'), 50);
      var height;
      clearInterval(this._timer);
      this._timer = setInterval(() => {
        this.createSelectorQuery().select('.top').boundingClientRect(res => {
          this.rect = res;
          if (res.height == height) {
            this.triggerEvent('ready', res)
            clearInterval(this._timer);
          }
          height = res.height;
        }).exec();
      }, 350)
    },
    // 预加载
    preLoad(html, num) {
      if (typeof html == 'string') {
        var id = hash(html);
        html = new Parser(html, this.data).parse();
        cache[id] = html;
      }
      var imgs, wait = [];
      (function f(ns) {
        for (var i = 0, n; n = ns[i++];) {
          if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
            wait.push(n.attrs.src);
          f(n.children || []);
        }
      })(html);
      if (num) wait = wait.slice(0, num);
      this._wait = (this._wait || []).concat(wait);
      if (!this.data.imgs) imgs = this._wait.splice(0, 15);
      else if (this.data.imgs.length < 15)
        imgs = this.data.imgs.concat(this._wait.splice(0, 15 - this.data.imgs.length));
      imgs && this.setData({
        imgs
      });
    },
    _load(e) {
      if (this._wait.length)
        this.setData({
          [`imgs[${e.target.id}]`]: this._wait.shift()
        })
    },
    // 事件处理
    _tap(e) {
      if (this.data.gestureZoom && e.timeStamp - this._lastT < 300) {
        var initY = e.detail.y - e.currentTarget.offsetTop;
        if (this._zoom) {
          this._scaleAm.translateX(0).scale(1).step();
          wx.pageScrollTo({
            scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
            duration: 400
          })
        } else {
          var initX = e.detail.x - e.currentTarget.offsetLeft;
          this._initY = initY;
          this._scaleAm = wx.createAnimation({
            transformOrigin: `${initX}px ${this._initY}px 0`,
            timingFunction: 'ease-in-out'
          });
          this._scaleAm.scale(2).step();
          this._tMax = initX / 2;
          this._tMin = (initX - this.rect.width) / 2;
          this._tX = 0;
        }
        this._zoom = !this._zoom;
        this.setData({
          scaleAm: this._scaleAm.export()
        })
      }
      this._lastT = e.timeStamp;
    },
    _touchstart(e) {
      if (e.touches.length == 1)
        this._initX = this._lastX = e.touches[0].pageX;
    },
    _touchmove(e) {
      var diff = e.touches[0].pageX - this._lastX;
      if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
        this._lastX = e.touches[0].pageX;
        if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0)) return;
        this._tX += diff * Math.abs(this._lastX - this._initX) * 0.05;
        if (this._tX < this._tMin) this._tX = this._tMin;
        if (this._tX > this._tMax) this._tX = this._tMax;
        this._scaleAm.translateX(this._tX).step();
        this.setData({
          scaleAm: this._scaleAm.export()
        })
      }
    }
  }
})