Current File : /home/tradevaly/www/node_modules/apexcharts/src/charts/Pie.js
import Animations from '../modules/Animations'
import Fill from '../modules/Fill'
import Utils from '../utils/Utils'
import Graphics from '../modules/Graphics'
import Filters from '../modules/Filters'
import Scales from '../modules/Scales'
import Helpers from './common/circle/Helpers'
/**
 * ApexCharts Pie Class for drawing Pie / Donut Charts.
 * @module Pie
 **/

class Pie {
  constructor(ctx) {
    this.ctx = ctx
    this.w = ctx.w
    const w = this.w

    this.chartType = this.w.config.chart.type

    this.initialAnim = this.w.config.chart.animations.enabled
    this.dynamicAnim =
      this.initialAnim &&
      this.w.config.chart.animations.dynamicAnimation.enabled

    this.animBeginArr = [0]
    this.animDur = 0

    this.donutDataLabels = this.w.config.plotOptions.pie.donut.labels

    this.lineColorArr =
      w.globals.stroke.colors !== undefined
        ? w.globals.stroke.colors
        : w.globals.colors

    this.defaultSize = Math.min(w.globals.gridWidth, w.globals.gridHeight)

    this.centerY = this.defaultSize / 2
    this.centerX = w.globals.gridWidth / 2

    if (w.config.chart.type === 'radialBar') {
      this.fullAngle = 360
    } else {
      this.fullAngle = Math.abs(
        w.config.plotOptions.pie.endAngle - w.config.plotOptions.pie.startAngle
      )
    }
    this.initialAngle = w.config.plotOptions.pie.startAngle % this.fullAngle

    w.globals.radialSize =
      this.defaultSize / 2.05 -
      w.config.stroke.width -
      (!w.config.chart.sparkline.enabled ? w.config.chart.dropShadow.blur : 0)

    this.donutSize =
      (w.globals.radialSize *
        parseInt(w.config.plotOptions.pie.donut.size, 10)) /
      100

    this.maxY = 0
    this.sliceLabels = []
    this.sliceSizes = []

    this.prevSectorAngleArr = [] // for dynamic animations
  }

  draw(series) {
    let self = this
    let w = this.w

    const graphics = new Graphics(this.ctx)

    this.ret = graphics.group({
      class: 'apexcharts-pie'
    })

    if (w.globals.noData) return this.ret

    let total = 0
    for (let k = 0; k < series.length; k++) {
      // CALCULATE THE TOTAL
      total += Utils.negToZero(series[k])
    }

    let sectorAngleArr = []

    // el to which series will be drawn
    let elSeries = graphics.group()

    // prevent division by zero error if there is no data
    if (total === 0) {
      total = 0.00001
    }

    series.forEach((m) => {
      this.maxY = Math.max(this.maxY, m)
    })

    // override maxY if user provided in config
    if (w.config.yaxis[0].max) {
      this.maxY = w.config.yaxis[0].max
    }

    if (w.config.grid.position === 'back' && this.chartType === 'polarArea') {
      this.drawPolarElements(this.ret)
    }

    for (let i = 0; i < series.length; i++) {
      // CALCULATE THE ANGLES
      let angle = (this.fullAngle * Utils.negToZero(series[i])) / total
      sectorAngleArr.push(angle)

      if (this.chartType === 'polarArea') {
        sectorAngleArr[i] = this.fullAngle / series.length
        this.sliceSizes.push((w.globals.radialSize * series[i]) / this.maxY)
      } else {
        this.sliceSizes.push(w.globals.radialSize)
      }
    }

    if (w.globals.dataChanged) {
      let prevTotal = 0
      for (let k = 0; k < w.globals.previousPaths.length; k++) {
        // CALCULATE THE PREV TOTAL
        prevTotal += Utils.negToZero(w.globals.previousPaths[k])
      }

      let previousAngle

      for (let i = 0; i < w.globals.previousPaths.length; i++) {
        // CALCULATE THE PREVIOUS ANGLES
        previousAngle =
          (this.fullAngle * Utils.negToZero(w.globals.previousPaths[i])) /
          prevTotal
        this.prevSectorAngleArr.push(previousAngle)
      }
    }

    // on small chart size after few count of resizes browser window donutSize can be negative
    if (this.donutSize < 0) {
      this.donutSize = 0
    }

    let scaleSize = w.config.plotOptions.pie.customScale
    let halfW = w.globals.gridWidth / 2
    let halfH = w.globals.gridHeight / 2
    let translateX = halfW - (w.globals.gridWidth / 2) * scaleSize
    let translateY = halfH - (w.globals.gridHeight / 2) * scaleSize

    if (this.chartType === 'donut') {
      // draw the inner circle and add some text to it
      const circle = graphics.drawCircle(this.donutSize)

      circle.attr({
        cx: this.centerX,
        cy: this.centerY,
        fill: w.config.plotOptions.pie.donut.background
          ? w.config.plotOptions.pie.donut.background
          : 'transparent'
      })

      elSeries.add(circle)
    }

    let elG = self.drawArcs(sectorAngleArr, series)

    // add slice dataLabels at the end
    this.sliceLabels.forEach((s) => {
      elG.add(s)
    })

    elSeries.attr({
      transform: `translate(${translateX}, ${translateY}) scale(${scaleSize})`
    })

    elSeries.add(elG)

    this.ret.add(elSeries)

    if (this.donutDataLabels.show) {
      let dataLabels = this.renderInnerDataLabels(this.donutDataLabels, {
        hollowSize: this.donutSize,
        centerX: this.centerX,
        centerY: this.centerY,
        opacity: this.donutDataLabels.show,
        translateX,
        translateY
      })

      this.ret.add(dataLabels)
    }

    if (w.config.grid.position === 'front' && this.chartType === 'polarArea') {
      this.drawPolarElements(this.ret)
    }

    return this.ret
  }

  // core function for drawing pie arcs
  drawArcs(sectorAngleArr, series) {
    let w = this.w
    const filters = new Filters(this.ctx)

    let graphics = new Graphics(this.ctx)
    let fill = new Fill(this.ctx)
    let g = graphics.group({
      class: 'apexcharts-slices'
    })

    let startAngle = this.initialAngle
    let prevStartAngle = this.initialAngle
    let endAngle = this.initialAngle
    let prevEndAngle = this.initialAngle

    this.strokeWidth = w.config.stroke.show ? w.config.stroke.width : 0

    for (let i = 0; i < sectorAngleArr.length; i++) {
      let elPieArc = graphics.group({
        class: `apexcharts-series apexcharts-pie-series`,
        seriesName: Utils.escapeString(w.globals.seriesNames[i]),
        rel: i + 1,
        'data:realIndex': i
      })

      g.add(elPieArc)

      startAngle = endAngle
      prevStartAngle = prevEndAngle

      endAngle = startAngle + sectorAngleArr[i]
      prevEndAngle = prevStartAngle + this.prevSectorAngleArr[i]

      const angle =
        endAngle < startAngle
          ? this.fullAngle + endAngle - startAngle
          : endAngle - startAngle

      let pathFill = fill.fillPath({
        seriesNumber: i,
        size: this.sliceSizes[i],
        value: series[i]
      }) // additionally, pass size for gradient drawing in the fillPath function

      let path = this.getChangedPath(prevStartAngle, prevEndAngle)

      let elPath = graphics.drawPath({
        d: path,
        stroke: Array.isArray(this.lineColorArr)
          ? this.lineColorArr[i]
          : this.lineColorArr,
        strokeWidth: 0,
        fill: pathFill,
        fillOpacity: w.config.fill.opacity,
        classes: `apexcharts-pie-area apexcharts-${this.chartType.toLowerCase()}-slice-${i}`
      })

      elPath.attr({
        index: 0,
        j: i
      })

      filters.setSelectionFilter(elPath, 0, i)

      if (w.config.chart.dropShadow.enabled) {
        const shadow = w.config.chart.dropShadow
        filters.dropShadow(elPath, shadow, i)
      }

      this.addListeners(elPath, this.donutDataLabels)

      Graphics.setAttrs(elPath.node, {
        'data:angle': angle,
        'data:startAngle': startAngle,
        'data:strokeWidth': this.strokeWidth,
        'data:value': series[i]
      })

      let labelPosition = {
        x: 0,
        y: 0
      }

      if (this.chartType === 'pie' || this.chartType === 'polarArea') {
        labelPosition = Utils.polarToCartesian(
          this.centerX,
          this.centerY,
          w.globals.radialSize / 1.25 +
            w.config.plotOptions.pie.dataLabels.offset,
          (startAngle + angle / 2) % this.fullAngle
        )
      } else if (this.chartType === 'donut') {
        labelPosition = Utils.polarToCartesian(
          this.centerX,
          this.centerY,
          (w.globals.radialSize + this.donutSize) / 2 +
            w.config.plotOptions.pie.dataLabels.offset,
          (startAngle + angle / 2) % this.fullAngle
        )
      }

      elPieArc.add(elPath)

      // Animation code starts
      let dur = 0
      if (this.initialAnim && !w.globals.resized && !w.globals.dataChanged) {
        dur = (angle / this.fullAngle) * w.config.chart.animations.speed

        if (dur === 0) dur = 1
        this.animDur = dur + this.animDur
        this.animBeginArr.push(this.animDur)
      } else {
        this.animBeginArr.push(0)
      }

      if (this.dynamicAnim && w.globals.dataChanged) {
        this.animatePaths(elPath, {
          size: this.sliceSizes[i],
          endAngle,
          startAngle,
          prevStartAngle,
          prevEndAngle,
          animateStartingPos: true,
          i,
          animBeginArr: this.animBeginArr,
          shouldSetPrevPaths: true,
          dur: w.config.chart.animations.dynamicAnimation.speed
        })
      } else {
        this.animatePaths(elPath, {
          size: this.sliceSizes[i],
          endAngle,
          startAngle,
          i,
          totalItems: sectorAngleArr.length - 1,
          animBeginArr: this.animBeginArr,
          dur
        })
      }
      // animation code ends

      if (
        w.config.plotOptions.pie.expandOnClick &&
        this.chartType !== 'polarArea'
      ) {
        elPath.click(this.pieClicked.bind(this, i))
      }

      if (
        typeof w.globals.selectedDataPoints[0] !== 'undefined' &&
        w.globals.selectedDataPoints[0].indexOf(i) > -1
      ) {
        this.pieClicked(i)
      }

      if (w.config.dataLabels.enabled) {
        let xPos = labelPosition.x
        let yPos = labelPosition.y
        let text = (100 * angle) / this.fullAngle + '%'

        if (
          angle !== 0 &&
          w.config.plotOptions.pie.dataLabels.minAngleToShowLabel <
            sectorAngleArr[i]
        ) {
          let formatter = w.config.dataLabels.formatter
          if (formatter !== undefined) {
            text = formatter(w.globals.seriesPercent[i][0], {
              seriesIndex: i,
              w
            })
          }
          let foreColor = w.globals.dataLabels.style.colors[i]

          const elPieLabelWrap = graphics.group({
            class: `apexcharts-datalabels`
          })
          let elPieLabel = graphics.drawText({
            x: xPos,
            y: yPos,
            text,
            textAnchor: 'middle',
            fontSize: w.config.dataLabels.style.fontSize,
            fontFamily: w.config.dataLabels.style.fontFamily,
            fontWeight: w.config.dataLabels.style.fontWeight,
            foreColor
          })

          elPieLabelWrap.add(elPieLabel)
          if (w.config.dataLabels.dropShadow.enabled) {
            const textShadow = w.config.dataLabels.dropShadow
            filters.dropShadow(elPieLabel, textShadow)
          }

          elPieLabel.node.classList.add('apexcharts-pie-label')
          if (
            w.config.chart.animations.animate &&
            w.globals.resized === false
          ) {
            elPieLabel.node.classList.add('apexcharts-pie-label-delay')
            elPieLabel.node.style.animationDelay =
              w.config.chart.animations.speed / 940 + 's'
          }

          this.sliceLabels.push(elPieLabelWrap)
        }
      }
    }

    return g
  }

  addListeners(elPath, dataLabels) {
    const graphics = new Graphics(this.ctx)
    // append filters on mouseenter and mouseleave
    elPath.node.addEventListener(
      'mouseenter',
      graphics.pathMouseEnter.bind(this, elPath)
    )

    elPath.node.addEventListener(
      'mouseleave',
      graphics.pathMouseLeave.bind(this, elPath)
    )
    elPath.node.addEventListener(
      'mouseleave',
      this.revertDataLabelsInner.bind(this, elPath.node, dataLabels)
    )
    elPath.node.addEventListener(
      'mousedown',
      graphics.pathMouseDown.bind(this, elPath)
    )

    if (!this.donutDataLabels.total.showAlways) {
      elPath.node.addEventListener(
        'mouseenter',
        this.printDataLabelsInner.bind(this, elPath.node, dataLabels)
      )

      elPath.node.addEventListener(
        'mousedown',
        this.printDataLabelsInner.bind(this, elPath.node, dataLabels)
      )
    }
  }

  // This function can be used for other circle charts too
  animatePaths(el, opts) {
    let w = this.w
    let me = this

    let angle =
      opts.endAngle < opts.startAngle
        ? this.fullAngle + opts.endAngle - opts.startAngle
        : opts.endAngle - opts.startAngle
    let prevAngle = angle

    let fromStartAngle = opts.startAngle
    let toStartAngle = opts.startAngle

    if (opts.prevStartAngle !== undefined && opts.prevEndAngle !== undefined) {
      fromStartAngle = opts.prevEndAngle
      prevAngle =
        opts.prevEndAngle < opts.prevStartAngle
          ? this.fullAngle + opts.prevEndAngle - opts.prevStartAngle
          : opts.prevEndAngle - opts.prevStartAngle
    }
    if (opts.i === w.config.series.length - 1) {
      // some adjustments for the last overlapping paths
      if (angle + toStartAngle > this.fullAngle) {
        opts.endAngle = opts.endAngle - (angle + toStartAngle)
      } else if (angle + toStartAngle < this.fullAngle) {
        opts.endAngle =
          opts.endAngle + (this.fullAngle - (angle + toStartAngle))
      }
    }

    if (angle === this.fullAngle) angle = this.fullAngle - 0.01

    me.animateArc(el, fromStartAngle, toStartAngle, angle, prevAngle, opts)
  }

  animateArc(el, fromStartAngle, toStartAngle, angle, prevAngle, opts) {
    let me = this
    const w = this.w
    const animations = new Animations(this.ctx)

    let size = opts.size

    let path

    if (isNaN(fromStartAngle) || isNaN(prevAngle)) {
      fromStartAngle = toStartAngle
      prevAngle = angle
      opts.dur = 0
    }

    let currAngle = angle
    let startAngle = toStartAngle
    let fromAngle =
      fromStartAngle < toStartAngle
        ? this.fullAngle + fromStartAngle - toStartAngle
        : fromStartAngle - toStartAngle

    if (w.globals.dataChanged && opts.shouldSetPrevPaths) {
      // to avoid flicker when updating, set prev path first and then animate from there
      if (opts.prevEndAngle) {
        path = me.getPiePath({
          me,
          startAngle: opts.prevStartAngle,
          angle:
            opts.prevEndAngle < opts.prevStartAngle
              ? this.fullAngle + opts.prevEndAngle - opts.prevStartAngle
              : opts.prevEndAngle - opts.prevStartAngle,
          size
        })
        el.attr({ d: path })
      }
    }

    if (opts.dur !== 0) {
      el.animate(opts.dur, w.globals.easing, opts.animBeginArr[opts.i])
        .afterAll(function() {
          if (
            me.chartType === 'pie' ||
            me.chartType === 'donut' ||
            me.chartType === 'polarArea'
          ) {
            this.animate(w.config.chart.animations.dynamicAnimation.speed).attr(
              {
                'stroke-width': me.strokeWidth
              }
            )
          }

          if (opts.i === w.config.series.length - 1) {
            animations.animationCompleted(el)
          }
        })
        .during((pos) => {
          currAngle = fromAngle + (angle - fromAngle) * pos
          if (opts.animateStartingPos) {
            currAngle = prevAngle + (angle - prevAngle) * pos
            startAngle =
              fromStartAngle -
              prevAngle +
              (toStartAngle - (fromStartAngle - prevAngle)) * pos
          }

          path = me.getPiePath({
            me,
            startAngle,
            angle: currAngle,
            size
          })

          el.node.setAttribute('data:pathOrig', path)

          el.attr({
            d: path
          })
        })
    } else {
      path = me.getPiePath({
        me,
        startAngle,
        angle,
        size
      })

      if (!opts.isTrack) {
        w.globals.animationEnded = true
      }
      el.node.setAttribute('data:pathOrig', path)

      el.attr({
        d: path,
        'stroke-width': me.strokeWidth
      })
    }
  }

  pieClicked(i) {
    let w = this.w
    let me = this
    let path

    let size =
      me.sliceSizes[i] + (w.config.plotOptions.pie.expandOnClick ? 4 : 0)
    let elPath = w.globals.dom.Paper.select(
      `.apexcharts-${me.chartType.toLowerCase()}-slice-${i}`
    ).members[0]

    if (elPath.attr('data:pieClicked') === 'true') {
      elPath.attr({
        'data:pieClicked': 'false'
      })
      this.revertDataLabelsInner(elPath.node, this.donutDataLabels)

      let origPath = elPath.attr('data:pathOrig')
      elPath.attr({
        d: origPath
      })
      return
    } else {
      // reset all elems
      let allEls = w.globals.dom.baseEl.getElementsByClassName(
        'apexcharts-pie-area'
      )
      Array.prototype.forEach.call(allEls, (pieSlice) => {
        pieSlice.setAttribute('data:pieClicked', 'false')
        let origPath = pieSlice.getAttribute('data:pathOrig')
        pieSlice.setAttribute('d', origPath)
      })
      elPath.attr('data:pieClicked', 'true')
    }

    let startAngle = parseInt(elPath.attr('data:startAngle'), 10)
    let angle = parseInt(elPath.attr('data:angle'), 10)

    path = me.getPiePath({
      me,
      startAngle,
      angle,
      size
    })

    if (angle === 360) return

    elPath.plot(path)
  }

  getChangedPath(prevStartAngle, prevEndAngle) {
    let path = ''
    if (this.dynamicAnim && this.w.globals.dataChanged) {
      path = this.getPiePath({
        me: this,
        startAngle: prevStartAngle,
        angle: prevEndAngle - prevStartAngle,
        size: this.size
      })
    }
    return path
  }

  getPiePath({ me, startAngle, angle, size }) {
    let path

    let startDeg = startAngle
    let startRadians = (Math.PI * (startDeg - 90)) / 180

    let endDeg = angle + startAngle
    // prevent overlap
    if (
      Math.ceil(endDeg) >=
      this.fullAngle +
        (this.w.config.plotOptions.pie.startAngle % this.fullAngle)
    ) {
      endDeg =
        this.fullAngle +
        (this.w.config.plotOptions.pie.startAngle % this.fullAngle) -
        0.01
    }
    if (Math.ceil(endDeg) > this.fullAngle) endDeg -= this.fullAngle

    let endRadians = (Math.PI * (endDeg - 90)) / 180

    let x1 = me.centerX + size * Math.cos(startRadians)
    let y1 = me.centerY + size * Math.sin(startRadians)
    let x2 = me.centerX + size * Math.cos(endRadians)
    let y2 = me.centerY + size * Math.sin(endRadians)

    let startInner = Utils.polarToCartesian(
      me.centerX,
      me.centerY,
      me.donutSize,
      endDeg
    )
    let endInner = Utils.polarToCartesian(
      me.centerX,
      me.centerY,
      me.donutSize,
      startDeg
    )

    let largeArc = angle > 180 ? 1 : 0

    const pathBeginning = ['M', x1, y1, 'A', size, size, 0, largeArc, 1, x2, y2]

    if (me.chartType === 'donut') {
      path = [
        ...pathBeginning,
        'L',
        startInner.x,
        startInner.y,
        'A',
        me.donutSize,
        me.donutSize,
        0,
        largeArc,
        0,
        endInner.x,
        endInner.y,
        'L',
        x1,
        y1,
        'z'
      ].join(' ')
    } else if (me.chartType === 'pie' || me.chartType === 'polarArea') {
      path = [...pathBeginning, 'L', me.centerX, me.centerY, 'L', x1, y1].join(
        ' '
      )
    } else {
      path = [...pathBeginning].join(' ')
    }

    return path
  }

  drawPolarElements(parent) {
    const w = this.w
    const scale = new Scales(this.ctx)
    const graphics = new Graphics(this.ctx)
    const helpers = new Helpers(this.ctx)

    const gCircles = graphics.group()
    const gYAxis = graphics.group()

    const yScale = scale.niceScale(
      0,
      Math.ceil(this.maxY),
      w.config.yaxis[0].tickAmount,
      0,
      true
    )

    const yTexts = yScale.result.reverse()
    let len = yScale.result.length

    this.maxY = yScale.niceMax

    let circleSize = w.globals.radialSize
    let diff = circleSize / (len - 1)

    for (let i = 0; i < len - 1; i++) {
      const circle = graphics.drawCircle(circleSize)

      circle.attr({
        cx: this.centerX,
        cy: this.centerY,
        fill: 'none',
        'stroke-width': w.config.plotOptions.polarArea.rings.strokeWidth,
        stroke: w.config.plotOptions.polarArea.rings.strokeColor
      })

      if (w.config.yaxis[0].show) {
        const yLabel = helpers.drawYAxisTexts(
          this.centerX,
          this.centerY -
            circleSize +
            parseInt(w.config.yaxis[0].labels.style.fontSize, 10) / 2,
          i,
          yTexts[i]
        )

        gYAxis.add(yLabel)
      }

      gCircles.add(circle)

      circleSize = circleSize - diff
    }

    this.drawSpokes(parent)

    parent.add(gCircles)
    parent.add(gYAxis)
  }

  renderInnerDataLabels(dataLabelsConfig, opts) {
    let w = this.w
    const graphics = new Graphics(this.ctx)

    let g = graphics.group({
      class: 'apexcharts-datalabels-group',
      transform: `translate(${opts.translateX ? opts.translateX : 0}, ${
        opts.translateY ? opts.translateY : 0
      }) scale(${w.config.plotOptions.pie.customScale})`
    })

    const showTotal = dataLabelsConfig.total.show

    g.node.style.opacity = opts.opacity

    let x = opts.centerX
    let y = opts.centerY

    let labelColor, valueColor

    if (dataLabelsConfig.name.color === undefined) {
      labelColor = w.globals.colors[0]
    } else {
      labelColor = dataLabelsConfig.name.color
    }
    let labelFontSize = dataLabelsConfig.name.fontSize
    let labelFontFamily = dataLabelsConfig.name.fontFamily
    let labelFontWeight = dataLabelsConfig.value.fontWeight

    if (dataLabelsConfig.value.color === undefined) {
      valueColor = w.config.chart.foreColor
    } else {
      valueColor = dataLabelsConfig.value.color
    }

    let lbFormatter = dataLabelsConfig.value.formatter
    let val = ''
    let name = ''

    if (showTotal) {
      labelColor = dataLabelsConfig.total.color
      labelFontSize = dataLabelsConfig.total.fontSize
      labelFontFamily = dataLabelsConfig.total.fontFamily
      labelFontWeight = dataLabelsConfig.total.fontWeight
      name = dataLabelsConfig.total.label
      val = dataLabelsConfig.total.formatter(w)
    } else {
      if (w.globals.series.length === 1) {
        val = lbFormatter(w.globals.series[0], w)
        name = w.globals.seriesNames[0]
      }
    }

    if (name) {
      name = dataLabelsConfig.name.formatter(
        name,
        dataLabelsConfig.total.show,
        w
      )
    }

    if (dataLabelsConfig.name.show) {
      let elLabel = graphics.drawText({
        x,
        y: y + parseFloat(dataLabelsConfig.name.offsetY),
        text: name,
        textAnchor: 'middle',
        foreColor: labelColor,
        fontSize: labelFontSize,
        fontWeight: labelFontWeight,
        fontFamily: labelFontFamily
      })
      elLabel.node.classList.add('apexcharts-datalabel-label')
      g.add(elLabel)
    }

    if (dataLabelsConfig.value.show) {
      let valOffset = dataLabelsConfig.name.show
        ? parseFloat(dataLabelsConfig.value.offsetY) + 16
        : dataLabelsConfig.value.offsetY

      let elValue = graphics.drawText({
        x,
        y: y + valOffset,
        text: val,
        textAnchor: 'middle',
        foreColor: valueColor,
        fontWeight: dataLabelsConfig.value.fontWeight,
        fontSize: dataLabelsConfig.value.fontSize,
        fontFamily: dataLabelsConfig.value.fontFamily
      })
      elValue.node.classList.add('apexcharts-datalabel-value')
      g.add(elValue)
    }

    // for a multi-series circle chart, we need to show total value instead of first series labels

    return g
  }

  /**
   *
   * @param {string} name - The name of the series
   * @param {string} val - The value of that series
   * @param {object} el - Optional el (indicates which series was hovered/clicked). If this param is not present, means we need to show total
   */
  printInnerLabels(labelsConfig, name, val, el) {
    const w = this.w

    let labelColor

    if (el) {
      if (labelsConfig.name.color === undefined) {
        labelColor =
          w.globals.colors[parseInt(el.parentNode.getAttribute('rel'), 10) - 1]
      } else {
        labelColor = labelsConfig.name.color
      }
    } else {
      if (w.globals.series.length > 1 && labelsConfig.total.show) {
        labelColor = labelsConfig.total.color
      }
    }

    let elLabel = w.globals.dom.baseEl.querySelector(
      '.apexcharts-datalabel-label'
    )
    let elValue = w.globals.dom.baseEl.querySelector(
      '.apexcharts-datalabel-value'
    )

    let lbFormatter = labelsConfig.value.formatter
    val = lbFormatter(val, w)

    // we need to show Total Val - so get the formatter of it
    if (!el && typeof labelsConfig.total.formatter === 'function') {
      val = labelsConfig.total.formatter(w)
    }

    const isTotal = name === labelsConfig.total.label
    name = labelsConfig.name.formatter(name, isTotal, w)

    if (elLabel !== null) {
      elLabel.textContent = name
    }

    if (elValue !== null) {
      elValue.textContent = val
    }
    if (elLabel !== null) {
      elLabel.style.fill = labelColor
    }
  }

  printDataLabelsInner(el, dataLabelsConfig) {
    let w = this.w

    let val = el.getAttribute('data:value')
    let name =
      w.globals.seriesNames[parseInt(el.parentNode.getAttribute('rel'), 10) - 1]

    if (w.globals.series.length > 1) {
      this.printInnerLabels(dataLabelsConfig, name, val, el)
    }

    let dataLabelsGroup = w.globals.dom.baseEl.querySelector(
      '.apexcharts-datalabels-group'
    )
    if (dataLabelsGroup !== null) {
      dataLabelsGroup.style.opacity = 1
    }
  }

  drawSpokes(parent) {
    const w = this.w
    const graphics = new Graphics(this.ctx)
    const spokeConfig = w.config.plotOptions.polarArea.spokes

    if (spokeConfig.strokeWidth === 0) return

    let spokes = []

    let angleDivision = 360 / w.globals.series.length
    for (let i = 0; i < w.globals.series.length; i++) {
      spokes.push(
        Utils.polarToCartesian(
          this.centerX,
          this.centerY,
          w.globals.radialSize,
          w.config.plotOptions.pie.startAngle + angleDivision * i
        )
      )
    }

    spokes.forEach((p, i) => {
      const line = graphics.drawLine(
        p.x,
        p.y,
        this.centerX,
        this.centerY,
        Array.isArray(spokeConfig.connectorColors)
          ? spokeConfig.connectorColors[i]
          : spokeConfig.connectorColors
      )

      parent.add(line)
    })
  }

  revertDataLabelsInner(elem, dataLabelsConfig, event) {
    let w = this.w
    let dataLabelsGroup = w.globals.dom.baseEl.querySelector(
      '.apexcharts-datalabels-group'
    )

    let sliceOut = false
    const slices = w.globals.dom.baseEl.getElementsByClassName(
      `apexcharts-pie-area`
    )

    const selectSlice = ({ makeSliceOut, printLabel }) => {
      Array.prototype.forEach.call(slices, (s) => {
        if (s.getAttribute('data:pieClicked') === 'true') {
          if (makeSliceOut) {
            sliceOut = true
          }
          if (printLabel) {
            this.printDataLabelsInner(s, dataLabelsConfig)
          }
        }
      })
    }

    selectSlice({ makeSliceOut: true, printLabel: false })

    if (dataLabelsConfig.total.show && w.globals.series.length > 1) {
      if (sliceOut && !dataLabelsConfig.total.showAlways) {
        selectSlice({ makeSliceOut: false, printLabel: true })
      } else {
        this.printInnerLabels(
          dataLabelsConfig,
          dataLabelsConfig.total.label,
          dataLabelsConfig.total.formatter(w)
        )
      }
    } else {
      selectSlice({ makeSliceOut: false, printLabel: true })

      if (!sliceOut) {
        if (
          w.globals.selectedDataPoints.length &&
          w.globals.series.length > 1
        ) {
          if (w.globals.selectedDataPoints[0].length > 0) {
            const index = w.globals.selectedDataPoints[0]
            const el = w.globals.dom.baseEl.querySelector(
              `.apexcharts-${this.chartType.toLowerCase()}-slice-${index}`
            )

            this.printDataLabelsInner(el, dataLabelsConfig)
          } else if (
            dataLabelsGroup &&
            w.globals.selectedDataPoints.length &&
            w.globals.selectedDataPoints[0].length === 0
          ) {
            dataLabelsGroup.style.opacity = 0
          }
        } else {
          if (dataLabelsGroup && w.globals.series.length > 1) {
            dataLabelsGroup.style.opacity = 0
          }
        }
      }
    }
  }
}

export default Pie