text: import '../libs/Treemap-squared'
  import Graphics from '../modules/Graphics'
  import Animations from '../modules/Animations'
  import Fill from '../modules/Fill'
  import Helpers from './common/treemap/Helpers'
  
  import Utils from '../utils/Utils'
  
  /**
   * ApexCharts TreemapChart Class.
   * @module TreemapChart
   **/
  
  export default class TreemapChart {
    constructor(ctx, xyRatios) {
      this.ctx = ctx
      this.w = ctx.w
  
      this.strokeWidth = this.w.config.stroke.width
      this.helpers = new Helpers(ctx)
      this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation
  
      this.labels = []
    }
  
    draw(series) {
      let w = this.w
      const graphics = new Graphics(this.ctx)
      const fill = new Fill(this.ctx)
  
      let ret = graphics.group({
        class: 'apexcharts-treemap'
      })
  
      let ser = []
      series.forEach((s) => {
        let d = s.map((v) => {
          return Math.abs(v)
        })
        ser.push(d)
      })
  
      this.negRange = this.helpers.checkColorRange()
  
      w.config.series.forEach((s, i) => {
        s.data.forEach((l) => {
          if (!Array.isArray(this.labels[i])) this.labels[i] = []
          this.labels[i].push(l.x)
        })
      })
  
      const nodes = window.TreemapSquared.generate(
        ser,
        w.globals.gridWidth,
        w.globals.gridHeight
      )
  
      nodes.forEach((node, i) => {
        let elSeries = graphics.group({
          class: `apexcharts-series apexcharts-treemap-series`,
          seriesName: Utils.escapeString(w.globals.seriesNames[i]),
          rel: i + 1,
          'data:realIndex': i
        })
  
        let elDataLabelWrap = graphics.group({
          class: 'apexcharts-data-labels'
        })
  
        node.forEach((r, j) => {
          const x1 = r[0]
          const y1 = r[1]
          const x2 = r[2]
          const y2 = r[3]
          let elRect = graphics.drawRect(
            x1,
            y1,
            x2 - x1,
            y2 - y1,
            0,
            '#fff',
            1,
            this.strokeWidth,
            w.config.plotOptions.treemap.useFillColorAsStroke
              ? color
              : w.globals.stroke.colors[i]
          )
          elRect.attr({
            cx: x1,
            cy: y1,
            i,
            j,
            width: x2 - x1,
            height: y2 - y1
          })
  
          let colorProps = this.helpers.getShadeColor(
            w.config.chart.type,
            i,
            j,
            this.negRange
          )
          let color = colorProps.color
  
          if (
            typeof w.config.series[i].data[j] !== 'undefined' &&
            w.config.series[i].data[j].fillColor
          ) {
            color = w.config.series[i].data[j].fillColor
          }
          let pathFill = fill.fillPath({
            color,
            seriesNumber: i,
            dataPointIndex: j
          })
  
          elRect.node.classList.add('apexcharts-treemap-rect')
  
          elRect.attr({
            fill: pathFill
          })
  
          this.helpers.addListeners(elRect)
  
          let fromRect = {
            x: x1 + (x2 - x1) / 2,
            y: y1 + (y2 - y1) / 2,
            width: 0,
            height: 0
          }
          let toRect = {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1
          }
  
          if (w.config.chart.animations.enabled && !w.globals.dataChanged) {
            let speed = 1
            if (!w.globals.resized) {
              speed = w.config.chart.animations.speed
            }
            this.animateTreemap(elRect, fromRect, toRect, speed)
          }
          if (w.globals.dataChanged) {
            let speed = 1
            if (this.dynamicAnim.enabled && w.globals.shouldAnimate) {
              speed = this.dynamicAnim.speed
  
              if (
                w.globals.previousPaths[i][j] &&
                w.globals.previousPaths[i][j].rect
              ) {
                fromRect = w.globals.previousPaths[i][j].rect
              }
  
              this.animateTreemap(elRect, fromRect, toRect, speed)
            }
          }
  
          const fontSize = this.getFontSize(r)
  
          let formattedText = w.config.dataLabels.formatter(this.labels[i][j], {
            value: w.globals.series[i][j],
            seriesIndex: i,
            dataPointIndex: j,
            w
          })
          let dataLabels = this.helpers.calculateDataLabels({
            text: formattedText,
            x: (x1 + x2) / 2,
            y: (y1 + y2) / 2 + this.strokeWidth / 2 + fontSize / 3,
            i,
            j,
            colorProps,
            fontSize,
            series
          })
          this.rotateToFitLabel(dataLabels, formattedText, x1, y1, x2, y2)
          elSeries.add(elRect)
  
          if (dataLabels !== null) {
            elSeries.add(dataLabels)
          }
        })
        elSeries.add(elDataLabelWrap)
  
        ret.add(elSeries)
      })
  
      return ret
    }
  
    // This calculates a font-size based upon
    // average label length and the size of the box the label is
    // going into. The maximum font size is set in chart config.
    getFontSize(coordinates) {
      const w = this.w
  
      // total length of labels (i.e [["Italy"],["Spain", "Greece"]] -> 16)
      function totalLabelLength(arr) {
        let i,
          total = 0
        if (Array.isArray(arr[0])) {
          for (i = 0; i < arr.length; i++) {
            total += totalLabelLength(arr[i])
          }
        } else {
          for (i = 0; i < arr.length; i++) {
            total += arr[i].length
          }
        }
        return total
      }
  
      // count of labels (i.e [["Italy"],["Spain", "Greece"]] -> 3)
      function countLabels(arr) {
        let i,
          total = 0
        if (Array.isArray(arr[0])) {
          for (i = 0; i < arr.length; i++) {
            total += countLabels(arr[i])
          }
        } else {
          for (i = 0; i < arr.length; i++) {
            total += 1
          }
        }
        return total
      }
      let averagelabelsize =
        totalLabelLength(this.labels) / countLabels(this.labels)
  
      function fontSize(width, height) {
        // the font size should be proportional to the size of the box (and the value)
        // otherwise you can end up creating a visual distortion where two boxes of identical
        // size have different sized labels, and thus make it look as if the two boxes
        // represent diffferent sizes
        let area = width * height
        let arearoot = Math.pow(area, 0.5)
        return Math.min(
          arearoot / averagelabelsize,
          parseInt(w.config.dataLabels.style.fontSize, 10)
        )
      }
  
      return fontSize(
        coordinates[2] - coordinates[0],
        coordinates[3] - coordinates[1]
      )
    }
  
    rotateToFitLabel(elText, text, x1, y1, x2, y2) {
      const graphics = new Graphics(this.ctx)
      const textRect = graphics.getTextRects(text)
      //if the label fits better sideways then rotate it
      if (textRect.width + 5 > x2 - x1 && textRect.width <= y2 - y1) {
        let labelRotatingCenter = graphics.rotateAroundCenter(elText.node)
  
        elText.node.setAttribute(
          'transform',
          `rotate(-90 ${labelRotatingCenter.x} ${labelRotatingCenter.y})`
        )
      }
    }
  
    animateTreemap(el, fromRect, toRect, speed) {
      const animations = new Animations(this.ctx)
      animations.animateRect(
        el,
        {
          x: fromRect.x,
          y: fromRect.y,
          width: fromRect.width,
          height: fromRect.height
        },
        {
          x: toRect.x,
          y: toRect.y,
          width: toRect.width,
          height: toRect.height
        },
        speed,
        () => {
          animations.animationCompleted(el)
        }
      )
    }
  }
  