const PI_3_2 = Math.PI * 1.5;
const PI_1_2 = Math.PI * 0.5;

const mergeProps = function(newVal, oldVal) {
  return { ...oldVal,
    ...newVal
  }
}

const defaultObjectProps = {
  indicatorTextStyle: {
    type: Object,
    value: {
      show: false,
      size: 12,
      color: '#666',
      text: ''
    }
  },
  indicatorValueStyle: {
    type: Object,
    value: {
      show: false,
      size: 18,
      color: '#4575e8'
    }
  },
  indicatorCircleStyle: {
    type: Object,
    value: {
      show: false,
      bgColor: '#00b800',
      r: 9,
      borderRadius: 3,
      borderColor: '#fff'
    }
  },
  scaleTextStyle: {
    type: Object,
    value: {
      show: false,
      size: 16,
      color: '#f0f0f0'
    }
  }
}

// components/gauge.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    width: {
      type: Number,
      value: 750
    },
    height: {
      type: Number,
      value: 350
    },
    gaugeid: {
      type: String,
      value: 'gauge' + Math.random()
    },
    r: {
      type: Number,
      value: 95
    },

    startAngle: {
      type: Number,
      // value: 90 / 90 * Math.PI,
      value: 80 / 90 * Math.PI,
    },
    endAngle: {
      type: Number,
      // value: 180 / 90 * Math.PI,
      value: 10 / 90 * Math.PI,
    },
    indicatorBgColor: {
      type: [Array, String],
      value: [{
          progress: 0,
          value: null
        },
        {
          progress: 0.6,
          value: null
        },
        {
          progress: 1,
          value: null
        }
      ]
    },

    bgColor: {
      type: String,
      value: null,
    },
    bgWidth: {
      type: Number,
      value: 15,
    },
    min: {
      type: Number,
      value: 0,
    },
    max: {
      type: Number,
      value: 100,
    },
    value: {
      type: Number,
      value: null,
      observer: function(newVal, oldVal, changedPath) {
        this.setData({
            value: newVal
          },
          function() {
            this.drawGauge(this.canvasId, this.x, this.y)
          }
        )
      }
    },
    animateMsec: {
      type: Number,
      value: 0,
    },
    indicatorText: {
      type: String,
      value: ''
    },
    scale: {
      type: Array,
      value: [
        0, 100
      ]
    },
    ...defaultObjectProps
  },

  /**
   * 组件的初始数据
   */
  data: {
    currentValue: 0
  },

  /**
   * 组件的方法列表
   */
  methods: {
    getPoint: function(x, y, r, angle) {
      const x1 = x + r * Math.cos(angle);
      const y1 = y + r * Math.sin(angle);
      return {
        x: x1,
        y: y1
      }
      // }
    },
    /**
     * 绘制圆圈
     * @params {CanvasContext} canvas context
     * @params {Object} 组件配置文件
     */
    _drawCircle: function(ctx, cfg) {
      ctx.beginPath()
      const config = cfg
      const innerCircleRadius = config.r - config.bgWidth;
      // ctx.moveTo(config.x,config.y)
      // 外圈
      ctx.arc(config.x, config.y, config.r, config.startAngle, config.endAngle)
      // 外圈结束坐标
      const outEndPoint = this.getPoint(config.x, config.y, config.r, config.endAngle)

      // 内圈结束坐标
      const innerEndPoint = this.getPoint(config.x, config.y, innerCircleRadius, config.endAngle)
      // 结束位置小圆圈坐标
      const endCirclePoint = {
        x: (outEndPoint.x + innerEndPoint.x) / 2,
        y: (outEndPoint.y + innerEndPoint.y) / 2,
      }

      // 结束位置圆弧
      ctx.arc(endCirclePoint.x, endCirclePoint.y, config.bgWidth / 2, config.endAngle, config.endAngle + Math.PI)
      // 内圈圆弧
      ctx.arc(config.x, config.y, innerCircleRadius, config.endAngle, config.startAngle, true)

      // 外圈开始位置坐标
      const outStartPoint = this.getPoint(config.x, config.y, config.r, config.startAngle)

      // 内圈开始位置坐标
      const innerStartPoint = this.getPoint(config.x, config.y, innerCircleRadius, config.startAngle)

      // 开始位置小圆圈坐标
      const startCirclePoint = {
        x: (outStartPoint.x + innerStartPoint.x) / 2,
        y: (outStartPoint.y + innerStartPoint.y) / 2,
      }
      // 开始位置小圆圈坐标
      // 开始位置圆弧
      ctx.moveTo(outStartPoint.x, outStartPoint.y)
      ctx.arc(startCirclePoint.x, startCirclePoint.y, config.bgWidth / 2, config.startAngle, config.startAngle - Math.PI, true)
      // 设置填充渐变色
      // 当背景颜色是数组时，设置为渐变色
      const {
        backgroundColor
      } = config

      if (Array.isArray(backgroundColor)) {
        const fillGrd = ctx.createLinearGradient(innerStartPoint.x, innerStartPoint.y, innerEndPoint.x, innerEndPoint.y);
        const {
          length
        } = config.backgroundColor
        for (let i = 0; i < length; i++) {
          const bg = backgroundColor[i]
          fillGrd.addColorStop(bg.progress, bg.value || '#32d900')
        }
        ctx.setFillStyle(fillGrd)
      } else {
        ctx.setFillStyle(config.backgroundColor)
      }
      ctx.closePath()
      ctx.fill()
    },
    _animate: function(func) {
      const {
        animateMsec
      } = this.data
      if (animateMsec === 0) {
        return func(1)
      }
      const startTime = Date.now();
      const endTime = startTime + animateMsec;
      // const traceTime = endTime - startTime;
      let timeOutId;
      const animateFunc = function() {
        const curTime = Date.now();
        const percent = (curTime - startTime) / animateMsec;
        if (percent >= 1) {
          func(1);
          // timeOutId && clearTimeout(timeOutId);
          return
        }
        func(percent)
        timeOutId = setTimeout(function() {
          animateFunc();
        }, 16)
      }
      animateFunc();
    },
    _drawBackground: function(ctx, config) {
      const newCfg = {
        ...config,
        backgroundColor: config.bgColor
      }
      delete newCfg.bgColor
      this._drawCircle(ctx, newCfg)
    },
    /**
     * 绘制指示器圆圈
     */
    _drawIndicator: function(ctx, value = 0, config) {
      let {
        startAngle,
        endAngle,
        min,
        max
      } = config
      if (endAngle <= startAngle) {
        endAngle += 2 * Math.PI
      }
      const currentAngle = (value / (max - min)) * (endAngle - startAngle) + startAngle
      const newCfg = {
        ...config,
        backgroundColor: config.indicatorBgColor,
        endAngle: currentAngle,
      }
      this._drawCircle(ctx, newCfg)
    },
    _drawIndicatorValue: function(ctx, text, config) {
      const {
        x,
        y,
        indicatorValueStyle
      } = config
      const {
        size = 25,
          color = '#1AAD16'
      } = indicatorValueStyle
      ctx.setFillStyle(color)
      // 以下精度可以加接口控制
      ctx.setFontSize(size)
      ctx.setTextAlign('center')
      ctx.fillText((!text||text==0)?'':Number.prototype.toFixed.call(text, 0), x, y)
    },
    _drawIndicatorText: function(ctx, config) {
      const {
        x,
        y,
        indicatorTextStyle
      } = config
      const {
        size,
        color,
        text
      } = mergeProps(indicatorTextStyle, defaultObjectProps.indicatorTextStyle.value)
      ctx.save()
      ctx.setFillStyle(color)
      // 以下精度可以加接口控制
      ctx.setFontSize(size)
      ctx.setTextAlign('center')
      const mergedIndicatorValueStyle = mergeProps(config.indicatorValueStyle, defaultObjectProps.indicatorValueStyle.value)
      ctx.fillText(text, x, y - 5 - mergedIndicatorValueStyle.size)
    },
    _drawIndicatorScale: function(ctx, config) {
      const {
        bgWidth,
        scale,
        r,
        x,
        y,
        min,
        max,
        scaleTextStyle
      } = config;
      let {
        startAngle,
        endAngle,
      } = config
      if (endAngle <= startAngle) {
        endAngle += Math.PI * 2
      }
      const len = scale.length;
      const {
        size,
        color
      } = mergeProps(scaleTextStyle, defaultObjectProps.scaleTextStyle.value);
      ctx.setFillStyle(color)
      // 以下精度可以加接口控制
      ctx.setFontSize(size)
      ctx.setTextAlign('center')
      for (let i = 0; i < len; i++) {
        const value = scale[i]
        let angle = (value / (max - min)) * (endAngle - startAngle) + startAngle
        if (angle >= Math.PI * 2) {
          angle = angle - Math.PI * 2
        }
        const point = this.getPoint(x, y, r - bgWidth - size - 5, angle);

        ctx.save()
        ctx.translate(point.x, point.y)
        const rotateDegrees = angle >= PI_3_2 ? (angle - PI_3_2) : (angle + PI_1_2);
        ctx.rotate(rotateDegrees)
        ctx.fillText(value, 0, 0)
        ctx.restore()
      }
    },
    /**
     * 绘制终点圆圈指示器
     */
    _drawIndicatorCircle: function(ctx, value = 0, config) {
      ctx.beginPath()
      const {
        indicatorCircleStyle,
        x,
        y,
        r,
        max,
        min,
        bgWidth
      } = config;
      let {
        startAngle,
        endAngle,
      } = config
      if (endAngle <= startAngle) {
        endAngle += 2 * Math.PI
      }
      const currentAngle = (value / (max - min)) * (endAngle - startAngle) + startAngle
      const outPoint = this.getPoint(x, y, r, currentAngle);
      const innerPoint = this.getPoint(x, y, r - bgWidth, currentAngle);
      const point = {
        x: (outPoint.x + innerPoint.x) / 2,
        y: (outPoint.y + innerPoint.y) / 2,
      }
      ctx.moveTo(point.x, point.y)
      let mergedIndicatorCircleStyle
      const {
        bgColor,
        borderRadius,
        borderColor
      } = mergedIndicatorCircleStyle = mergeProps(indicatorCircleStyle, defaultObjectProps.indicatorCircleStyle.value)
      // 边框
      if (!isNaN(borderRadius) && borderRadius !== 0) {
        const outR = mergedIndicatorCircleStyle.r + borderRadius
        ctx.arc(point.x, point.y, outR, 0, 2 * Math.PI);
        if (Array.isArray(borderColor)) {
          const fillGrd = ctx.createCircularGradient(point.x, point.y, outR);
          const {
            length
          } = borderColor
          for (let i = 0; i < length; i++) {
            const bg = borderColor[i]
            fillGrd.addColorStop(bg.progress, bg.value || '#32d900')
          }
          ctx.setFillStyle(fillGrd)
        } else {
          ctx.setFillStyle(borderColor)
        }
        ctx.closePath()
        ctx.fill()
      }
      // 内圈指示器
      ctx.beginPath()
      ctx.arc(point.x, point.y, mergedIndicatorCircleStyle.r, 0, 2 * Math.PI);
      ctx.setFillStyle(bgColor)
      ctx.closePath()
      ctx.fill()

    },
    drawGauge: function(canvasId, x, y) {
      const ctx = wx.createCanvasContext(canvasId, this);
      this.ctx = ctx;
      const config = {
        x,
        y,
        ...this.properties
      }
      this._animate(this._drawGaugeWithAnimate.bind(this, config))
    },
    _drawGaugeWithAnimate: function(config, percent) {
      const {
        ctx
      } = this
      const {
        value,
        min
      } = this.data;
      const {
        indicatorTextStyle,
        indicatorValueStyle,
        indicatorCircleStyle,
        scaleTextStyle
      } = this.properties
      const animateValue = min + (value - min) * percent;
      this._drawBackground(ctx, config)
      this._drawIndicator(ctx, animateValue, config)
      if (scaleTextStyle.show) {
        this._drawIndicatorScale(ctx, config)
      }
      if (indicatorTextStyle.show) {
        this._drawIndicatorText(ctx, config)
      }
      if (indicatorCircleStyle.show) {
        this._drawIndicatorCircle(ctx, animateValue, config)
      }
      if (indicatorValueStyle.show) {
        this._drawIndicatorValue(ctx, animateValue, config)
      }
      ctx.draw()
    }
  },

  ready: function(opt) {
    const canvasId = 'gauge_' + this.data.gaugeid;
    const that = this;

    let x = 187;
    let y = 187;
    wx.getSystemInfo({
      success: function(res) {
        const {
          screenWidth,
          screenHeight
        } = res;
        const rpxTopx = screenWidth / 750;
        const {
          width,
          height
        } = that.data
        that.x = x = width * rpxTopx / 2;
        that.y = y = height * rpxTopx / 2;
        that.canvasId = canvasId
        that.drawGauge(canvasId, x, y)
      },
    })
  }
})