import Pen, { penCache, clearPenCache } from './lib/pen';
import Downloader from './lib/downloader';
import WxCanvas from './lib/wx-canvas';

const util = require('./lib/util');
const calc = require('./lib/calc');

const downloader = new Downloader();

// 最大尝试的绘制次数
const MAX_PAINT_COUNT = 5;
const ACTION_DEFAULT_SIZE = 24;
const ACTION_OFFSET = '2rpx';
Component({
  canvasWidthInPx: 0,
  canvasHeightInPx: 0,
  canvasNode: null,
  paintCount: 0,
  currentPalette: {},
  outterDisabled: false,
  isDisabled: false,
  needClear: false,
  /**
   * 组件的属性列表
   */
  properties: {
    use2D: {
      type: Boolean,
    },
    customStyle: {
      type: String,
    },
    // 运行自定义选择框和删除缩放按钮
    customActionStyle: {
      type: Object,
    },
    palette: {
      type: Object,
      observer: function (newVal, oldVal) {
        if (this.isNeedRefresh(newVal, oldVal)) {
          this.paintCount = 0;
          clearPenCache();
          this.startPaint();
        }
      },
    },
    dancePalette: {
      type: Object,
      observer: function (newVal, oldVal) {
        if (!this.isEmpty(newVal) && !this.properties.use2D) {
          clearPenCache();
          this.initDancePalette(newVal);
        }
      },
    },
    // 缩放比，会在传入的 palette 中统一乘以该缩放比
    scaleRatio: {
      type: Number,
      value: 1,
    },
    widthPixels: {
      type: Number,
      value: 0,
    },
    // 启用脏检查，默认 false
    dirty: {
      type: Boolean,
      value: false,
    },
    LRU: {
      type: Boolean,
      value: false,
    },
    action: {
      type: Object,
      observer: function (newVal, oldVal) {
        if (newVal && !this.isEmpty(newVal) && !this.properties.use2D) {
          this.doAction(newVal, null, false, true);
        }
      },
    },
    disableAction: {
      type: Boolean,
      observer: function (isDisabled) {
        this.outterDisabled = isDisabled;
        this.isDisabled = isDisabled;
      },
    },
    clearActionBox: {
      type: Boolean,
      observer: function (needClear) {
        if (needClear && !this.needClear) {
          if (this.frontContext) {
            setTimeout(() => {
              this.frontContext.draw();
            }, 100);
            this.touchedView = {};
            this.prevFindedIndex = this.findedIndex;
            this.findedIndex = -1;
          }
        }
        this.needClear = needClear;
      },
    },
  },

  data: {
    picURL: '',
    showCanvas: true,
    painterStyle: '',
  },

  methods: {
    /**
     * 判断一个 object 是否为 空
     * @param {object} object
     */
    isEmpty(object) {
      for (const i in object) {
        return false;
      }
      return true;
    },

    isNeedRefresh(newVal, oldVal) {
      if (!newVal || this.isEmpty(newVal) || (this.data.dirty && util.equal(newVal, oldVal))) {
        return false;
      }
      return true;
    },

    getBox(rect, type) {
      const boxArea = {
        type: 'rect',
        css: {
          height: `${rect.bottom - rect.top}px`,
          width: `${rect.right - rect.left}px`,
          left: `${rect.left}px`,
          top: `${rect.top}px`,
          borderWidth: '4rpx',
          borderColor: '#1A7AF8',
          color: 'transparent',
        },
      };
      if (type === 'text') {
        boxArea.css = Object.assign({}, boxArea.css, {
          borderStyle: 'dashed',
        });
      }
      if (this.properties.customActionStyle && this.properties.customActionStyle.border) {
        boxArea.css = Object.assign({}, boxArea.css, this.properties.customActionStyle.border);
      }
      Object.assign(boxArea, {
        id: 'box',
      });
      return boxArea;
    },

    getScaleIcon(rect, type) {
      let scaleArea = {};
      const { customActionStyle } = this.properties;
      if (customActionStyle && customActionStyle.scale) {
        scaleArea = {
          type: 'image',
          url: type === 'text' ? customActionStyle.scale.textIcon : customActionStyle.scale.imageIcon,
          css: {
            height: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
          },
        };
      } else {
        scaleArea = {
          type: 'rect',
          css: {
            height: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
            color: '#0000ff',
          },
        };
      }
      scaleArea.css = Object.assign({}, scaleArea.css, {
        align: 'center',
        left: `${rect.right + ACTION_OFFSET.toPx()}px`,
        top:
          type === 'text'
            ? `${rect.top - ACTION_OFFSET.toPx() - scaleArea.css.height.toPx() / 2}px`
            : `${rect.bottom - ACTION_OFFSET.toPx() - scaleArea.css.height.toPx() / 2}px`,
      });
      Object.assign(scaleArea, {
        id: 'scale',
      });
      return scaleArea;
    },

    getDeleteIcon(rect) {
      let deleteArea = {};
      const { customActionStyle } = this.properties;
      if (customActionStyle && customActionStyle.scale) {
        deleteArea = {
          type: 'image',
          url: customActionStyle.delete.icon,
          css: {
            height: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
          },
        };
      } else {
        deleteArea = {
          type: 'rect',
          css: {
            height: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
            borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
            color: '#0000ff',
          },
        };
      }
      deleteArea.css = Object.assign({}, deleteArea.css, {
        align: 'center',
        left: `${rect.left - ACTION_OFFSET.toPx()}px`,
        top: `${rect.top - ACTION_OFFSET.toPx() - deleteArea.css.height.toPx() / 2}px`,
      });
      Object.assign(deleteArea, {
        id: 'delete',
      });
      return deleteArea;
    },

    doAction(action, callback, isMoving, overwrite) {
      if (this.properties.use2D) {
        return;
      }
      let newVal = null;
      if (action) {
        newVal = action.view;
      }
      if (newVal && newVal.id && this.touchedView.id !== newVal.id) {
        // 带 id 的动作给撤回时使用，不带 id，表示对当前选中对象进行操作
        const { views } = this.currentPalette;
        for (let i = 0; i < views.length; i++) {
          if (views[i].id === newVal.id) {
            // 跨层回撤，需要重新构建三层关系
            this.touchedView = views[i];
            this.findedIndex = i;
            this.sliceLayers();
            break;
          }
        }
      }

      const doView = this.touchedView;

      if (!doView || this.isEmpty(doView)) {
        return;
      }
      if (newVal && newVal.css) {
        if (overwrite) {
          doView.css = newVal.css;
        } else if (Array.isArray(doView.css) && Array.isArray(newVal.css)) {
          doView.css = Object.assign({}, ...doView.css, ...newVal.css);
        } else if (Array.isArray(doView.css)) {
          doView.css = Object.assign({}, ...doView.css, newVal.css);
        } else if (Array.isArray(newVal.css)) {
          doView.css = Object.assign({}, doView.css, ...newVal.css);
        } else {
          doView.css = Object.assign({}, doView.css, newVal.css);
        }
      }
      if (newVal && newVal.rect) {
        doView.rect = newVal.rect;
      }
      if (newVal && newVal.url && doView.url && newVal.url !== doView.url) {
        downloader
          .download(newVal.url, this.properties.LRU)
          .then(path => {
            if (newVal.url.startsWith('https')) {
              doView.originUrl = newVal.url;
            }
            doView.url = path;
            wx.getImageInfo({
              src: path,
              success: res => {
                doView.sHeight = res.height;
                doView.sWidth = res.width;
                this.reDraw(doView, callback, isMoving);
              },
              fail: () => {
                this.reDraw(doView, callback, isMoving);
              },
            });
          })
          .catch(error => {
            // 未下载成功，直接绘制
            console.error(error);
            this.reDraw(doView, callback, isMoving);
          });
      } else {
        newVal && newVal.text && doView.text && newVal.text !== doView.text && (doView.text = newVal.text);
        newVal &&
          newVal.content &&
          doView.content &&
          newVal.content !== doView.content &&
          (doView.content = newVal.content);
        this.reDraw(doView, callback, isMoving);
      }
    },

    reDraw(doView, callback, isMoving) {
      const draw = {
        width: this.currentPalette.width,
        height: this.currentPalette.height,
        views: this.isEmpty(doView) ? [] : [doView],
      };
      const pen = new Pen(this.globalContext, draw);

      pen.paint(callbackInfo => {
        callback && callback(callbackInfo);
        this.triggerEvent('viewUpdate', {
          view: this.touchedView,
        });
      });

      const { rect, css, type } = doView;

      this.block = {
        width: this.currentPalette.width,
        height: this.currentPalette.height,
        views: this.isEmpty(doView) ? [] : [this.getBox(rect, doView.type)],
      };
      if (css && css.scalable) {
        this.block.views.push(this.getScaleIcon(rect, type));
      }
      if (css && css.deletable) {
        this.block.views.push(this.getDeleteIcon(rect));
      }
      const topBlock = new Pen(this.frontContext, this.block);
      topBlock.paint();
    },

    isInView(x, y, rect) {
      return x > rect.left && y > rect.top && x < rect.right && y < rect.bottom;
    },

    isInDelete(x, y) {
      for (const view of this.block.views) {
        if (view.id === 'delete') {
          return x > view.rect.left && y > view.rect.top && x < view.rect.right && y < view.rect.bottom;
        }
      }
      return false;
    },

    isInScale(x, y) {
      for (const view of this.block.views) {
        if (view.id === 'scale') {
          return x > view.rect.left && y > view.rect.top && x < view.rect.right && y < view.rect.bottom;
        }
      }
      return false;
    },

    touchedView: {},
    findedIndex: -1,
    onClick() {
      const x = this.startX;
      const y = this.startY;
      const totalLayerCount = this.currentPalette.views.length;
      let canBeTouched = [];
      let isDelete = false;
      let deleteIndex = -1;
      for (let i = totalLayerCount - 1; i >= 0; i--) {
        const view = this.currentPalette.views[i];
        const { rect } = view;
        if (this.touchedView && this.touchedView.id && this.touchedView.id === view.id && this.isInDelete(x, y, rect)) {
          canBeTouched.length = 0;
          deleteIndex = i;
          isDelete = true;
          break;
        }
        if (this.isInView(x, y, rect)) {
          canBeTouched.push({
            view,
            index: i,
          });
        }
      }
      this.touchedView = {};
      if (canBeTouched.length === 0) {
        this.findedIndex = -1;
      } else {
        let i = 0;
        const touchAble = canBeTouched.filter(item => Boolean(item.view.id));
        if (touchAble.length === 0) {
          this.findedIndex = canBeTouched[0].index;
        } else {
          for (i = 0; i < touchAble.length; i++) {
            if (this.findedIndex === touchAble[i].index) {
              i++;
              break;
            }
          }
          if (i === touchAble.length) {
            i = 0;
          }
          this.touchedView = touchAble[i].view;
          this.findedIndex = touchAble[i].index;
          this.triggerEvent('viewClicked', {
            view: this.touchedView,
          });
        }
      }
      if (this.findedIndex < 0 || (this.touchedView && !this.touchedView.id)) {
        // 证明点击了背景 或无法移动的view
        this.frontContext.draw();
        if (isDelete) {
          this.triggerEvent('touchEnd', {
            view: this.currentPalette.views[deleteIndex],
            index: deleteIndex,
            type: 'delete',
          });
          this.doAction();
        } else if (this.findedIndex < 0) {
          this.triggerEvent('viewClicked', {});
        }
        this.findedIndex = -1;
        this.prevFindedIndex = -1;
      } else if (this.touchedView && this.touchedView.id) {
        this.sliceLayers();
      }
    },

    sliceLayers() {
      const bottomLayers = this.currentPalette.views.slice(0, this.findedIndex);
      const topLayers = this.currentPalette.views.slice(this.findedIndex + 1);
      const bottomDraw = {
        width: this.currentPalette.width,
        height: this.currentPalette.height,
        background: this.currentPalette.background,
        views: bottomLayers,
      };
      const topDraw = {
        width: this.currentPalette.width,
        height: this.currentPalette.height,
        views: topLayers,
      };
      if (this.prevFindedIndex < this.findedIndex) {
        new Pen(this.bottomContext, bottomDraw).paint();
        this.doAction();
        new Pen(this.topContext, topDraw).paint();
      } else {
        new Pen(this.topContext, topDraw).paint();
        this.doAction();
        new Pen(this.bottomContext, bottomDraw).paint();
      }
      this.prevFindedIndex = this.findedIndex;
    },

    startX: 0,
    startY: 0,
    startH: 0,
    startW: 0,
    isScale: false,
    startTimeStamp: 0,
    onTouchStart(event) {
      if (this.isDisabled) {
        return;
      }
      const { x, y } = event.touches[0];
      this.startX = x;
      this.startY = y;
      this.startTimeStamp = new Date().getTime();
      if (this.touchedView && !this.isEmpty(this.touchedView)) {
        const { rect } = this.touchedView;
        if (this.isInScale(x, y, rect)) {
          this.isScale = true;
          this.startH = rect.bottom - rect.top;
          this.startW = rect.right - rect.left;
        } else {
          this.isScale = false;
        }
      } else {
        this.isScale = false;
      }
    },

    onTouchEnd(e) {
      if (this.isDisabled) {
        return;
      }
      const current = new Date().getTime();
      if (current - this.startTimeStamp <= 500 && !this.hasMove) {
        !this.isScale && this.onClick(e);
      } else if (this.touchedView && !this.isEmpty(this.touchedView)) {
        this.triggerEvent('touchEnd', {
          view: this.touchedView,
        });
      }
      this.hasMove = false;
    },

    onTouchCancel(e) {
      if (this.isDisabled) {
        return;
      }
      this.onTouchEnd(e);
    },

    hasMove: false,
    onTouchMove(event) {
      if (this.isDisabled) {
        return;
      }
      this.hasMove = true;
      if (!this.touchedView || (this.touchedView && !this.touchedView.id)) {
        return;
      }
      const { x, y } = event.touches[0];
      const offsetX = x - this.startX;
      const offsetY = y - this.startY;
      const { rect, type } = this.touchedView;
      let css = {};
      if (this.isScale) {
        clearPenCache(this.touchedView.id);
        const newW = this.startW + offsetX > 1 ? this.startW + offsetX : 1;
        if (this.touchedView.css && this.touchedView.css.minWidth) {
          if (newW < this.touchedView.css.minWidth.toPx()) {
            return;
          }
        }
        if (this.touchedView.rect && this.touchedView.rect.minWidth) {
          if (newW < this.touchedView.rect.minWidth) {
            return;
          }
        }
        const newH = this.startH + offsetY > 1 ? this.startH + offsetY : 1;
        css = {
          width: `${newW}px`,
        };
        if (type !== 'text') {
          if (type === 'image') {
            css.height = `${(newW * this.startH) / this.startW}px`;
          } else {
            css.height = `${newH}px`;
          }
        }
      } else {
        this.startX = x;
        this.startY = y;
        css = {
          left: `${rect.x + offsetX}px`,
          top: `${rect.y + offsetY}px`,
          right: undefined,
          bottom: undefined,
        };
      }
      this.doAction(
        {
          view: {
            css,
          },
        },
        null,
        !this.isScale,
      );
    },

    initScreenK() {
      if (!(getApp() && getApp().systemInfo && getApp().systemInfo.screenWidth)) {
        try {
          getApp().systemInfo = wx.getSystemInfoSync();
        } catch (e) {
          console.error(`Painter get system info failed, ${JSON.stringify(e)}`);
          return;
        }
      }
      this.screenK = 0.5;
      if (getApp() && getApp().systemInfo && getApp().systemInfo.screenWidth) {
        this.screenK = getApp().systemInfo.screenWidth / 750;
      }
      setStringPrototype(this.screenK, this.properties.scaleRatio);
    },

    initDancePalette() {
      if (this.properties.use2D) {
        return;
      }
      this.isDisabled = true;
      this.initScreenK();
      this.downloadImages(this.properties.dancePalette).then(async palette => {
        this.currentPalette = palette;
        const { width, height } = palette;

        if (!width || !height) {
          console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
          return;
        }
        this.setData({
          painterStyle: `width:${width.toPx()}px;height:${height.toPx()}px;`,
        });
        this.frontContext || (this.frontContext = await this.getCanvasContext(this.properties.use2D, 'front'));
        this.bottomContext || (this.bottomContext = await this.getCanvasContext(this.properties.use2D, 'bottom'));
        this.topContext || (this.topContext = await this.getCanvasContext(this.properties.use2D, 'top'));
        this.globalContext || (this.globalContext = await this.getCanvasContext(this.properties.use2D, 'k-canvas'));
        new Pen(this.bottomContext, palette, this.properties.use2D).paint(() => {
          this.isDisabled = false;
          this.isDisabled = this.outterDisabled;
          this.triggerEvent('didShow');
        });
        this.globalContext.draw();
        this.frontContext.draw();
        this.topContext.draw();
      });
      this.touchedView = {};
    },

    startPaint() {
      this.initScreenK();
      const { width, height } = this.properties.palette;
      console.log("startPaint width: ",width)
      if (!width || !height) {
        console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
        return;
      }

      let needScale = false;
      // 生成图片时，根据设置的像素值重新绘制
      if (width.toPx() !== this.canvasWidthInPx) {
        console.log("this ..............")
        console.log("width.toPx(): ", width.toPx())
        console.log("this.canvasWidthInPx: ", this.canvasWidthInPx)

        this.canvasWidthInPx = width.toPx();
        needScale = this.properties.use2D;
      }

      console.log("this.properties.widthPixels: ", this.properties.widthPixels)
      if (this.properties.widthPixels) {
        console.log("that ......")
        setStringPrototype(this.screenK, this.properties.widthPixels / this.canvasWidthInPx);
        this.canvasWidthInPx = this.properties.widthPixels;
      }

      if (this.canvasHeightInPx !== height.toPx()) {
        this.canvasHeightInPx = height.toPx();
        needScale = needScale || this.properties.use2D;
      }
      this.setData(
        {
          photoStyle: `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`,
        },
        function () {
          this.downloadImages(this.properties.palette).then(async palette => {
            if (!this.photoContext) {
              this.photoContext = await this.getCanvasContext(this.properties.use2D, 'photo');
            }
            if (needScale) {
              const scale = getApp().systemInfo.pixelRatio;
              this.photoContext.width = this.canvasWidthInPx * scale;
              this.photoContext.height = this.canvasHeightInPx * scale;
              this.photoContext.scale(scale, scale);
            }
            new Pen(this.photoContext, palette).paint(() => {
              this.saveImgToLocal();
            });
            setStringPrototype(this.screenK, this.properties.scaleRatio);
          });
        },
      );
    },

    downloadImages(palette) {
      return new Promise((resolve, reject) => {
        let preCount = 0;
        let completeCount = 0;
        const paletteCopy = JSON.parse(JSON.stringify(palette));
        if (paletteCopy.background) {
          preCount++;
          downloader.download(paletteCopy.background, this.properties.LRU).then(
            path => {
              paletteCopy.background = path;
              completeCount++;
              if (preCount === completeCount) {
                resolve(paletteCopy);
              }
            },
            () => {
              completeCount++;
              if (preCount === completeCount) {
                resolve(paletteCopy);
              }
            },
          );
        }
        if (paletteCopy.views) {
          for (const view of paletteCopy.views) {
            if (view && view.type === 'image' && view.url) {
              preCount++;
              /* eslint-disable no-loop-func */
              downloader.download(view.url, this.properties.LRU).then(
                path => {
                  view.originUrl = view.url;
                  view.url = path;
                  wx.getImageInfo({
                    src: path,
                    success: res => {
                      // 获得一下图片信息，供后续裁减使用
                      view.sWidth = res.width;
                      view.sHeight = res.height;
                    },
                    fail: error => {
                      // 如果图片坏了，则直接置空，防止坑爹的 canvas 画崩溃了
                      console.warn(`getImageInfo ${view.originUrl} failed, ${JSON.stringify(error)}`);
                      view.url = '';
                    },
                    complete: () => {
                      completeCount++;
                      if (preCount === completeCount) {
                        resolve(paletteCopy);
                      }
                    },
                  });
                },
                () => {
                  completeCount++;
                  if (preCount === completeCount) {
                    resolve(paletteCopy);
                  }
                },
              );
            }
          }
        }
        if (preCount === 0) {
          resolve(paletteCopy);
        }
      });
    },

    saveImgToLocal() {
      const that = this;
      const optionsOf2d = {
        canvas: that.canvasNode,
      }
      const optionsOfOld = {
        canvasId: 'photo',
        destWidth: that.canvasWidthInPx,
        destHeight: that.canvasHeightInPx,
      }
      setTimeout(() => {
        wx.canvasToTempFilePath(
          {
            ...(that.properties.use2D ? optionsOf2d : optionsOfOld),
            success: function (res) {
              that.getImageInfo(res.tempFilePath);
            },
            fail: function (error) {
              console.error(`canvasToTempFilePath failed, ${JSON.stringify(error)}`);
              that.triggerEvent('imgErr', {
                error: error,
              });
            },
          },
          this,
        );
      }, 300);
    },

    getCanvasContext(use2D, id) {
      const that = this;
      return new Promise(resolve => {
        if (use2D) {
          const query = wx.createSelectorQuery().in(that);
          const selectId = `#${id}`;
          query
            .select(selectId)
            .fields({ node: true, size: true })
            .exec(res => {
              that.canvasNode = res[0].node;
              const ctx = that.canvasNode.getContext('2d');
              const wxCanvas = new WxCanvas('2d', ctx, id, true, that.canvasNode);
              resolve(wxCanvas);
            });
        } else {
          const temp = wx.createCanvasContext(id, that);
          resolve(new WxCanvas('mina', temp, id, true));
        }
      });
    },

    getImageInfo(filePath) {
      const that = this;
      wx.getImageInfo({
        src: filePath,
        success: infoRes => {
          if (that.paintCount > MAX_PAINT_COUNT) {
            const error = `The result is always fault, even we tried ${MAX_PAINT_COUNT} times`;
            console.error(error);
            that.triggerEvent('imgErr', {
              error: error,
            });
            return;
          }
          // 比例相符时才证明绘制成功，否则进行强制重绘制
          if (
            Math.abs(
              (infoRes.width * that.canvasHeightInPx - that.canvasWidthInPx * infoRes.height) /
                (infoRes.height * that.canvasHeightInPx),
            ) < 0.01
          ) {
            that.triggerEvent('imgOK', {
              path: filePath,
            });
          } else {
            that.startPaint();
          }
          that.paintCount++;
        },
        fail: error => {
          console.error(`getImageInfo failed, ${JSON.stringify(error)}`);
          that.triggerEvent('imgErr', {
            error: error,
          });
        },
      });
    },
  },
});

function setStringPrototype(screenK, scale) {
  /* eslint-disable no-extend-native */
  /**
   * string 到对应的 px
   * @param {Number} baseSize 当设置了 % 号时，设置的基准值
   */
  String.prototype.toPx = function toPx(_, baseSize) {
    if (this === '0') {
      return 0;
    }
    const REG = /-?[0-9]+(\.[0-9]+)?(rpx|px|%)/;

    const parsePx = origin => {
      const results = new RegExp(REG).exec(origin);
      if (!origin || !results) {
        console.error(`The size: ${origin} is illegal`);
        return 0;
      }
      const unit = results[2];
      const value = parseFloat(origin);

      let res = 0;
      if (unit === 'rpx') {
        res = Math.round(value * (screenK || 0.5) * (scale || 1));
      } else if (unit === 'px') {
        res = Math.round(value * (scale || 1));
      } else if (unit === '%') {
        res = Math.round((value * baseSize) / 100);
      }
      return res;
    };
    const formula = /^calc\((.+)\)$/.exec(this);
    if (formula && formula[1]) {
      // 进行 calc 计算
      const afterOne = formula[1].replace(/([^\s\(\+\-\*\/]+)\.(left|right|bottom|top|width|height)/g, word => {
        const [id, attr] = word.split('.');
        return penCache.viewRect[id][attr];
      });
      const afterTwo = afterOne.replace(new RegExp(REG, 'g'), parsePx);
      return calc(afterTwo);
    } else {
      return parsePx(this);
    }
  };
}
