export default {
  data () {
    return {
      defaultHorizon: false,
      defaultXAxisFS: 10,
      defaultYAxisFS: 10,
      defaultXOffset: 30,
      defaultRotateAngel: 30,

      defaultXAxisLineColor: 'rgba(255, 255, 255, 0.3)',
      defaultYAxisLineColor: 'rgba(255, 255, 255, 0.3)',
      defaultGridColor: 'rgba(255, 255, 255, 0.3)',
      defaultGridType: 'line',
      defaultAxisLabelGap: 5,

      horizon: false,
      // user data's max and min value
      valueMaxMin: [],
      // axis's max and min value
      axisMaxMin: [],
      // label's max width where x and y Axis
      labelXYMaxWidth: [],
      valueAxisTag: [],
      labelAxisTag: [],
      xyAxisFS: [],
      xyAxisUnitWidth: [],
      axisMargin: [],
      axisOriginPos: [],
      axisWH: [],
      valueTagPos: [],
      labelTagPos: [],
      labelAxisPos: [],
      valueTagGap: 0,
      labelTagGap: 0,
      xTagColor: '',
      yTagColor: '',
      xTagColorMul: false,
      yTagColorMul: false,
      xGridColor: '',
      yGridColor: '',
      xGridColorMul: false,
      yGridColorMul: false
    }
  },
  methods: {
    initAxis () {
      const { calcMaxMinValue, calcValueAxisTag, calcLabelAxisTag } = this

      calcMaxMinValue()

      calcValueAxisTag()

      calcLabelAxisTag()

      const { calcXYAxisFS, calcXYLabelMaxWidth, calcAxisMargin } = this

      calcXYAxisFS()

      calcXYLabelMaxWidth()

      calcAxisMargin()

      const { calcAxisOriginPos, calcAxisWH, calcValueTagPos } = this

      calcAxisOriginPos()

      calcAxisWH()

      calcValueTagPos()

      const { calcLabelTagPos, calcTagGap, calcTagColor } = this

      calcLabelTagPos()

      calcTagGap()

      calcTagColor()

      const { calcGridColor } = this

      calcGridColor()
    },
    calcMaxMinValue () {
      const { data: { data }, multipleSum, filterNull, getArrayMaxMin } = this

      const valueData = data.map(item => item.data)

      const trueValue = valueData.map(item =>
        item.map(v =>
          v ? (typeof v === 'number' ? v : multipleSum(...filterNull(v))) : false))

      this.valueMaxMin = getArrayMaxMin(trueValue)
    },
    calcValueAxisTag () {
      const { valueMaxMin: [ valueMax, valueMin ], data, defaultHorizon } = this

      const { horizon } = data

      this.horizon = horizon || defaultHorizon

      let { max, min, num, fixed } = data[horizon ? 'x' : 'y']

      let [trueMax, trueMin] = [max, min]

      const thirdValueMinus = parseInt((valueMax - valueMin) / 3)

      !max && (max !== 0) && (trueMax = valueMax + thirdValueMinus)
      !min && (min !== 0) && (trueMin = valueMin - thirdValueMinus)

      const trueMinus = trueMax - trueMin

      !num && trueMinus < 9 && (num = trueMinus + 1)
      !num && (num = 10)

      const valueGap = trueMinus / (num - 1)

      const valueAxisTag = this.valueAxisTag = Array(num).fill(0).map((t, i) =>
        (trueMin + i * valueGap).toFixed(fixed))

      const lastValueAxisTagIndex = valueAxisTag.length - 1

      this.axisMaxMin = [parseFloat(valueAxisTag[lastValueAxisTagIndex]), parseFloat(valueAxisTag[0])]
    },
    calcLabelAxisTag () {
      const { data, horizon } = this

      this.labelAxisTag = data[horizon ? 'y' : 'x'].data
    },
    calcXYAxisFS () {
      const { defaultXAxisFS, defaultYAxisFS } = this

      const { data: { x: { fontSize: xfs }, y: { fontSize: yfs } } } = this

      this.xyAxisFS = [xfs || defaultXAxisFS, yfs || defaultYAxisFS]
    },
    calcXYLabelMaxWidth () {
      const { ctx, valueAxisTag, labelAxisTag, horizon, xyAxisFS } = this

      const { canvas: { getTextsWidth } } = this

      const { data: { x: { unit: xUN }, y: { unit: yUN } } } = this

      ctx.font = `${xyAxisFS[0]}px Arial`

      this.labelXYMaxWidth[0] = Math.max(...getTextsWidth(ctx, horizon ? valueAxisTag : labelAxisTag))

      this.xyAxisUnitWidth[0] = ctx.measureText(xUN || '').width

      ctx.font = `${xyAxisFS[1]}px Arial`

      this.labelXYMaxWidth[1] = Math.max(...getTextsWidth(ctx, horizon ? labelAxisTag : valueAxisTag))

      this.xyAxisUnitWidth[1] = ctx.measureText(yUN || '').width
    },
    calcAxisMargin () {
      const { defaultXOffset, labelXYMaxWidth, data, xyAxisUnitWidth } = this

      const { offset: xOF, unitWidth: xUW } = data.x

      const { offset: yOF, unitHeight: yUH } = data.y

      this.axisMargin[0] = yUH || defaultXOffset
      this.axisMargin[1] = xUW || xyAxisUnitWidth[0] + 10
      this.axisMargin[2] = xOF || defaultXOffset
      this.axisMargin[3] = yOF || labelXYMaxWidth[1] + 10
    },
    calcAxisOriginPos () {
      const { axisMargin, canvasWH } = this

      this.axisOriginPos[0] = axisMargin[3]
      this.axisOriginPos[1] = canvasWH[1] - axisMargin[2]
    },
    calcAxisWH () {
      const { axisMargin, canvasWH } = this

      this.axisWH[0] = canvasWH[0] - axisMargin[1] - axisMargin[3]
      this.axisWH[1] = canvasWH[1] - axisMargin[0] - axisMargin[2]
    },
    calcValueTagPos () {
      const { axisWH, valueAxisTag, horizon, axisOriginPos: [x, y] } = this

      const valueTagNum = valueAxisTag.length

      const gapWidth = (horizon ? axisWH[0] : axisWH[1]) / (valueTagNum - 1)

      this.valueTagPos = new Array(valueTagNum).fill(0).map((t, i) =>
        horizon ? [x + gapWidth * i, y + 5] : [x - 5, y - gapWidth * i])
    },
    calcLabelTagPos () {
      const { axisWH, labelAxisTag, horizon, axisOriginPos: [x, y], data } = this

      const { defaultAxisLabelGap, axisType } = this

      const { boundaryGap } = data

      const labelNum = labelAxisTag.length

      const gapAllWidth = horizon ? axisWH[1] : axisWH[0]

      const tempArray = new Array(labelNum).fill(0)

      if (axisType === 'column' || (axisType === 'line' && boundaryGap)) {
        const gapWidth = gapAllWidth / labelNum

        const halfGapWidth = gapWidth / 2

        this.labelAxisPos = tempArray.map((t, i) =>
          horizon ? [x, y - gapWidth * i - halfGapWidth]
            : [x + gapWidth * i + halfGapWidth, y])

        this.labelTagPos = tempArray.map((t, i) =>
          horizon ? [x - defaultAxisLabelGap, y - gapWidth * i - halfGapWidth]
            : [x + gapWidth * i + halfGapWidth, y + defaultAxisLabelGap])
      }

      if (axisType === 'line' && !boundaryGap) {
        const gapWidth = gapAllWidth / (labelNum - 1)

        this.labelAxisPos = tempArray.map((t, i) =>
          horizon ? [x, y - gapWidth]
            : [x + gapWidth * i, y])

        this.labelTagPos = tempArray.map((t, i) =>
          horizon ? [x - defaultAxisLabelGap, y - gapWidth]
            : [x + gapWidth * i, y + defaultAxisLabelGap])
      }
    },
    calcTagGap () {
      const { horizon, valueTagPos, labelTagPos } = this

      const v = horizon ? '0' : '1'

      this.valueTagGap = Math.abs(valueTagPos[0][v] - valueTagPos[1][v])

      const l = horizon ? '1' : '0'

      this.labelTagGap = Math.abs(labelTagPos[0][l] - labelTagPos[1][l])
    },
    calcTagColor () {
      const { defaultXAxisLineColor, defaultYAxisLineColor, drawColors, data } = this

      const { x: { color: xc }, y: { color: yc } } = data

      let xTagColor = xc || defaultXAxisLineColor

      xTagColor === 'colors' && (xTagColor = drawColors)

      let yTagColor = yc || defaultYAxisLineColor

      yTagColor === 'colors' && (yTagColor = drawColors)

      this.xTagColor = xTagColor

      this.xTagColorMul = xTagColor instanceof Array

      this.yTagColor = yTagColor

      this.yTagColorMul = yTagColor instanceof Array
    },
    calcGridColor () {
      const { drawColors, data, defaultGridColor } = this

      const { x: { gridColor: xgc }, y: { gridColor: ygc } } = data

      let xGridColor = xgc || defaultGridColor
      let yGridColor = ygc || defaultGridColor

      xGridColor === 'colors' && (xGridColor = drawColors)
      yGridColor === 'colors' && (yGridColor = drawColors)

      this.xGridColor = xGridColor
      this.yGridColor = yGridColor

      this.xGridColorMul = xGridColor instanceof Array
      this.yGridColorMul = yGridColor instanceof Array
    },
    drawAxis () {
      const { drawAxisLine, drawAxisTag, drawUnit, drawGrid } = this

      drawAxisLine()

      drawAxisTag()

      drawUnit()

      drawGrid()
    },
    drawAxisLine () {
      const { ctx, defaultXAxisLineColor, defaultYAxisLineColor, axisOriginPos, axisWH, data } = this

      const { x: { lineColor: xlc, noAxisLine: xNAL }, y: { lineColor: ylc, noAxisLine: yNAL } } = data

      if (xNAL && yNAL) return

      ctx.lineWidth = 1

      if (!xNAL) {
        ctx.strokeStyle = xlc || defaultXAxisLineColor
        ctx.beginPath()
        ctx.moveTo(...axisOriginPos)
        ctx.lineTo(axisOriginPos[0] + axisWH[0], axisOriginPos[1])
        ctx.stroke()
      }

      if (!yNAL) {
        ctx.strokeStyle = ylc || defaultYAxisLineColor
        ctx.beginPath()
        ctx.moveTo(...axisOriginPos)
        ctx.lineTo(axisOriginPos[0], axisOriginPos[1] - axisWH[1])
        ctx.stroke()
      }
    },
    drawAxisTag () {
      const { ctx, horizon, valueTagPos, labelTagPos, valueAxisTag, labelAxisTag } = this

      const { xTagColor, xTagColorMul, yTagColor, yTagColorMul, xyAxisFS } = this

      const { data: { x: { noAxisTag: xNAT, rotate }, y: { noAxisTag: yNAT } } } = this

      if (xNAT && yNAT) return

      const xAxisData = horizon ? valueTagPos : labelTagPos
      const yAxisData = horizon ? labelTagPos : valueTagPos

      const xTagData = horizon ? valueAxisTag : labelAxisTag
      const yTagData = horizon ? labelAxisTag : valueAxisTag

      !xTagColorMul && (ctx.fillStyle = xTagColor)

      const xTagColorNum = xTagColor.length

      ctx.font = `${xyAxisFS[0]}px Arial`

      ctx.textAlign = 'center'
      ctx.textBaseline = 'top'

      if (rotate) ctx.textAlign = 'left'

      !xNAT && xAxisData.forEach((pos, i) => {
        if (!xTagData[i]) return

        if (rotate) {
          ctx.save()
          ctx.translate(...pos)
          ctx.rotate(Math.PI / 4)
        }

        xTagColorMul && (ctx.fillStyle = xTagColor[i % xTagColorNum])

        ctx.fillText(xTagData[i], ...(rotate ? [0, 0] : pos))

        if (rotate) ctx.restore()
      })

      !yTagColorMul && (ctx.fillStyle = yTagColor)

      const yTagColorNum = yTagColor.length

      ctx.font = `${xyAxisFS[1]}px Arial`

      ctx.textAlign = 'right'
      ctx.textBaseline = 'middle'

      !yNAT && yAxisData.forEach((pos, i) => {
        if (!yTagData[i]) return

        xTagColorMul && (ctx.fillStyle = yTagColor[i % yTagColorNum])

        ctx.fillText(yTagData[i], ...pos)
      })

      this.ctx.fill()
    },
    drawUnit () {
      const { ctx, data, axisOriginPos, canvasWH, defaultAxisLabelGap } = this

      const { x: { unit: xUN, fontSize: xFS }, y: { unit: yUN, fontSize: yFS } } = data

      ctx.font = `${xFS}px Arial`

      ctx.textAlign = 'right'
      ctx.textBaseline = 'top'
      ctx.fillText(xUN || '', canvasWH[0], axisOriginPos[1] + defaultAxisLabelGap)

      ctx.font = `${yFS}px Arial`

      ctx.fillText(yUN || '', axisOriginPos[0], 0)
    },
    drawGrid () {
      const { horizon, labelTagPos, valueTagPos, axisOriginPos, ctx, data, axisWH } = this

      const { xGridColor, xGridColorMul, yGridColor, yGridColorMul, axisType } = this

      const { x: { grid: xG, gridType: xT }, y: { grid: yG, gridType: yT }, boundaryGap } = data

      const xAxisData = horizon ? valueTagPos : labelTagPos
      const yAxisData = horizon ? labelTagPos : valueTagPos

      ctx.lineWidth = 1

      !xGridColorMul && (ctx.strokeStyle = xGridColor)

      const xGridColorNum = xGridColor.length

      ctx.setLineDash(xT === 'dashed' ? [5, 5] : [10, 0])

      xG && xAxisData.forEach(([x, y], i) => {
        xGridColorMul && (ctx.strokeStyle = xGridColor[i % xGridColorNum])

        if (!horizon && i === 0 && axisType === 'line' && !boundaryGap) return
        if (horizon && i === 0) return

        ctx.beginPath()
        ctx.moveTo(x, axisOriginPos[1])
        ctx.lineTo(x, axisOriginPos[1] - axisWH[1])
        ctx.stroke()
      })

      !yGridColorMul && (ctx.strokeStyle = yGridColor)

      const yGridColorNum = yGridColor.length

      ctx.setLineDash(yT === 'dashed' ? [5, 5] : [10, 0])

      yG && yAxisData.forEach(([x, y], i) => {
        yGridColorMul && (ctx.strokeStyle = yGridColor[i % yGridColorNum])

        if (!horizon && i === 0) return
        if (horizon && i === 0 && axisType === 'line' && !boundaryGap) return

        ctx.beginPath()
        ctx.moveTo(axisOriginPos[0], y)
        ctx.lineTo(axisOriginPos[0] + axisWH[0], y)
        ctx.stroke()
      })
    }
  }
}
