import Graphics from '../Graphics'
import Utils from '../../utils/Utils'
import AxesUtils from './AxesUtils'

/**
 * ApexCharts YAxis Class for drawing Y-Axis.
 *
 * @module YAxis
 **/

export default class YAxis {
  constructor(ctx, elgrid) {
    this.ctx = ctx
    this.elgrid = elgrid
    this.w = ctx.w
    const w = this.w

    this.xaxisFontSize = w.config.xaxis.labels.style.fontSize
    this.axisFontFamily = w.config.xaxis.labels.style.fontFamily

    this.xaxisForeColors = w.config.xaxis.labels.style.colors
    this.isCategoryBarHorizontal =
      w.config.chart.type === 'bar' && w.config.plotOptions.bar.horizontal

    this.xAxisoffX = 0
    if (w.config.xaxis.position === 'bottom') {
      this.xAxisoffX = w.globals.gridHeight
    }
    this.drawnLabels = []
    this.axesUtils = new AxesUtils(ctx)
  }

  drawYaxis(realIndex) {
    let w = this.w
    let graphics = new Graphics(this.ctx)

    const yaxisStyle = w.config.yaxis[realIndex].labels.style
    let yaxisFontSize = yaxisStyle.fontSize
    let yaxisFontFamily = yaxisStyle.fontFamily
    let yaxisFontWeight = yaxisStyle.fontWeight

    let elYaxis = graphics.group({
      class: 'apexcharts-yaxis',
      rel: realIndex,
      transform: 'translate(' + w.globals.translateYAxisX[realIndex] + ', 0)',
    })

    if (this.axesUtils.isYAxisHidden(realIndex)) {
      return elYaxis
    }

    let elYaxisTexts = graphics.group({
      class: 'apexcharts-yaxis-texts-g',
    })

    elYaxis.add(elYaxisTexts)

    let tickAmount = w.globals.yAxisScale[realIndex].result.length - 1

    // labelsDivider is simply svg height/number of ticks
    let labelsDivider = w.globals.gridHeight / tickAmount

    // initial label position = 0;
    let l = w.globals.translateY
    let lbFormatter = w.globals.yLabelFormatters[realIndex]

    let labels = w.globals.yAxisScale[realIndex].result.slice()

    labels = this.axesUtils.checkForReversedLabels(realIndex, labels)

    let firstLabel = ''
    if (w.config.yaxis[realIndex].labels.show) {
      for (let i = tickAmount; i >= 0; i--) {
        let val = labels[i]

        val = lbFormatter(val, i, w)

        let xPad = w.config.yaxis[realIndex].labels.padding
        if (w.config.yaxis[realIndex].opposite && w.config.yaxis.length !== 0) {
          xPad = xPad * -1
        }

        let textAnchor = 'end'
        if (w.config.yaxis[realIndex].opposite) {
          textAnchor = 'start'
        }
        if (w.config.yaxis[realIndex].labels.align === 'left') {
          textAnchor = 'start'
        } else if (w.config.yaxis[realIndex].labels.align === 'center') {
          textAnchor = 'middle'
        } else if (w.config.yaxis[realIndex].labels.align === 'right') {
          textAnchor = 'end'
        }

        const yColors = this.axesUtils.getYAxisForeColor(
          yaxisStyle.colors,
          realIndex
        )
        const getForeColor = () => {
          return Array.isArray(yColors) ? yColors[i] : yColors
        }

        let offsetY = w.config.yaxis[realIndex].labels.offsetY

        if (w.config.chart.type === 'heatmap') {
          const divisor = w.globals.gridHeight / w.globals.series.length - 1
          offsetY = offsetY - divisor / 2
        }

        let label = graphics.drawText({
          x: xPad,
          y: l + tickAmount / 10 + offsetY + 1,
          text: val,
          textAnchor,
          fontSize: yaxisFontSize,
          fontFamily: yaxisFontFamily,
          fontWeight: yaxisFontWeight,
          maxWidth: w.config.yaxis[realIndex].labels.maxWidth,
          foreColor: getForeColor(),
          isPlainText: false,
          cssClass: 'apexcharts-yaxis-label ' + yaxisStyle.cssClass,
        })
        if (i === tickAmount) {
          firstLabel = label
        }
        elYaxisTexts.add(label)

        let elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title')
        elTooltipTitle.textContent = Array.isArray(val) ? val.join(' ') : val
        label.node.appendChild(elTooltipTitle)

        if (w.config.yaxis[realIndex].labels.rotate !== 0) {
          let firstabelRotatingCenter = graphics.rotateAroundCenter(
            firstLabel.node
          )
          let labelRotatingCenter = graphics.rotateAroundCenter(label.node)
          label.node.setAttribute(
            'transform',
            `rotate(${w.config.yaxis[realIndex].labels.rotate} ${firstabelRotatingCenter.x} ${labelRotatingCenter.y})`
          )
        }
        l = l + labelsDivider
      }
    }

    if (w.config.yaxis[realIndex].title.text !== undefined) {
      let elYaxisTitle = graphics.group({
        class: 'apexcharts-yaxis-title',
      })

      let x = 0
      if (w.config.yaxis[realIndex].opposite) {
        x = w.globals.translateYAxisX[realIndex]
      }
      let elYAxisTitleText = graphics.drawText({
        x,
        y:
          w.globals.gridHeight / 2 +
          w.globals.translateY +
          w.config.yaxis[realIndex].title.offsetY,
        text: w.config.yaxis[realIndex].title.text,
        textAnchor: 'end',
        foreColor: w.config.yaxis[realIndex].title.style.color,
        fontSize: w.config.yaxis[realIndex].title.style.fontSize,
        fontWeight: w.config.yaxis[realIndex].title.style.fontWeight,
        fontFamily: w.config.yaxis[realIndex].title.style.fontFamily,
        cssClass:
          'apexcharts-yaxis-title-text ' +
          w.config.yaxis[realIndex].title.style.cssClass,
      })

      elYaxisTitle.add(elYAxisTitleText)

      elYaxis.add(elYaxisTitle)
    }

    let axisBorder = w.config.yaxis[realIndex].axisBorder

    let x = 31 + axisBorder.offsetX
    if (w.config.yaxis[realIndex].opposite) {
      x = -31 - axisBorder.offsetX
    }

    if (axisBorder.show) {
      let elVerticalLine = graphics.drawLine(
        x,
        w.globals.translateY + axisBorder.offsetY - 2,
        x,
        w.globals.gridHeight + w.globals.translateY + axisBorder.offsetY + 2,
        axisBorder.color,
        0,
        axisBorder.width
      )

      elYaxis.add(elVerticalLine)
    }
    if (w.config.yaxis[realIndex].axisTicks.show) {
      this.axesUtils.drawYAxisTicks(
        x,
        tickAmount,
        axisBorder,
        w.config.yaxis[realIndex].axisTicks,
        realIndex,
        labelsDivider,
        elYaxis
      )
    }

    return elYaxis
  }

  // This actually becomes horizontal axis (for bar charts)
  drawYaxisInversed(realIndex) {
    let w = this.w
    let graphics = new Graphics(this.ctx)

    let elXaxis = graphics.group({
      class: 'apexcharts-xaxis apexcharts-yaxis-inversed',
    })

    let elXaxisTexts = graphics.group({
      class: 'apexcharts-xaxis-texts-g',
      transform: `translate(${w.globals.translateXAxisX}, ${w.globals.translateXAxisY})`,
    })

    elXaxis.add(elXaxisTexts)

    let tickAmount = w.globals.yAxisScale[realIndex].result.length - 1

    // labelsDivider is simply svg width/number of ticks
    let labelsDivider = w.globals.gridWidth / tickAmount + 0.1

    // initial label position;
    let l = labelsDivider + w.config.xaxis.labels.offsetX

    let lbFormatter = w.globals.xLabelFormatter

    let labels = w.globals.yAxisScale[realIndex].result.slice()

    let timescaleLabels = w.globals.timescaleLabels
    if (timescaleLabels.length > 0) {
      this.xaxisLabels = timescaleLabels.slice()
      labels = timescaleLabels.slice()
      tickAmount = labels.length
    }

    labels = this.axesUtils.checkForReversedLabels(realIndex, labels)

    const tl = timescaleLabels.length

    if (w.config.xaxis.labels.show) {
      for (let i = tl ? 0 : tickAmount; tl ? i < tl : i >= 0; tl ? i++ : i--) {
        let val = labels[i]
        val = lbFormatter(val, i, w)

        let x =
          w.globals.gridWidth +
          w.globals.padHorizontal -
          (l - labelsDivider + w.config.xaxis.labels.offsetX)

        if (timescaleLabels.length) {
          let label = this.axesUtils.getLabel(
            labels,
            timescaleLabels,
            x,
            i,
            this.drawnLabels,
            this.xaxisFontSize
          )
          x = label.x
          val = label.text
          this.drawnLabels.push(label.text)

          if (i === 0 && w.globals.skipFirstTimelinelabel) {
            val = ''
          }
          if (i === labels.length - 1 && w.globals.skipLastTimelinelabel) {
            val = ''
          }
        }
        let elTick = graphics.drawText({
          x,
          y:
            this.xAxisoffX +
            w.config.xaxis.labels.offsetY +
            30 -
            (w.config.xaxis.position === 'top'
              ? w.globals.xAxisHeight + w.config.xaxis.axisTicks.height - 2
              : 0),
          text: val,
          textAnchor: 'middle',
          foreColor: Array.isArray(this.xaxisForeColors)
            ? this.xaxisForeColors[realIndex]
            : this.xaxisForeColors,
          fontSize: this.xaxisFontSize,
          fontFamily: this.xaxisFontFamily,
          fontWeight: w.config.xaxis.labels.style.fontWeight,
          isPlainText: false,
          cssClass:
            'apexcharts-xaxis-label ' + w.config.xaxis.labels.style.cssClass,
        })

        elXaxisTexts.add(elTick)

        elTick.tspan(val)

        let elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title')
        elTooltipTitle.textContent = val
        elTick.node.appendChild(elTooltipTitle)

        l = l + labelsDivider
      }
    }

    this.inversedYAxisTitleText(elXaxis)
    this.inversedYAxisBorder(elXaxis)

    return elXaxis
  }

  inversedYAxisBorder(parent) {
    const w = this.w
    const graphics = new Graphics(this.ctx)

    let axisBorder = w.config.xaxis.axisBorder
    if (axisBorder.show) {
      let lineCorrection = 0
      if (w.config.chart.type === 'bar' && w.globals.isXNumeric) {
        lineCorrection = lineCorrection - 15
      }
      let elHorzLine = graphics.drawLine(
        w.globals.padHorizontal + lineCorrection + axisBorder.offsetX,
        this.xAxisoffX,
        w.globals.gridWidth,
        this.xAxisoffX,
        axisBorder.color,
        0,
        axisBorder.height
      )

      // in horizontal bars, we append axisBorder to elGridBorders element to avoid z-index issues
      if (this.elgrid && this.elgrid.elGridBorders && w.config.grid.show) {
        this.elgrid.elGridBorders.add(elHorzLine)
      } else {
        parent.add(elHorzLine)
      }
    }
  }

  inversedYAxisTitleText(parent) {
    const w = this.w
    const graphics = new Graphics(this.ctx)
    if (w.config.xaxis.title.text !== undefined) {
      let elYaxisTitle = graphics.group({
        class: 'apexcharts-xaxis-title apexcharts-yaxis-title-inversed',
      })

      let elYAxisTitleText = graphics.drawText({
        x: w.globals.gridWidth / 2 + w.config.xaxis.title.offsetX,
        y:
          this.xAxisoffX +
          parseFloat(this.xaxisFontSize) +
          parseFloat(w.config.xaxis.title.style.fontSize) +
          w.config.xaxis.title.offsetY +
          20,
        text: w.config.xaxis.title.text,
        textAnchor: 'middle',
        fontSize: w.config.xaxis.title.style.fontSize,
        fontFamily: w.config.xaxis.title.style.fontFamily,
        fontWeight: w.config.xaxis.title.style.fontWeight,
        foreColor: w.config.xaxis.title.style.color,
        cssClass:
          'apexcharts-xaxis-title-text ' + w.config.xaxis.title.style.cssClass,
      })

      elYaxisTitle.add(elYAxisTitleText)

      parent.add(elYaxisTitle)
    }
  }

  yAxisTitleRotate(realIndex, yAxisOpposite) {
    let w = this.w

    let graphics = new Graphics(this.ctx)

    let yAxisLabelsCoord = {
      width: 0,
      height: 0,
    }
    let yAxisTitleCoord = {
      width: 0,
      height: 0,
    }

    let elYAxisLabelsWrap = w.globals.dom.baseEl.querySelector(
      ` .apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-texts-g`
    )

    if (elYAxisLabelsWrap !== null) {
      yAxisLabelsCoord = elYAxisLabelsWrap.getBoundingClientRect()
    }

    let yAxisTitle = w.globals.dom.baseEl.querySelector(
      `.apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-title text`
    )

    if (yAxisTitle !== null) {
      yAxisTitleCoord = yAxisTitle.getBoundingClientRect()
    }

    if (yAxisTitle !== null) {
      let x = this.xPaddingForYAxisTitle(
        realIndex,
        yAxisLabelsCoord,
        yAxisTitleCoord,
        yAxisOpposite
      )

      yAxisTitle.setAttribute('x', x.xPos - (yAxisOpposite ? 10 : 0))
    }

    if (yAxisTitle !== null) {
      let titleRotatingCenter = graphics.rotateAroundCenter(yAxisTitle)
      yAxisTitle.setAttribute(
        'transform',
        `rotate(${
          yAxisOpposite
            ? w.config.yaxis[realIndex].title.rotate * -1
            : w.config.yaxis[realIndex].title.rotate
        } ${titleRotatingCenter.x} ${titleRotatingCenter.y})`
      )
    }
  }

  xPaddingForYAxisTitle(
    realIndex,
    yAxisLabelsCoord,
    yAxisTitleCoord,
    yAxisOpposite
  ) {
    let w = this.w
    let oppositeAxisCount = 0

    let x = 0
    let padd = 10

    if (w.config.yaxis[realIndex].title.text === undefined || realIndex < 0) {
      return {
        xPos: x,
        padd: 0,
      }
    }

    if (yAxisOpposite) {
      x =
        yAxisLabelsCoord.width +
        w.config.yaxis[realIndex].title.offsetX +
        yAxisTitleCoord.width / 2 +
        padd / 2

      oppositeAxisCount += 1

      if (oppositeAxisCount === 0) {
        x = x - padd / 2
      }
    } else {
      x =
        yAxisLabelsCoord.width * -1 +
        w.config.yaxis[realIndex].title.offsetX +
        padd / 2 +
        yAxisTitleCoord.width / 2

      if (w.globals.isBarHorizontal) {
        padd = 25
        x =
          yAxisLabelsCoord.width * -1 -
          w.config.yaxis[realIndex].title.offsetX -
          padd
      }
    }

    return {
      xPos: x,
      padd,
    }
  }

  // sets the x position of the y-axis by counting the labels width, title width and any offset
  setYAxisXPosition(yaxisLabelCoords, yTitleCoords) {
    let w = this.w

    let xLeft = 0
    let xRight = 0
    let leftOffsetX = 18
    let rightOffsetX = 1

    if (w.config.yaxis.length > 1) {
      this.multipleYs = true
    }

    w.config.yaxis.map((yaxe, index) => {
      let shouldNotDrawAxis =
        w.globals.ignoreYAxisIndexes.indexOf(index) > -1 ||
        !yaxe.show ||
        yaxe.floating ||
        yaxisLabelCoords[index].width === 0

      let axisWidth = yaxisLabelCoords[index].width + yTitleCoords[index].width

      if (!yaxe.opposite) {
        xLeft = w.globals.translateX - leftOffsetX

        if (!shouldNotDrawAxis) {
          leftOffsetX = leftOffsetX + axisWidth + 20
        }

        w.globals.translateYAxisX[index] = xLeft + yaxe.labels.offsetX
      } else {
        if (w.globals.isBarHorizontal) {
          xRight = w.globals.gridWidth + w.globals.translateX - 1

          w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX
        } else {
          xRight = w.globals.gridWidth + w.globals.translateX + rightOffsetX

          if (!shouldNotDrawAxis) {
            rightOffsetX = rightOffsetX + axisWidth + 20
          }

          w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + 20
        }
      }
    })
  }

  setYAxisTextAlignments() {
    const w = this.w

    let yaxis = w.globals.dom.baseEl.getElementsByClassName(`apexcharts-yaxis`)
    yaxis = Utils.listToArray(yaxis)
    yaxis.forEach((y, index) => {
      const yaxe = w.config.yaxis[index]
      // proceed only if user has specified alignment
      if (yaxe && !yaxe.floating && yaxe.labels.align !== undefined) {
        const yAxisInner = w.globals.dom.baseEl.querySelector(
          `.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-texts-g`
        )
        let yAxisTexts = w.globals.dom.baseEl.querySelectorAll(
          `.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-label`
        )

        yAxisTexts = Utils.listToArray(yAxisTexts)

        const rect = yAxisInner.getBoundingClientRect()

        if (yaxe.labels.align === 'left') {
          yAxisTexts.forEach((label, lI) => {
            label.setAttribute('text-anchor', 'start')
          })
          if (!yaxe.opposite) {
            yAxisInner.setAttribute('transform', `translate(-${rect.width}, 0)`)
          }
        } else if (yaxe.labels.align === 'center') {
          yAxisTexts.forEach((label, lI) => {
            label.setAttribute('text-anchor', 'middle')
          })
          yAxisInner.setAttribute(
            'transform',
            `translate(${(rect.width / 2) * (!yaxe.opposite ? -1 : 1)}, 0)`
          )
        } else if (yaxe.labels.align === 'right') {
          yAxisTexts.forEach((label, lI) => {
            label.setAttribute('text-anchor', 'end')
          })
          if (yaxe.opposite) {
            yAxisInner.setAttribute('transform', `translate(${rect.width}, 0)`)
          }
        }
      }
    })
  }
}
