import CLASS from './class'
import { ChartInternal } from './core'
import { isValue, isFunction, isArray, isString, sanitise } from './util'

ChartInternal.prototype.initTooltip = function() {
  var $$ = this,
    config = $$.config,
    i
  $$.tooltip = $$.selectChart
    .style('position', 'relative')
    .append('div')
    .attr('class', CLASS.tooltipContainer)
    .style('position', 'absolute')
    .style('pointer-events', 'none')
    .style('display', 'none')
  // Show tooltip if needed
  if (config.tooltip_init_show) {
    if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
      config.tooltip_init_x = $$.parseDate(config.tooltip_init_x)
      for (i = 0; i < $$.data.targets[0].values.length; i++) {
        if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) {
          break
        }
      }
      config.tooltip_init_x = i
    }
    $$.tooltip.html(
      config.tooltip_contents.call(
        $$,
        $$.data.targets.map(function(d) {
          return $$.addName(d.values[config.tooltip_init_x])
        }),
        $$.axis.getXAxisTickFormat(),
        $$.getYFormat($$.hasArcType()),
        $$.color
      )
    )
    $$.tooltip
      .style('top', config.tooltip_init_position.top)
      .style('left', config.tooltip_init_position.left)
      .style('display', 'block')
  }
}
ChartInternal.prototype.getTooltipSortFunction = function() {
  var $$ = this,
    config = $$.config

  if (config.data_groups.length === 0 || config.tooltip_order !== undefined) {
    // if data are not grouped or if an order is specified
    // for the tooltip values we sort them by their values

    var order = config.tooltip_order
    if (order === undefined) {
      order = config.data_order
    }

    var valueOf = function(obj) {
      return obj ? obj.value : null
    }

    // if data are not grouped, we sort them by their value
    if (isString(order) && order.toLowerCase() === 'asc') {
      return function(a, b) {
        return valueOf(a) - valueOf(b)
      }
    } else if (isString(order) && order.toLowerCase() === 'desc') {
      return function(a, b) {
        return valueOf(b) - valueOf(a)
      }
    } else if (isFunction(order)) {
      // if the function is from data_order we need
      // to wrap the returned function in order to format
      // the sorted value to the expected format

      var sortFunction = order

      if (config.tooltip_order === undefined) {
        sortFunction = function(a, b) {
          return order(
            a
              ? {
                  id: a.id,
                  values: [a]
                }
              : null,
            b
              ? {
                  id: b.id,
                  values: [b]
                }
              : null
          )
        }
      }

      return sortFunction
    } else if (isArray(order)) {
      return function(a, b) {
        return order.indexOf(a.id) - order.indexOf(b.id)
      }
    }
  } else {
    // if data are grouped, we follow the order of grouped targets
    var ids = $$.orderTargets($$.data.targets).map(function(i) {
      return i.id
    })

    // if it was either asc or desc we need to invert the order
    // returned by orderTargets
    if ($$.isOrderAsc() || $$.isOrderDesc()) {
      ids = ids.reverse()
    }

    return function(a, b) {
      return ids.indexOf(a.id) - ids.indexOf(b.id)
    }
  }
}
ChartInternal.prototype.getTooltipContent = function(
  d,
  defaultTitleFormat,
  defaultValueFormat,
  color
) {
  var $$ = this,
    config = $$.config,
    titleFormat = config.tooltip_format_title || defaultTitleFormat,
    nameFormat =
      config.tooltip_format_name ||
      function(name) {
        return name
      },
    text,
    i,
    title,
    value,
    name,
    bgcolor

  var valueFormat = config.tooltip_format_value
  if (!valueFormat) {
    valueFormat = $$.isTargetNormalized(d.id)
      ? (v, ratio) => `${(ratio * 100).toFixed(2)}%`
      : defaultValueFormat
  }

  var tooltipSortFunction = this.getTooltipSortFunction()
  if (tooltipSortFunction) {
    d.sort(tooltipSortFunction)
  }

  for (i = 0; i < d.length; i++) {
    if (!(d[i] && (d[i].value || d[i].value === 0))) {
      continue
    }

    if ($$.isStanfordGraphType()) {
      // Custom tooltip for stanford plots
      if (!text) {
        title = $$.getStanfordTooltipTitle(d[i])
        text = "<table class='" + $$.CLASS.tooltip + "'>" + title
      }

      bgcolor = $$.getStanfordPointColor(d[i])
      name = sanitise(config.data_epochs) // Epochs key name
      value = d[i].epochs
    } else {
      // Regular tooltip
      if (!text) {
        title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x)
        text =
          "<table class='" +
          $$.CLASS.tooltip +
          "'>" +
          (title || title === 0
            ? "<tr><th colspan='2'>" + title + '</th></tr>'
            : '')
      }

      value = sanitise(
        valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)
      )
      if (value !== undefined) {
        // Skip elements when their name is set to null
        if (d[i].name === null) {
          continue
        }

        name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index))
        bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id)
      }
    }

    if (value !== undefined) {
      text +=
        "<tr class='" +
        $$.CLASS.tooltipName +
        '-' +
        $$.getTargetSelectorSuffix(d[i].id) +
        "'>"
      text +=
        "<td class='name'><span style='background-color:" +
        bgcolor +
        "'></span>" +
        name +
        '</td>'
      text += "<td class='value'>" + value + '</td>'
      text += '</tr>'
    }
  }
  return text + '</table>'
}
ChartInternal.prototype.tooltipPosition = function(
  dataToShow,
  tWidth,
  tHeight,
  element
) {
  var $$ = this,
    config = $$.config,
    d3 = $$.d3
  var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight
  var forArc = $$.hasArcType(),
    mouse = d3.mouse(element)
  // Determin tooltip position
  if (forArc) {
    tooltipLeft =
      ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]
    tooltipTop =
      ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20
  } else {
    svgLeft = $$.getSvgLeft(true)
    if (config.axis_rotated) {
      tooltipLeft = svgLeft + mouse[0] + 100
      tooltipRight = tooltipLeft + tWidth
      chartRight = $$.currentWidth - $$.getCurrentPaddingRight()
      tooltipTop = $$.x(dataToShow[0].x) + 20
    } else {
      tooltipLeft =
        svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20
      tooltipRight = tooltipLeft + tWidth
      chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight()
      tooltipTop = mouse[1] + 15
    }

    if (tooltipRight > chartRight) {
      // 20 is needed for Firefox to keep tooltip width
      tooltipLeft -= tooltipRight - chartRight + 20
    }
    if (tooltipTop + tHeight > $$.currentHeight) {
      tooltipTop -= tHeight + 30
    }
  }
  if (tooltipTop < 0) {
    tooltipTop = 0
  }
  return {
    top: tooltipTop,
    left: tooltipLeft
  }
}
ChartInternal.prototype.showTooltip = function(selectedData, element) {
  var $$ = this,
    config = $$.config
  var tWidth, tHeight, position
  var forArc = $$.hasArcType(),
    dataToShow = selectedData.filter(function(d) {
      return d && isValue(d.value)
    }),
    positionFunction =
      config.tooltip_position || ChartInternal.prototype.tooltipPosition
  if (dataToShow.length === 0 || !config.tooltip_show) {
    $$.hideTooltip()
    return
  }
  $$.tooltip
    .html(
      config.tooltip_contents.call(
        $$,
        selectedData,
        $$.axis.getXAxisTickFormat(),
        $$.getYFormat(forArc),
        $$.color
      )
    )
    .style('display', 'block')

  // Get tooltip dimensions
  tWidth = $$.tooltip.property('offsetWidth')
  tHeight = $$.tooltip.property('offsetHeight')

  position = positionFunction.call(this, dataToShow, tWidth, tHeight, element)
  // Set tooltip
  $$.tooltip
    .style('top', position.top + 'px')
    .style('left', position.left + 'px')
}
ChartInternal.prototype.hideTooltip = function() {
  this.tooltip.style('display', 'none')
}
