'use strict';

const PropTypes = require('prop-types');
const React = require('react');
const createReactClass = require('create-react-class');

const d3 = require('d3');
const DataSeries = require('./DataSeries');
const { Chart, XAxis, YAxis, Tooltip } = require('../common');
const {
  CartesianChartPropsMixin,
  DefaultAccessorsMixin,
  ViewBoxMixin,
  TooltipMixin,
} = require('../mixins');

module.exports = createReactClass({

  displayName: 'BarChart',

  propTypes: {
    chartClassName: PropTypes.string,
    data: PropTypes.array.isRequired,
    hoverAnimation: PropTypes.bool,
    margins: PropTypes.object,
    rangeRoundBandsPadding: PropTypes.number,
    // https://github.com/mbostock/d3/wiki/Stack-Layout#offset
    stackOffset: PropTypes.oneOf(['silhouette', 'expand', 'wigget', 'zero']),
    grouped: PropTypes.bool,
    valuesAccessor: PropTypes.func,
    xAccessor: PropTypes.func,
    yAccessor: PropTypes.func,
    y0Accessor: PropTypes.func,
    title: PropTypes.string,
    xAxisClassName: PropTypes.string,
    yAxisClassName: PropTypes.string,
    yAxisTickCount: PropTypes.number,
  },

  mixins: [CartesianChartPropsMixin, DefaultAccessorsMixin, ViewBoxMixin, TooltipMixin],

  getDefaultProps() {
    return {
      chartClassName: 'rd3-barchart',
      hoverAnimation: true,
      margins: { top: 10, right: 20, bottom: 40, left: 45 },
      rangeRoundBandsPadding: 0.25,
      stackOffset: 'zero',
      grouped: false,
      valuesAccessor: d => d.values,
      y0Accessor: d => d.y0,
      xAxisClassName: 'rd3-barchart-xaxis',
      yAxisClassName: 'rd3-barchart-yaxis',
      yAxisTickCount: 4,
    };
  },

  _getStackedValuesMaxY(_data) {
    // in stacked bar chart, the maximum height we need for
    // yScale domain is the sum of y0 + y
    const { valuesAccessor } = this.props;
    return d3.max(_data, (d) => (
      d3.max(valuesAccessor(d), (d2) => (
        // where y0, y is generated by d3.layout.stack()
        this.props.y0Accessor(d2) + this.props.yAccessor(d2)
      ))
    ));
  },

  _getStackedValuesMinY(_data) {
    const { valuesAccessor } = this.props;
    return d3.min(_data, (d) => (
      d3.min(valuesAccessor(d), (d2) => (
        // where y0, y is generated by d3.layout.stack()
        this.props.y0Accessor(d2) + this.props.yAccessor(d2)
      ))
    ));
  },

  _getLabels(firstSeries) {
    // we only need first series to get all the labels
    const { valuesAccessor, xAccessor } = this.props;
    return valuesAccessor(firstSeries).map(xAccessor);
  },

  _stack() {
    // Only support columns with all positive or all negative values
    // https://github.com/mbostock/d3/issues/2265
    const { stackOffset, xAccessor, yAccessor, valuesAccessor } = this.props;
    return d3.layout.stack()
                    .offset(stackOffset)
                    .x(xAccessor)
                    .y(yAccessor)
                    .values(valuesAccessor);
  },

  render() {
    const props = this.props;
    const yOrient = this.getYOrient();

    const domain = props.domain || {};

    if (props.data.length === 0) {
      return null;
    }
    const _data = this._stack()(props.data);

    const { innerHeight, innerWidth, trans, svgMargins } = this.getDimensions();

    const xDomain = domain.x || this._getLabels(_data[0]);
    const xScale = d3.scale.ordinal()
                     .domain(xDomain)
                     .rangeRoundBands([0, innerWidth], props.rangeRoundBandsPadding);

    const minYDomain = Math.min(0, this._getStackedValuesMinY(_data));
    const maxYDomain = this._getStackedValuesMaxY(_data);
    const yDomain = domain.y || [minYDomain, maxYDomain];
    const yScale = d3.scale.linear().range([innerHeight, 0]).domain(yDomain);

    const series = props.data.map((item) => item.name);

    return (
      <span>
        <Chart
          viewBox={this.getViewBox()}
          legend={props.legend}
          data={props.data}
          margins={props.margins}
          colors={props.colors}
          colorAccessor={props.colorAccessor}
          width={props.width}
          height={props.height}
          title={props.title}
          shouldUpdate={!this.state.changeState}
        >
          <g transform={trans} className={props.chartClassName}>
            <YAxis
              yAxisClassName={props.yAxisClassName}
              yAxisTickValues={props.yAxisTickValues}
              yAxisLabel={props.yAxisLabel}
              yAxisLabelOffset={props.yAxisLabelOffset}
              yScale={yScale}
              margins={svgMargins}
              yAxisTickCount={props.yAxisTickCount}
              tickFormatting={props.yAxisFormatter}
              tickStroke={props.xAxisTickStroke}
              tickTextStroke={props.xAxisTickTextStroke}
              width={innerWidth}
              height={innerHeight}
              horizontalChart={props.horizontal}
              xOrient={props.xOrient}
              yOrient={yOrient}
              gridHorizontal={props.gridHorizontal}
              gridHorizontalStroke={props.gridHorizontalStroke}
              gridHorizontalStrokeWidth={props.gridHorizontalStrokeWidth}
              gridHorizontalStrokeDash={props.gridHorizontalStrokeDash}
            />
            <XAxis
              xAxisClassName={props.xAxisClassName}
              xAxisTickValues={props.xAxisTickValues}
              xAxisLabel={props.xAxisLabel}
              xAxisLabelOffset={props.xAxisLabelOffset}
              xScale={xScale}
              margins={svgMargins}
              tickFormatting={props.xAxisFormatter}
              tickStroke={props.yAxisTickStroke}
              tickTextStroke={props.yAxisTickTextStroke}
              width={innerWidth}
              height={innerHeight}
              horizontalChart={props.horizontal}
              xOrient={props.xOrient}
              yOrient={yOrient}
              gridVertical={props.gridVertical}
              gridVerticalStroke={props.gridVerticalStroke}
              gridVerticalStrokeWidth={props.gridVerticalStrokeWidth}
              gridVerticalStrokeDash={props.gridVerticalStrokeDash}
            />
            <DataSeries
              yScale={yScale}
              xScale={xScale}
              margins={svgMargins}
              _data={_data}
              series={series}
              width={innerWidth}
              height={innerHeight}
              grouped={props.grouped}
              colors={props.colors}
              colorAccessor={props.colorAccessor}
              hoverAnimation={props.hoverAnimation}
              valuesAccessor={props.valuesAccessor}
              xAccessor={props.xAccessor}
              yAccessor={props.yAccessor}
              y0Accessor={props.y0Accessor}
              onMouseOver={this.onMouseOver}
              onMouseLeave={this.onMouseLeave}
            />
          </g>
        </Chart>
        {(props.showTooltip ? <Tooltip {...this.state.tooltip} /> : null)}
      </span>
    );
  },
});
