<template>
  <view
    class="u-poster"
    :style="[customStyle, { position: 'relative' }, painterStyle]"
  >
    <block v-if="!use2D">
      <canvas
        canvas-id="photo"
        style="position: absolute; left: -9999px; top: -9999rpx"
        :style="photoStyle"
      />
    </block>
    <block v-else>
      <canvas type="2d" id="photo" :style="photoStyle" />
    </block>
  </view>
</template>

<script>
import Pen, { penCache, clearPenCache } from './lib/pen'
import WxCanvas from './lib/wx-canvas'
const app = getApp()

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

function setStringPrototype(screenK, scale) {
  /* eslint-disable no-extend-native */
  /**
   * string 到对应的 px
   * @param {Number} baseSize 当设置了 % 号时，设置的基准值
   */
  Number.prototype.toPx = 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' || unit === '') {
        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)
    }
  }
}

/**
 * painter 画板
 * @description 此组件一般用于生成海报
 * @description 基于Painter的二次改造，感谢作者的辛苦付出
 * @tutorial https://github.com/Kujiale-Mobile/Painter
 * @property {String} color 线条的颜色(默认#e4e7ed)
 * @example <u-poster ref="poster"></u-poster>
 */
export default {
  name: 'u-poster',

  props: {
    use2D: {
      type: Boolean,
      default: false,
    },
    customStyle: {
      type: Object,
      default() {
        return {}
      },
    },
    // 运行自定义选择框和删除缩放按钮
    customActionStyle: {
      type: Object,
      default() {
        return {}
      },
    },
    config: {
      type: Object,
      default() {
        return {}
      },
    },
    // 缩放比，会在传入的 config 中统一乘以该缩放比
    scaleRatio: {
      type: Number,
      default: 1,
    },
    widthPixels: {
      type: Number,
      default: 0,
    },
    // 启用脏检查，默认 false
    dirty: {
      type: Boolean,
      default: false,
    },
    LRU: {
      type: Boolean,
      default: false,
    },
  },

  data() {
    this.imageTempFiles = {}

    this.canvasWidthInPx = 0
    this.canvasHeightInPx = 0
    this.canvasNode = null
    this.paintCount = 0

    return {
      photoStyle: '',
      painterStyle: '',
    }
  },

  computed: {},

  watch: {
    config(newVal, oldVal) {
      if (this.isNeedRefresh(newVal, oldVal)) {
        this.paintCount = 0
        clearPenCache()
        this.startPaint()
      }
    },
  },

  created() {},

  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.dirty && util.equal(newVal, oldVal))
      ) {
        return false
      }
      return true
    },

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

    startPaint() {
      this.initScreenK()
      const { width, height } = this.config

      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) {
        this.canvasWidthInPx = width.toPx()
        needScale = this.use2D
      }
      if (this.widthPixels) {
        setStringPrototype(
          this.screenK,
          this.widthPixels / this.canvasWidthInPx
        )
        this.canvasWidthInPx = this.widthPixels
      }

      if (this.canvasHeightInPx !== height.toPx()) {
        this.canvasHeightInPx = height.toPx()
        needScale = needScale || this.use2D
      }
      this.photoStyle = `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`
      this.downloadImages(this.config).then(async (config) => {
        if (!this.photoContext) {
          this.photoContext = await this.getCanvasContext(this.use2D, 'photo')
        }
        if (needScale) {
          const scale = app.systemInfo.pixelRatio
          this.photoContext.width = this.canvasWidthInPx * scale
          this.photoContext.height = this.canvasHeightInPx * scale
          this.photoContext.scale(scale, scale)
        }

        new Pen(this.photoContext, config).paint(() => {
          this.saveImgToLocal()
          this.$emit('ready', this.photoContext.ctx, this.photoContext)
        })
        setStringPrototype(this.screenK, this.scaleRatio)
      })
    },

    getImageInfo(url) {
      return new Promise((resolve, reject) => {
        if (this.imageTempFiles[url]) {
          resolve({ url, path: url, res: this.imageTempFiles[url] })
          return
        }
        if (!util.isLocalUrl(url) && !util.isValidUrl(url)) {
          resolve({ url, path: url, res: null })
          return
        }
        uni.getImageInfo({
          src: url,
          success: (res) => {
            this.imageTempFiles[url] = res

            if (!(url && util.isValidUrl(url))) {
              resolve({ url, path: url, res })
            } else {
              resolve({ url, path: res.path, res })
            }
          },
          fail: (err) => {
            reject(err)
          },
        })
      })
    },

    downloadImages(config) {
      return new Promise((resolve, reject) => {
        let preCount = 0
        let completeCount = 0
        const configCopy = JSON.parse(JSON.stringify(config))
        if (configCopy.background) {
          preCount++
          this.getImageInfo(configCopy.background).then(
            ({ path }) => {
              configCopy.background = path
              completeCount++
              if (preCount === completeCount) {
                resolve(configCopy)
              }
            },
            () => {
              completeCount++
              if (preCount === completeCount) {
                resolve(configCopy)
              }
            }
          )
        }
        if (configCopy.views) {
          for (const view of configCopy.views) {
            if (view && view.type === 'image' && view.url) {
              preCount++
              this.getImageInfo(view.url).then(
                ({ path, res }) => {
                  view.path = path
                  view.sWidth = res.width
                  view.sHeight = res.height

                  completeCount++
                  if (preCount === completeCount) {
                    resolve(configCopy)
                  }
                },
                (error) => {
                  // 如果图片坏了，则直接置空，防止坑爹的 canvas 画崩溃了
                  console.warn(
                    `getImageInfo ${view.url} failed, ${JSON.stringify(error)}`
                  )
                  view.path = ''
                  completeCount++
                  if (preCount === completeCount) {
                    resolve(configCopy)
                  }
                }
              )
            }
          }
        }
        if (preCount === 0) {
          resolve(configCopy)
        }
      })
    },

    saveImgToLocal(drawType = 'default') {
      const that = this
      setTimeout(() => {
        uni.canvasToTempFilePath(
          {
            canvasId: 'photo',
            canvas: that.use2D ? that.canvasNode : null,
            destWidth: that.canvasWidthInPx,
            destHeight: that.canvasHeightInPx,
            success: function (res) {
              that.$emit('success', {
                type: drawType,
                path: res.tempFilePath,
              })
            },
            fail: function (error) {
              console.error(
                `canvasToTempFilePath failed, ${JSON.stringify(error)}`
              )
              that.$emit('fail', {
                error: error,
              })
            },
          },
          this
        )
      }, 300)
    },

    getCanvasContext(use2D = this.use2D, id = 'photo') {
      const that = this
      return new Promise((resolve) => {
        if (use2D) {
          const query = uni.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 = uni.createCanvasContext(id, that)
          resolve(new WxCanvas('mina', temp, id, true))
        }
      })
    },

    draw(reserve = true) {
      this.photoContext.draw(reserve, () => {
        this.saveImgToLocal('custom')
      })
    },
  },
}
</script>

<style scoped lang="scss">
@import '../../libs/css/style.components.scss';

.u-poster-canvas {
  // position: fixed;
  // top: -1000000px;
  // left: -1000000px;
}
</style>
