import Animations from '../modules/Animations'
import Fill from '../modules/Fill'
import Filters from '../modules/Filters'
import Graphics from '../modules/Graphics'
import Markers from '../modules/Markers'

/**
 * ApexCharts Scatter Class.
 * This Class also handles bubbles chart as currently there is no major difference in drawing them,
 * @module Scatter
 **/
export default class Scatter {
  constructor(ctx) {
    this.ctx = ctx
    this.w = ctx.w

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

  draw(elSeries, j, opts) {
    let w = this.w

    let graphics = new Graphics(this.ctx)

    let realIndex = opts.realIndex
    let pointsPos = opts.pointsPos
    let zRatio = opts.zRatio
    let elPointsMain = opts.elParent

    let elPointsWrap = graphics.group({
      class: `apexcharts-series-markers apexcharts-series-${w.config.chart.type}`
    })

    elPointsWrap.attr('clip-path', `url(#gridRectMarkerMask${w.globals.cuid})`)

    if (Array.isArray(pointsPos.x)) {
      for (let q = 0; q < pointsPos.x.length; q++) {
        let dataPointIndex = j + 1
        let shouldDraw = true

        // a small hack as we have 2 points for the first val to connect it
        if (j === 0 && q === 0) dataPointIndex = 0
        if (j === 0 && q === 1) dataPointIndex = 1

        let radius = 0
        let finishRadius = w.globals.markers.size[realIndex]

        if (zRatio !== Infinity) {
          // means we have a bubble
          finishRadius = w.globals.seriesZ[realIndex][dataPointIndex] / zRatio

          const bubble = w.config.plotOptions.bubble
          if (bubble.minBubbleRadius && finishRadius < bubble.minBubbleRadius) {
            finishRadius = bubble.minBubbleRadius
          }

          if (bubble.maxBubbleRadius && finishRadius > bubble.maxBubbleRadius) {
            finishRadius = bubble.maxBubbleRadius
          }
        }

        if (!w.config.chart.animations.enabled) {
          radius = finishRadius
        }

        let x = pointsPos.x[q]
        let y = pointsPos.y[q]

        radius = radius || 0

        if (
          y === null ||
          typeof w.globals.series[realIndex][dataPointIndex] === 'undefined'
        ) {
          shouldDraw = false
        }

        if (shouldDraw) {
          const point = this.drawPoint(
            x,
            y,
            radius,
            finishRadius,
            realIndex,
            dataPointIndex,
            j
          )
          elPointsWrap.add(point)
        }

        elPointsMain.add(elPointsWrap)
      }
    }
  }

  drawPoint(x, y, radius, finishRadius, realIndex, dataPointIndex, j) {
    const w = this.w

    let i = realIndex
    let anim = new Animations(this.ctx)
    let filters = new Filters(this.ctx)
    let fill = new Fill(this.ctx)
    let markers = new Markers(this.ctx)
    const graphics = new Graphics(this.ctx)

    const markerConfig = markers.getMarkerConfig({
      cssClass: 'apexcharts-marker',
      seriesIndex: i,
      dataPointIndex,
      finishRadius:
        w.config.chart.type === 'bubble' ||
        (w.globals.comboCharts &&
          w.config.series[realIndex] &&
          w.config.series[realIndex].type === 'bubble')
          ? finishRadius
          : null
    })

    finishRadius = markerConfig.pSize

    let pathFillCircle = fill.fillPath({
      seriesNumber: realIndex,
      dataPointIndex,
      color: markerConfig.pointFillColor,
      patternUnits: 'objectBoundingBox',
      value: w.globals.series[realIndex][j]
    })

    let el
    if (markerConfig.shape === 'circle') {
      el = graphics.drawCircle(radius)
    } else if (
      markerConfig.shape === 'square' ||
      markerConfig.shape === 'rect'
    ) {
      el = graphics.drawRect(
        0,
        0,
        markerConfig.width - markerConfig.pointStrokeWidth / 2,
        markerConfig.height - markerConfig.pointStrokeWidth / 2,
        markerConfig.pRadius
      )
    }

    if (w.config.series[i].data[dataPointIndex]) {
      if (w.config.series[i].data[dataPointIndex].fillColor) {
        pathFillCircle = w.config.series[i].data[dataPointIndex].fillColor
      }
    }

    el.attr({
      x: x - markerConfig.width / 2 - markerConfig.pointStrokeWidth / 2,
      y: y - markerConfig.height / 2 - markerConfig.pointStrokeWidth / 2,
      cx: x,
      cy: y,
      fill: pathFillCircle,
      'fill-opacity': markerConfig.pointFillOpacity,
      stroke: markerConfig.pointStrokeColor,
      r: finishRadius,
      'stroke-width': markerConfig.pointStrokeWidth,
      'stroke-dasharray': markerConfig.pointStrokeDashArray,
      'stroke-opacity': markerConfig.pointStrokeOpacity
    })

    if (w.config.chart.dropShadow.enabled) {
      const dropShadow = w.config.chart.dropShadow
      filters.dropShadow(el, dropShadow, realIndex)
    }

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

      anim.animateMarker(
        el,
        0,
        markerConfig.shape === 'circle'
          ? finishRadius
          : { width: markerConfig.width, height: markerConfig.height },
        speed,
        w.globals.easing,
        () => {
          window.setTimeout(() => {
            anim.animationCompleted(el)
          }, 100)
        }
      )
    } else {
      w.globals.animationEnded = true
    }

    if (w.globals.dataChanged && markerConfig.shape === 'circle') {
      if (this.dynamicAnim) {
        let speed = w.config.chart.animations.dynamicAnimation.speed
        let prevX, prevY, prevR

        let prevPathJ = null

        prevPathJ =
          w.globals.previousPaths[realIndex] &&
          w.globals.previousPaths[realIndex][j]

        if (typeof prevPathJ !== 'undefined' && prevPathJ !== null) {
          // series containing less elements will ignore these values and revert to 0
          prevX = prevPathJ.x
          prevY = prevPathJ.y
          prevR =
            typeof prevPathJ.r !== 'undefined' ? prevPathJ.r : finishRadius
        }

        for (let cs = 0; cs < w.globals.collapsedSeries.length; cs++) {
          if (w.globals.collapsedSeries[cs].index === realIndex) {
            speed = 1
            finishRadius = 0
          }
        }

        if (x === 0 && y === 0) finishRadius = 0

        anim.animateCircle(
          el,
          {
            cx: prevX,
            cy: prevY,
            r: prevR
          },
          {
            cx: x,
            cy: y,
            r: finishRadius
          },
          speed,
          w.globals.easing
        )
      } else {
        el.attr({
          r: finishRadius
        })
      }
    }

    el.attr({
      rel: dataPointIndex,
      j: dataPointIndex,
      index: realIndex,
      'default-marker-size': finishRadius
    })

    filters.setSelectionFilter(el, realIndex, dataPointIndex)
    markers.addEvents(el)

    el.node.classList.add('apexcharts-marker')

    return el
  }

  centerTextInBubble(y) {
    let w = this.w
    y = y + parseInt(w.config.dataLabels.style.fontSize, 10) / 4

    return {
      y
    }
  }
}
