text: import DataLabels from '../modules/DataLabels'
  import Animations from '../modules/Animations'
  import Graphics from '../modules/Graphics'
  import Fill from '../modules/Fill'
  import Utils from '../utils/Utils'
  import Filters from '../modules/Filters'
  
  /**
   * ApexCharts HeatMap Class.
   * @module HeatMap
   **/
  
  export default class HeatMap {
    constructor(ctx, xyRatios) {
      this.ctx = ctx
      this.w = ctx.w
  
      this.xRatio = xyRatios.xRatio
      this.yRatio = xyRatios.yRatio
  
      this.negRange = false
  
      this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation
  
      this.rectRadius = this.w.config.plotOptions.heatmap.radius
      this.strokeWidth = this.w.config.stroke.width
    }
  
    draw(series) {
      let w = this.w
      const graphics = new Graphics(this.ctx)
  
      let ret = graphics.group({
        class: 'apexcharts-heatmap'
      })
  
      ret.attr('clip-path', `url(#gridRectMask${w.globals.cuid})`)
  
      // width divided into equal parts
      let xDivision = w.globals.gridWidth / w.globals.dataPoints
      let yDivision = w.globals.gridHeight / w.globals.series.length
  
      let y1 = 0
      let rev = false
  
      this.checkColorRange()
  
      let heatSeries = series.slice()
      if (w.config.yaxis[0].reversed) {
        rev = true
        heatSeries.reverse()
      }
  
      for (
        let i = rev ? 0 : heatSeries.length - 1;
        rev ? i < heatSeries.length : i >= 0;
        rev ? i++ : i--
      ) {
        // el to which series will be drawn
        let elSeries = graphics.group({
          class: `apexcharts-series apexcharts-heatmap-series`,
          seriesName: Utils.escapeString(w.globals.seriesNames[i]),
          rel: i + 1,
          'data:realIndex': i
        })
  
        if (w.config.chart.dropShadow.enabled) {
          const shadow = w.config.chart.dropShadow
          const filters = new Filters(this.ctx)
          filters.dropShadow(elSeries, shadow, i)
        }
  
        let x1 = 0
  
        for (let j = 0; j < heatSeries[i].length; j++) {
          let colorShadePercent = 1
          let shadeIntensity = w.config.plotOptions.heatmap.shadeIntensity
  
          const heatColorProps = this.determineHeatColor(i, j)
  
          if (w.globals.hasNegs || this.negRange) {
            if (w.config.plotOptions.heatmap.reverseNegativeShade) {
              if (heatColorProps.percent < 0) {
                colorShadePercent =
                  (heatColorProps.percent / 100) * (shadeIntensity * 1.25)
              } else {
                colorShadePercent =
                  (1 - heatColorProps.percent / 100) * (shadeIntensity * 1.25)
              }
            } else {
              if (heatColorProps.percent < 0) {
                colorShadePercent =
                  1 - (1 + heatColorProps.percent / 100) * shadeIntensity
              } else {
                colorShadePercent =
                  (1 - heatColorProps.percent / 100) * shadeIntensity
              }
            }
          } else {
            colorShadePercent = 1 - heatColorProps.percent / 100
          }
  
          let color = heatColorProps.color
          let utils = new Utils()
  
          if (w.config.plotOptions.heatmap.enableShades) {
            color = Utils.hexToRgba(
              utils.shadeColor(colorShadePercent, heatColorProps.color),
              w.config.fill.opacity
            )
          }
  
          if (w.config.fill.type === 'image') {
            const fill = new Fill(this.ctx)
  
            color = fill.fillPath({
              seriesNumber: i,
              opacity: w.globals.hasNegs
                ? heatColorProps.percent < 0
                  ? 1 - (1 + heatColorProps.percent / 100)
                  : shadeIntensity + heatColorProps.percent / 100
                : heatColorProps.percent / 100,
              patternID: Utils.randomId(),
              width: w.config.fill.image.width
                ? w.config.fill.image.width
                : xDivision,
              height: w.config.fill.image.height
                ? w.config.fill.image.height
                : yDivision
            })
          }
  
          let radius = this.rectRadius
  
          let rect = graphics.drawRect(x1, y1, xDivision, yDivision, radius)
          rect.attr({
            cx: x1,
            cy: y1
          })
  
          rect.node.classList.add('apexcharts-heatmap-rect')
          elSeries.add(rect)
  
          rect.attr({
            fill: color,
            i,
            index: i,
            j,
            val: heatSeries[i][j],
            'stroke-width': this.strokeWidth,
            stroke: w.globals.stroke.colors[0],
            color
          })
  
          rect.node.addEventListener(
            'mouseenter',
            graphics.pathMouseEnter.bind(this, rect)
          )
          rect.node.addEventListener(
            'mouseleave',
            graphics.pathMouseLeave.bind(this, rect)
          )
          rect.node.addEventListener(
            'mousedown',
            graphics.pathMouseDown.bind(this, rect)
          )
  
          if (w.config.chart.animations.enabled && !w.globals.dataChanged) {
            let speed = 1
            if (!w.globals.resized) {
              speed = w.config.chart.animations.speed
            }
            this.animateHeatMap(rect, x1, y1, xDivision, yDivision, speed)
          }
  
          if (w.globals.dataChanged) {
            let speed = 1
            if (this.dynamicAnim.enabled && w.globals.shouldAnimate) {
              speed = this.dynamicAnim.speed
  
              let colorFrom =
                w.globals.previousPaths[i] &&
                w.globals.previousPaths[i][j] &&
                w.globals.previousPaths[i][j].color
  
              if (!colorFrom) colorFrom = 'rgba(255, 255, 255, 0)'
  
              this.animateHeatColor(
                rect,
                Utils.isColorHex(colorFrom)
                  ? colorFrom
                  : Utils.rgb2hex(colorFrom),
                Utils.isColorHex(color) ? color : Utils.rgb2hex(color),
                speed
              )
            }
          }
  
          let dataLabels = this.calculateHeatmapDataLabels({
            x: x1,
            y: y1,
            i,
            j,
            series: heatSeries,
            rectHeight: yDivision,
            rectWidth: xDivision
          })
          if (dataLabels !== null) {
            elSeries.add(dataLabels)
          }
  
          x1 = x1 + xDivision
        }
  
        y1 = y1 + yDivision
  
        ret.add(elSeries)
      }
  
      // adjust yaxis labels for heatmap
      let yAxisScale = w.globals.yAxisScale[0].result.slice()
      if (w.config.yaxis[0].reversed) {
        yAxisScale.unshift('')
      } else {
        yAxisScale.push('')
      }
      w.globals.yAxisScale[0].result = yAxisScale
      let divisor = w.globals.gridHeight / w.globals.series.length
      w.config.yaxis[0].labels.offsetY = -(divisor / 2)
  
      return ret
    }
  
    checkColorRange() {
      const w = this.w
  
      let heatmap = w.config.plotOptions.heatmap
  
      if (heatmap.colorScale.ranges.length > 0) {
        heatmap.colorScale.ranges.map((range, index) => {
          if (range.from < 0) {
            this.negRange = true
          }
        })
      }
    }
  
    determineHeatColor(i, j) {
      const w = this.w
  
      let val = w.globals.series[i][j]
  
      let heatmap = w.config.plotOptions.heatmap
  
      let seriesNumber = heatmap.colorScale.inverse ? j : i
  
      let color = w.globals.colors[seriesNumber]
      let min = Math.min(...w.globals.series[i])
      let max = Math.max(...w.globals.series[i])
  
      if (!heatmap.distributed) {
        min = w.globals.minY
        max = w.globals.maxY
      }
  
      if (typeof heatmap.colorScale.min !== 'undefined') {
        min =
          heatmap.colorScale.min < w.globals.minY
            ? heatmap.colorScale.min
            : w.globals.minY
        max =
          heatmap.colorScale.max > w.globals.maxY
            ? heatmap.colorScale.max
            : w.globals.maxY
      }
  
      let total = Math.abs(max) + Math.abs(min)
  
      if (val === 0) {
        // to avoid invalid percentage for 0 values
        val = 0.000001
      }
  
      let percent = (100 * val) / (total === 0 ? total - 0.000001 : total)
  
      if (heatmap.colorScale.ranges.length > 0) {
        const colorRange = heatmap.colorScale.ranges
        colorRange.map((range, index) => {
          if (val >= range.from && val <= range.to) {
            color = range.color
            min = range.from
            max = range.to
            let rTotal = Math.abs(max) + Math.abs(min)
            percent = (100 * val) / (rTotal === 0 ? rTotal - 0.000001 : rTotal)
          }
        })
      }
  
      return {
        color,
        percent
      }
    }
  
    calculateHeatmapDataLabels({ x, y, i, j, series, rectHeight, rectWidth }) {
      let w = this.w
      // let graphics = new Graphics(this.ctx)
      let dataLabelsConfig = w.config.dataLabels
  
      const graphics = new Graphics(this.ctx)
  
      let dataLabels = new DataLabels(this.ctx)
      let formatter = dataLabelsConfig.formatter
  
      let elDataLabelsWrap = null
  
      if (dataLabelsConfig.enabled) {
        elDataLabelsWrap = graphics.group({
          class: 'apexcharts-data-labels'
        })
  
        const offX = dataLabelsConfig.offsetX
        const offY = dataLabelsConfig.offsetY
  
        let dataLabelsX = x + rectWidth / 2 + offX
        let dataLabelsY =
          y +
          rectHeight / 2 +
          parseFloat(dataLabelsConfig.style.fontSize) / 3 +
          offY
  
        let text = formatter(w.globals.series[i][j], {
          seriesIndex: i,
          dataPointIndex: j,
          w
        })
        dataLabels.plotDataLabelsText({
          x: dataLabelsX,
          y: dataLabelsY,
          text,
          i,
          j,
          parent: elDataLabelsWrap,
          dataLabelsConfig
        })
      }
  
      return elDataLabelsWrap
    }
  
    animateHeatMap(el, x, y, width, height, speed) {
      const animations = new Animations(this.ctx)
      animations.animateRect(
        el,
        {
          x: x + width / 2,
          y: y + height / 2,
          width: 0,
          height: 0
        },
        {
          x,
          y,
          width,
          height
        },
        speed,
        () => {
          animations.animationCompleted(el)
        }
      )
    }
  
    animateHeatColor(el, colorFrom, colorTo, speed) {
      el.attr({
        fill: colorFrom
      })
        .animate(speed)
        .attr({
          fill: colorTo
        })
    }
  }
  