var React = require('react');
var Support = require('../lib/support');
var d3 = require('d3');

var LineChart = React.createClass({
  chart: function(selection){
    var {
      margin,
      width,
      height,
      yAxisTitle,
      lineInterpolation,
      duration,
      color,
      style,
      seriesNames,
      seriesValues,
      pointNames,
      pointIndexes,
      pointValues,
      pointText,
      enterSeries,
      updateSeries,
      exitSeries,
      enterPoints,
      updatePoints,
      exitPoints,
      enterSeriesTitle,
      updateSeriesTitle,
      exitSeriesTitle
    } = Support.getProps(this, 'chart', {
      margin: Support.types.Object({
                  top: Support.types.Number(20),
                  right: Support.types.Number(80),
                  bottom: Support.types.Number(30),
                  left: Support.types.Number(50)
                }),
      width: Support.types.Number(-1),
      height: Support.types.Number(420),
      yAxisTitle: Support.types.String(false),
      lineInterpolation: Support.types.String('linear'),
      duration: Support.types.Number(500),
      color: Support.types.Function(d3.scale.category10()),
      style: Support.types.Object(false),
      seriesNames: Support.types.Function(function(d) { return d[0]; }),
      seriesValues: Support.types.Function(function(d) { return d[1]; }),
      pointNames: Support.types.Function(function(d) { return d[0]; }),
      pointIndexes: Support.types.Function(function(d, i) { return i; }),
      pointValues: Support.types.Function(function(d) { return d[1]; }),
      pointText: Support.types.Function(function(d){return '';}),
      enterSeries: Support.types.Function(function(series){
          series.append('path')
              .attr('class', 'line')
              ;
        }),
      updateSeries: Support.types.Function(function(series){
          series.select('path.line')
                   .attr('d', function(d) {
                     return line(seriesValues(d));
                   })
                   .attr('stroke', function(d) { return color(seriesNames(d)); });
        }),
      exitSeries: Support.types.Function(function(series){
        series.remove();
      }),
      enterPoints: Support.types.Function(function(points){
          points
            .append("circle")
            .attr("stroke", function(d){
              return color(seriesNames(this.parentNode.parentNode.__data__));
            })
            .attr("r", 5)
            .attr("fill", "white").attr("fill-opacity", .5)
            .append('title')
            .text(pointText);
        }),
      updatePoints:Support.types.Function(function(points){
        points.select('circle title')
          .text(pointText);
        }),
      exitPoints:Support.types.Function(function(points){
          points.select('circle').remove();
        }),
      enterSeriesTitle: Support.types.Function(function(seriesTitle){
          seriesTitle
            .append('text');
        }),
      updateSeriesTitle: Support.types.Function(function(seriesTitle){
          seriesTitle
            .attr('transform', function(d) {
                var idx = d.idx;
                return 'translate(' + x(pointIndexes(d.value, idx)) + ',' + y(pointValues(d.value, idx)) + ')';
              })
            .attr('x', 3)
            .attr('dy', '.35em')
            .select('text')
            .text(function(d) { return d.name; });
        }),
      exitSeriesTitle: Support.types.Function(function(series){
        series.remove();
      })
    });

    var w = width - margin.left - margin.right;
    var h = height - margin.top - margin.bottom;

    var x = d3.time.scale()
        .range([0, w]);

    var y = d3.scale.linear()
        .range([h, 0]);

    var xAxis = d3.svg.axis()
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient('left');

    var line = d3.svg.line()
        .interpolate(lineInterpolation) // a B-spline, with control point duplication on the ends
        .x(function(d, i) { return x(pointIndexes(d, i)); })
        .y(function(d, i) { return y(pointValues(d, i)); });

    selection.each(function(data) {
      var svgWidth = width===-1?this.offsetWidth:width;
      var maxChartHeight = height - margin.top - margin.bottom;

      w = svgWidth - margin.left - margin.right;

      x.range([0, w]);
      xAxis.scale(x);

      var svg = d3.select(this).selectAll('svg').data([data]);
      svg.enter().append('svg')
                 .append('g')
                  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
      svg
        .attr('width', svgWidth)
        .attr('height', height);
      svg = svg.select('g');

      if(color&&color.domain){
        var names = data.map(seriesNames);
        color.domain(names);
      }

      var indexes = data.map(seriesValues).map(function(list){
        return list.map(pointIndexes);
      }).reduce(function(curr, res){
        return res.concat(curr);
      }, []);
      x.domain(d3.extent(indexes));

      y.domain([
        d3.min(data, function(c) { return d3.min(seriesValues(c), pointValues); }),
        d3.max(data, function(c) { return d3.max(seriesValues(c), pointValues); })
      ]);

      var xAxisGroup = svg.selectAll('g.x.axis');
      if(!xAxisGroup[0][0]){
        xAxisGroup = svg.append('g')
          .attr('class', 'x axis')
          .attr('transform', 'translate(0,' + h + ')');
      }
      xAxisGroup.call(xAxis);

      var yAxisGroup = svg.selectAll('g.y.axis');
      if(!yAxisGroup[0][0]){
        yAxisGroup = svg.append('g')
          .attr('class', 'y axis');
        if(yAxisTitle){
          yAxisGroup
            .append('text')
              .attr('transform', 'rotate(-90)')
              .attr('y', 6)
              .attr('dy', '.71em')
              .style('text-anchor', 'end')
              .text(yAxisTitle);
        }
      }
      yAxisGroup.call(yAxis);
      var series = svg.selectAll('.series').data(data, function(d){
        return seriesNames(d);
      });
      var seriesEnter = series.enter()
        .append('g')
        .attr('class', 'series')
        ;
      enterSeries(seriesEnter);
      updateSeries(series);
      var seriesExit = series.exit();
      exitSeries(seriesExit);

      var points = series.selectAll('.points').data(function(d){
        return seriesValues(d);
      });
      var pointsEnter = points.enter()
        .append('g')
        .attr('class', 'points')
        ;

      points
        .attr('transform', function(d, i) {
            return 'translate(' + x(pointIndexes(d, i)) + ',' + y(pointValues(d, i)) + ')';
          })
        ;

      enterPoints(pointsEnter);
      updatePoints(points);
      var pointsExit = points.exit();
      exitPoints(pointsExit);

      var seriesTitle = svg.selectAll('g.series.title').data(data);
      var seriesTitleEnter = seriesTitle.enter()
            .append('g')
            .attr('class', 'series title');
      var seriesTitleExit = seriesTitle.exit();
      seriesTitle.datum(function(d){
          var values = seriesValues(d);
          return {name: seriesNames(d), value: values[values.length - 1], idx: values.length - 1};
        });
      enterSeriesTitle(seriesTitleEnter);
      updateSeriesTitle(seriesTitle);
      exitSeriesTitle(seriesTitleExit);

      if(style){
        Object.keys(style).forEach(function(key){
          svg.selectAll(key).attr('style', style[key]);
        });
      }
    });
  },
  renderChart: function(data){
    if(!data){
      return;
    }
    var container = this.getDOMNode();
    d3.select(container)
      .datum(data)
      .call(this.chart)
      ;
  },
  componentDidMount: function(){
    return this.renderChart(this.props.data);
  },
  componentDidUpdate: function(){
    return this.renderChart(this.props.data);
  },
  render: function(){
    var classNames = Support.classList(this, {
      'line-chart': true
    });
    return (
      <div className={classNames}>
      </div>
    );
  }
});

module.exports = {
  LineChart: LineChart
};
