import _ from 'lodash';
import { metricToFormatter } from './util/Utils.js';
import Percentage from './util/Percentage.js';
import React from 'react';
import * as d3 from 'd3';
import './../../css/bar-chart.css';

const defaultSvgWidth = 595;
const defaultSvgHeight = 150;
const margin = { top: 0, right: 0, bottom: 20, left: 0 };
const horizontalLabelLimit = 4; // number of bars beyond which to tilt axis labels
const labelLimit = 40; // beyond this, stop labelling bars entirely

export default class LineGraph extends React.Component {
  constructor(props) {
    super(props);

    this.state = this.getChartDimensions();
  }

  componentWillMount() {
    this.initializeScales();
  }

  componentDidMount() {
    this.svg = d3.select("." + this.props.containerClassName)
      .append("svg")
      .attr("class", "bar-chart")
      .attr("width", this.state.svgWidth)
      .attr("height", this.state.svgHeight)
      .append("g")
      .attr("transform", "translate(" + this.state.margin.left + "," + this.state.margin.top + ")");

    this.tooltip = d3.select("." + this.props.containerClassName + " .bar-chart-tooltip")
      .append("div").attr("class", "tooltip");

    this.xAxis = this.svg.append("g")
      .attr("class", "x-axis")
      .attr("transform", "translate(0," + this.state.height + ")");

    this.updateScales();
    this.renderGraph();
  }

  shouldComponentUpdate(nextProps) {
    if (nextProps.lastUpdated === this.props.lastUpdated) {
      // control whether react re-renders the component
      // only rerender if the input data has changed
      return false;
    }
    return true;
  }

  componentDidUpdate() {
    this.updateScales();
    this.renderGraph();
  }

  getChartDimensions() {
    let svgWidth = this.props.width || defaultSvgWidth;
    let svgHeight = this.props.height || defaultSvgHeight;
    let tiltLabels = false;
    let hideLabels = false;

    if (_.size(this.props.data) > horizontalLabelLimit) {
      if (_.size(this.props.data) > labelLimit) {
        // if there are way too many bars, don't label at all
        hideLabels = true;
      } else {
        // if there are many bars, tilt x axis labels
        margin.bottom += 100;
        tiltLabels = true;
      }
    }

    let width = svgWidth - margin.left - margin.right;
    let height = svgHeight - margin.top - margin.bottom;

    return {
      svgWidth: svgWidth,
      svgHeight: svgHeight,
      width: width,
      height: height,
      margin: margin,
      tiltLabels: tiltLabels,
      hideLabels: hideLabels
    };
  }

  chartData() {
    let data = this.props.data;
    _.each(data, d => {
      let p = new Percentage(d.requestRate, d.totalRequests);
      d.shareOfRequests = p.get();
      d.pretty = p.prettyRate();
    });
    return data;
  }

  updateScales() {
    let data = this.chartData();
    this.xScale.domain(_.map(data, d => d.name));
    this.yScale.domain([0, d3.max(data, d => d.requestRate)]);
  }

  initializeScales() {
    this.xScale = d3.scaleBand()
      .range([0, this.state.width])
      .padding(0.1);
    this.yScale = d3.scaleLinear()
      .range([this.state.height, 0]);
  }

  renderGraph() {
    let data = this.chartData();
    let barChart = this.svg.selectAll(".bar")
      .remove()
      .exit()
      .data(data);

    barChart.enter().append("rect")
      .attr("class", "bar")
      .attr("x", d => this.xScale(d.name))
      .attr("width", () =>  this.xScale.bandwidth())
      .attr("y", d => this.yScale(d.requestRate))
      .attr("height", d => this.state.height - this.yScale(d.requestRate))
      .on("mousemove", d => {
        this.tooltip
          .style("left", d3.event.pageX - 50 + "px")
          .style("top", d3.event.pageY - 70 + "px")
          .style("display", "inline-block") // show tooltip
          .html(`${d.name}:<br /> ${metricToFormatter["REQUEST_RATE"](d.requestRate)} (${d.pretty} of total)`);
      })
      .on("mouseout", () => this.tooltip.style("display", "none"));

    this.updateAxes();
  }

  updateAxes() {
    this.xAxis
      .call(d3.axisBottom(this.xScale)) // add x axis labels
      .selectAll("text")
      .attr("class", "tick-labels")
      .attr("transform", () => this.state.tiltLabels ? "rotate(-65)" : "")
      .style("text-anchor", () => this.state.tiltLabels ? "end" : "")
      .text(d => {
        if (this.state.hideLabels) {
          return;
        }

        let displayText = d;
        // truncate long label names
        if (_.size(displayText) > 20) {
          displayText = "..." + displayText.substring(displayText.length - 20);
        }
        return displayText;
      });
  }

  render() {
    return null;
  }
}
