import React from 'react';
import PropTypes from 'prop-types';
import Chart from 'chart.js';
import isEqual from 'lodash/isEqual';
import find from 'lodash/find';


export class AnterosChart extends React.Component {
  constructor(props) {
    super(props);
    this.handleOnClick = this.handleOnClick.bind(this);
    this.renderChart = this.renderChart.bind(this);
    this.ref = this.ref.bind(this);
  }



  componentWillMount() {
    this.chart_instance = undefined;
  }

  componentDidMount() {
    this.renderChart();
  }

  componentDidUpdate() {
    if (this.props.redraw) {
      this.chart_instance.destroy();
      this.renderChart();
      return;
    }

    this.updateChart();
  }

  shouldComponentUpdate(nextProps) {
    const {
      redraw,
      type,
      options,
      plugins,
      legend,
      height,
      width
    } = this.props;

    if (nextProps.redraw === true) {
      return true;
    }

    if (height !== nextProps.height || width !== nextProps.width) {
      return true;
    }

    if (type !== nextProps.type) {
      return true;
    }

    if (!isEqual(legend, nextProps.legend)) {
      return true;
    }

    if (!isEqual(options, nextProps.options)) {
      return true;
    }

    const nextData = this.transformDataProp(nextProps);

    if (!isEqual(this.shadowDataProp, nextData)) {
      return true;
    }

    return !isEqual(plugins, nextProps.plugins);


  }

  componentWillUnmount() {
    this.chart_instance.destroy();
  }

  transformDataProp(props) {
    const { data } = props;
    if (typeof (data) == 'function') {
      const node = this.element;
      return data(node);
    } else {
      return data;
    }
  }


  memoizeDataProps() {
    if (!this.props.data) {
      return;
    }

    const data = this.transformDataProp(this.props);

    this.shadowDataProp = {
      ...data,
      datasets: data.datasets && data.datasets.map(set => {
        return {
          ...set
        };
      })
    };

    return data;
  }

  updateChart() {
    const { options } = this.props;

    const data = this.memoizeDataProps(this.props);

    if (!this.chart_instance) return;

    if (options) {
      this.chart_instance.options = Chart.helpers.configMerge(this.chart_instance.options, options);
    }

    let currentDatasets = (this.chart_instance.config.data && this.chart_instance.config.data.datasets) || [];
    const nextDatasets = data.datasets || [];

    const currentDatasetKeys = currentDatasets.map(this.props.datasetKeyProvider);
    const nextDatasetKeys = nextDatasets.map(this.props.datasetKeyProvider);
    const newDatasets = nextDatasets.filter(d => currentDatasetKeys.indexOf(this.props.datasetKeyProvider(d)) === -1);

    for (let idx = currentDatasets.length - 1; idx >= 0; idx -= 1) {
      const currentDatasetKey = this.props.datasetKeyProvider(currentDatasets[idx]);
      if (nextDatasetKeys.indexOf(currentDatasetKey) === -1) {
        currentDatasets.splice(idx, 1);
      } else {
        const retainedDataset = find(nextDatasets, d => this.props.datasetKeyProvider(d) === currentDatasetKey);
        if (retainedDataset) {
          currentDatasets[idx].data.splice(retainedDataset.data.length);
          retainedDataset.data.forEach((point, pid) => {
            currentDatasets[idx].data[pid] = retainedDataset.data[pid];
          });
          const { data, ...otherProps } = retainedDataset;
          currentDatasets[idx] = {
            data: currentDatasets[idx].data,
            ...currentDatasets[idx],
            ...otherProps
          };
        }
      }
    }
    newDatasets.forEach(d => currentDatasets.push(d));
    const { datasets, ...rest } = data;

    this.chart_instance.config.data = {
      ...this.chart_instance.config.data,
      ...rest
    };

    this.chart_instance.update();
  }

  renderChart() {
    const { options, legend, type, redraw, plugins } = this.props;
    const node = this.element;
    const data = this.memoizeDataProps();

    if (typeof legend !== 'undefined' && !isEqual(AnterosChart.defaultProps.legend, legend)) {
      options.legend = legend;
    }

    this.chart_instance = new Chart(node, {
      type,
      data,
      options,
      plugins
    });
  }

  handleOnClick(event) {
    const instance = this.chart_instance;

    const {
      getDatasetAtEvent,
      getElementAtEvent,
      getElementsAtEvent,
      onElementsClick
    } = this.props;

    getDatasetAtEvent && getDatasetAtEvent(instance.getDatasetAtEvent(event), event);
    getElementAtEvent && getElementAtEvent(instance.getElementAtEvent(event), event);
    getElementsAtEvent && getElementsAtEvent(instance.getElementsAtEvent(event), event);
    onElementsClick && onElementsClick(instance.getElementsAtEvent(event), event);
  }

  ref(element) {
    this.element = element
  }

  render() {
    const { height, width, onElementsClick } = this.props;

    return (
      <canvas
        ref={this.ref}
        height={height}
        width={width}
        onClick={this.handleOnClick}
      />
    );
  }
}

AnterosChart.propTypes = {
  data: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.func
  ]).isRequired,
  getDatasetAtEvent: PropTypes.func,
  getElementAtEvent: PropTypes.func,
  getElementsAtEvent: PropTypes.func,
  height: PropTypes.string,
  legend: PropTypes.object,
  onElementsClick: PropTypes.func,
  options: PropTypes.object,
  plugins: PropTypes.arrayOf(PropTypes.object),
  redraw: PropTypes.bool,
  type: function (props, propName, componentName) {
    if (!Chart.controllers[props[propName]]) {
      return new Error(
        'Invalid chart type `' + props[propName] + '` supplied to' +
        ' `' + componentName + '`.'
      );
    }
  },
  width: PropTypes.string,
  datasetKeyProvider: PropTypes.func
}

AnterosChart.defaultProps = {
  legend: {
    display: true,
    position: 'bottom'
  },
  type: 'bar',
  height: "400px",
  width: "400px",
  redraw: false,
  options: {}
}

export class Doughnut extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='doughnut'
      />
    );
  }
}

export class Pie extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='pie'
      />
    );
  }
}

export class Line extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='line'
      />
    );
  }
}

export class Bar extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='bar'
      />
    );
  }
}

export class HorizontalBar extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='horizontalBar'
      />
    );
  }
}

export class Radar extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='radar'
      />
    );
  }
}

export class Polar extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='polarArea'
      />
    );
  }
}

export class Bubble extends React.Component {
  render() {
    return (
      <ChartComponent
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='bubble'
      />
    );
  }
}

export class Scatter extends React.Component {
  render() {
    return (
      <AnterosChart
        {...this.props}
        ref={ref => this.chart_instance = ref && ref.chart_instance}
        type='scatter'
      />
    );
  }
}

export const defaults = Chart.defaults;
export { Chart };