import Pie from './Pie'
import Utils from '../utils/Utils'
import Fill from '../modules/Fill'
import Graphics from '../modules/Graphics'
import Filters from '../modules/Filters'

/**
 * ApexCharts Radial Class for drawing Circle / Semi Circle Charts.
 * @module Radial
 **/

class Radial extends Pie {
  constructor(ctx) {
    super(ctx)

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

    const w = this.w
    this.startAngle = w.config.plotOptions.radialBar.startAngle
    this.endAngle = w.config.plotOptions.radialBar.endAngle

    this.totalAngle = Math.abs(
      w.config.plotOptions.radialBar.endAngle -
        w.config.plotOptions.radialBar.startAngle
    )

    this.trackStartAngle = w.config.plotOptions.radialBar.track.startAngle
    this.trackEndAngle = w.config.plotOptions.radialBar.track.endAngle

    this.donutDataLabels = this.w.config.plotOptions.radialBar.dataLabels
    this.radialDataLabels = this.donutDataLabels // make a copy for easy reference

    if (!this.trackStartAngle) this.trackStartAngle = this.startAngle
    if (!this.trackEndAngle) this.trackEndAngle = this.endAngle

    if (this.endAngle === 360) this.endAngle = 359.99

    this.margin = parseInt(w.config.plotOptions.radialBar.track.margin, 10)
  }

  draw(series) {
    let w = this.w
    const graphics = new Graphics(this.ctx)

    let ret = graphics.group({
      class: 'apexcharts-radialbar'
    })

    if (w.globals.noData) return ret

    let elSeries = graphics.group()

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

    let size = this.defaultSize / 2.05
    if (!w.config.chart.sparkline.enabled) {
      size = size - w.config.stroke.width - w.config.chart.dropShadow.blur
    }
    let colorArr = w.globals.fill.colors

    if (w.config.plotOptions.radialBar.track.show) {
      let elTracks = this.drawTracks({
        size,
        centerX,
        centerY,
        colorArr,
        series
      })
      elSeries.add(elTracks)
    }

    let elG = this.drawArcs({
      size,
      centerX,
      centerY,
      colorArr,
      series
    })

    let totalAngle = 360

    if (w.config.plotOptions.radialBar.startAngle < 0) {
      totalAngle = this.totalAngle
    }

    let angleRatio = (360 - totalAngle) / 360
    w.globals.radialSize = size - size * angleRatio

    if (this.radialDataLabels.value.show) {
      let offset = Math.max(
        this.radialDataLabels.value.offsetY,
        this.radialDataLabels.name.offsetY
      )
      w.globals.radialSize += offset * angleRatio
    }

    elSeries.add(elG.g)

    if (w.config.plotOptions.radialBar.hollow.position === 'front') {
      elG.g.add(elG.elHollow)
      if (elG.dataLabels) {
        elG.g.add(elG.dataLabels)
      }
    }

    ret.add(elSeries)

    return ret
  }

  drawTracks(opts) {
    let w = this.w
    const graphics = new Graphics(this.ctx)

    let g = graphics.group({
      class: 'apexcharts-tracks'
    })

    let filters = new Filters(this.ctx)
    let fill = new Fill(this.ctx)

    let strokeWidth = this.getStrokeWidth(opts)

    opts.size = opts.size - strokeWidth / 2

    for (let i = 0; i < opts.series.length; i++) {
      let elRadialBarTrack = graphics.group({
        class: 'apexcharts-radialbar-track apexcharts-track'
      })
      g.add(elRadialBarTrack)

      elRadialBarTrack.attr({
        rel: i + 1
      })

      opts.size = opts.size - strokeWidth - this.margin

      const trackConfig = w.config.plotOptions.radialBar.track
      let pathFill = fill.fillPath({
        seriesNumber: 0,
        size: opts.size,
        fillColors: Array.isArray(trackConfig.background)
          ? trackConfig.background[i]
          : trackConfig.background,
        solid: true
      })

      let startAngle = this.trackStartAngle
      let endAngle = this.trackEndAngle

      if (Math.abs(endAngle) + Math.abs(startAngle) >= 360)
        endAngle = 360 - Math.abs(this.startAngle) - 0.1

      let elPath = graphics.drawPath({
        d: '',
        stroke: pathFill,
        strokeWidth:
          (strokeWidth * parseInt(trackConfig.strokeWidth, 10)) / 100,
        fill: 'none',
        strokeOpacity: trackConfig.opacity,
        classes: 'apexcharts-radialbar-area'
      })

      if (trackConfig.dropShadow.enabled) {
        const shadow = trackConfig.dropShadow
        filters.dropShadow(elPath, shadow)
      }

      elRadialBarTrack.add(elPath)

      elPath.attr('id', 'apexcharts-radialbarTrack-' + i)

      this.animatePaths(elPath, {
        centerX: opts.centerX,
        centerY: opts.centerY,
        endAngle,
        startAngle,
        size: opts.size,
        i,
        totalItems: 2,
        animBeginArr: 0,
        dur: 0,
        isTrack: true,
        easing: w.globals.easing
      })
    }

    return g
  }

  drawArcs(opts) {
    let w = this.w
    // size, donutSize, centerX, centerY, colorArr, lineColorArr, sectorAngleArr, series

    let graphics = new Graphics(this.ctx)
    let fill = new Fill(this.ctx)
    let filters = new Filters(this.ctx)
    let g = graphics.group()

    let strokeWidth = this.getStrokeWidth(opts)
    opts.size = opts.size - strokeWidth / 2

    let hollowFillID = w.config.plotOptions.radialBar.hollow.background
    let hollowSize =
      opts.size -
      strokeWidth * opts.series.length -
      this.margin * opts.series.length -
      (strokeWidth *
        parseInt(w.config.plotOptions.radialBar.track.strokeWidth, 10)) /
        100 /
        2

    let hollowRadius = hollowSize - w.config.plotOptions.radialBar.hollow.margin

    if (w.config.plotOptions.radialBar.hollow.image !== undefined) {
      hollowFillID = this.drawHollowImage(opts, g, hollowSize, hollowFillID)
    }

    let elHollow = this.drawHollow({
      size: hollowRadius,
      centerX: opts.centerX,
      centerY: opts.centerY,
      fill: hollowFillID ? hollowFillID : 'transparent'
    })

    if (w.config.plotOptions.radialBar.hollow.dropShadow.enabled) {
      const shadow = w.config.plotOptions.radialBar.hollow.dropShadow
      filters.dropShadow(elHollow, shadow)
    }

    let shown = 1
    if (!this.radialDataLabels.total.show && w.globals.series.length > 1) {
      shown = 0
    }

    let dataLabels = null

    if (this.radialDataLabels.show) {
      dataLabels = this.renderInnerDataLabels(this.radialDataLabels, {
        hollowSize,
        centerX: opts.centerX,
        centerY: opts.centerY,
        opacity: shown
      })
    }

    if (w.config.plotOptions.radialBar.hollow.position === 'back') {
      g.add(elHollow)
      if (dataLabels) {
        g.add(dataLabels)
      }
    }

    let reverseLoop = false
    if (w.config.plotOptions.radialBar.inverseOrder) {
      reverseLoop = true
    }

    for (
      let i = reverseLoop ? opts.series.length - 1 : 0;
      reverseLoop ? i >= 0 : i < opts.series.length;
      reverseLoop ? i-- : i++
    ) {
      let elRadialBarArc = graphics.group({
        class: `apexcharts-series apexcharts-radial-series`,
        seriesName: Utils.escapeString(w.globals.seriesNames[i])
      })
      g.add(elRadialBarArc)

      elRadialBarArc.attr({
        rel: i + 1,
        'data:realIndex': i
      })

      this.ctx.series.addCollapsedClassToSeries(elRadialBarArc, i)

      opts.size = opts.size - strokeWidth - this.margin

      let pathFill = fill.fillPath({
        seriesNumber: i,
        size: opts.size,
        value: opts.series[i]
      })

      let startAngle = this.startAngle
      let prevStartAngle

      // if data exceeds 100, make it 100
      const dataValue =
        Utils.negToZero(opts.series[i] > 100 ? 100 : opts.series[i]) / 100

      let endAngle = Math.round(this.totalAngle * dataValue) + this.startAngle

      let prevEndAngle
      if (w.globals.dataChanged) {
        prevStartAngle = this.startAngle
        prevEndAngle =
          Math.round(
            (this.totalAngle * Utils.negToZero(w.globals.previousPaths[i])) /
              100
          ) + prevStartAngle
      }

      const currFullAngle = Math.abs(endAngle) + Math.abs(startAngle)
      if (currFullAngle >= 360) {
        endAngle = endAngle - 0.01
      }

      const prevFullAngle = Math.abs(prevEndAngle) + Math.abs(prevStartAngle)
      if (prevFullAngle >= 360) {
        prevEndAngle = prevEndAngle - 0.01
      }

      let angle = endAngle - startAngle

      const dashArray = Array.isArray(w.config.stroke.dashArray)
        ? w.config.stroke.dashArray[i]
        : w.config.stroke.dashArray

      let elPath = graphics.drawPath({
        d: '',
        stroke: pathFill,
        strokeWidth,
        fill: 'none',
        fillOpacity: w.config.fill.opacity,
        classes: 'apexcharts-radialbar-area apexcharts-radialbar-slice-' + i,
        strokeDashArray: dashArray
      })

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

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

      this.addListeners(elPath, this.radialDataLabels)

      elRadialBarArc.add(elPath)

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

      let dur = 0
      if (this.initialAnim && !w.globals.resized && !w.globals.dataChanged) {
        dur = w.config.chart.animations.speed
      }

      if (w.globals.dataChanged) {
        dur = w.config.chart.animations.dynamicAnimation.speed
      }
      this.animDur = dur / (opts.series.length * 1.2) + this.animDur
      this.animBeginArr.push(this.animDur)

      this.animatePaths(elPath, {
        centerX: opts.centerX,
        centerY: opts.centerY,
        endAngle,
        startAngle,
        prevEndAngle,
        prevStartAngle,
        size: opts.size,
        i,
        totalItems: 2,
        animBeginArr: this.animBeginArr,
        dur,
        shouldSetPrevPaths: true,
        easing: w.globals.easing
      })
    }

    return {
      g,
      elHollow,
      dataLabels
    }
  }

  drawHollow(opts) {
    const graphics = new Graphics(this.ctx)

    let circle = graphics.drawCircle(opts.size * 2)

    circle.attr({
      class: 'apexcharts-radialbar-hollow',
      cx: opts.centerX,
      cy: opts.centerY,
      r: opts.size,
      fill: opts.fill
    })

    return circle
  }

  drawHollowImage(opts, g, hollowSize, hollowFillID) {
    const w = this.w
    let fill = new Fill(this.ctx)

    let randID = Utils.randomId()
    let hollowFillImg = w.config.plotOptions.radialBar.hollow.image

    if (w.config.plotOptions.radialBar.hollow.imageClipped) {
      fill.clippedImgArea({
        width: hollowSize,
        height: hollowSize,
        image: hollowFillImg,
        patternID: `pattern${w.globals.cuid}${randID}`
      })
      hollowFillID = `url(#pattern${w.globals.cuid}${randID})`
    } else {
      const imgWidth = w.config.plotOptions.radialBar.hollow.imageWidth
      const imgHeight = w.config.plotOptions.radialBar.hollow.imageHeight
      if (imgWidth === undefined && imgHeight === undefined) {
        let image = w.globals.dom.Paper.image(hollowFillImg).loaded(function(
          loader
        ) {
          this.move(
            opts.centerX -
              loader.width / 2 +
              w.config.plotOptions.radialBar.hollow.imageOffsetX,
            opts.centerY -
              loader.height / 2 +
              w.config.plotOptions.radialBar.hollow.imageOffsetY
          )
        })
        g.add(image)
      } else {
        let image = w.globals.dom.Paper.image(hollowFillImg).loaded(function(
          loader
        ) {
          this.move(
            opts.centerX -
              imgWidth / 2 +
              w.config.plotOptions.radialBar.hollow.imageOffsetX,
            opts.centerY -
              imgHeight / 2 +
              w.config.plotOptions.radialBar.hollow.imageOffsetY
          )
          this.size(imgWidth, imgHeight)
        })
        g.add(image)
      }
    }
    return hollowFillID
  }

  getStrokeWidth(opts) {
    const w = this.w
    return (
      (opts.size *
        (100 - parseInt(w.config.plotOptions.radialBar.hollow.size, 10))) /
        100 /
        (opts.series.length + 1) -
      this.margin
    )
  }
}

export default Radial
