/* eslint camelcase: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import Mustache from 'mustache';

import { d3format } from '../modules/utils';
import ChartBody from './ChartBody';
import Loading from '../components/Loading';
import StackTraceMessage from '../components/StackTraceMessage';
import visMap from '../../visualizations/main';
import sandboxedEval from '../modules/sandbox';

const propTypes = {
  annotationData: PropTypes.object,
  actions: PropTypes.object,
  chartKey: PropTypes.string.isRequired,
  containerId: PropTypes.string.isRequired,
  datasource: PropTypes.object.isRequired,
  formData: PropTypes.object.isRequired,
  height: PropTypes.number,
  width: PropTypes.number,
  setControlValue: PropTypes.func,
  timeout: PropTypes.number,
  vizType: PropTypes.string.isRequired,
  // state
  chartAlert: PropTypes.string,
  chartStatus: PropTypes.string,
  chartUpdateEndTime: PropTypes.number,
  chartUpdateStartTime: PropTypes.number,
  latestQueryFormData: PropTypes.object,
  queryRequest: PropTypes.object,
  queryResponse: PropTypes.object,
  lastRendered: PropTypes.number,
  triggerQuery: PropTypes.bool,
  // dashboard callbacks
  addFilter: PropTypes.func,
  getFilters: PropTypes.func,
  clearFilter: PropTypes.func,
  removeFilter: PropTypes.func,
};

const defaultProps = {
  addFilter: () => ({}),
  getFilters: () => ({}),
  clearFilter: () => ({}),
  removeFilter: () => ({}),
};

class Chart extends React.PureComponent {
  constructor(props) {
    super(props);
    // these properties are used by visualizations
    this.annotationData = props.annotationData;
    this.containerId = props.containerId;
    this.selector = `#${this.containerId}`;
    this.formData = props.formData;
    this.datasource = props.datasource;
    this.addFilter = this.addFilter.bind(this);
    this.getFilters = this.getFilters.bind(this);
    this.clearFilter = this.clearFilter.bind(this);
    this.removeFilter = this.removeFilter.bind(this);
    this.height = this.height.bind(this);
    this.width = this.width.bind(this);
  }

  componentDidMount() {
    if (this.props.triggerQuery) {
      this.props.actions.runQuery(this.props.formData, false,
        this.props.timeout,
        this.props.chartKey,
      );
    }
  }

  componentWillReceiveProps(nextProps) {
    this.annotationData = nextProps.annotationData;
    this.containerId = nextProps.containerId;
    this.selector = `#${this.containerId}`;
    this.formData = nextProps.formData;
    this.datasource = nextProps.datasource;
  }

  componentDidUpdate(prevProps) {
    if (
        this.props.queryResponse &&
        this.props.chartStatus === 'success' &&
        !this.props.queryResponse.error && (
        prevProps.annotationData !== this.props.annotationData ||
        prevProps.queryResponse !== this.props.queryResponse ||
        prevProps.height !== this.props.height ||
        prevProps.width !== this.props.width ||
        prevProps.lastRendered !== this.props.lastRendered)
    ) {
      this.renderViz();
    }
  }

  getFilters() {
    return this.props.getFilters();
  }

  addFilter(col, vals, merge = true, refresh = true) {
    this.props.addFilter(col, vals, merge, refresh);
  }

  clearFilter() {
    this.props.clearFilter();
  }

  removeFilter(col, vals, refresh = true) {
    this.props.removeFilter(col, vals, refresh);
  }

  clearError() {
    this.setState({
      errorMsg: null,
    });
  }

  width() {
    return this.props.width || this.container.el.offsetWidth;
  }

  height() {
    return this.props.height || this.container.el.offsetHeight;
  }

  d3format(col, number) {
    const { datasource } = this.props;
    const format = (datasource.column_formats && datasource.column_formats[col]) || '0.3s';

    return d3format(format, number);
  }

  render_template(s) {
    const context = {
      width: this.width(),
      height: this.height(),
    };
    return Mustache.render(s, context);
  }

  renderViz() {
    const viz = visMap[this.props.vizType];
    const fd = this.props.formData;
    const qr = this.props.queryResponse;
    try {
      // Executing user-defined data mutator function
      if (fd.js_data) {
        qr.data = sandboxedEval(fd.js_data)(qr.data);
      }
      // [re]rendering the visualization
      viz(this, qr, this.props.setControlValue);
    } catch (e) {
      this.props.actions.chartRenderingFailed(e, this.props.chartKey);
    }
  }

  render() {
    const isLoading = this.props.chartStatus === 'loading';
    return (
      <div className={`token col-md-12 ${isLoading ? 'is-loading' : ''}`}>
        {isLoading &&
          <Loading size={25} />
        }

        {this.props.chartAlert &&
        <StackTraceMessage
          message={this.props.chartAlert}
          queryResponse={this.props.queryResponse}
        />
        }

        {!this.props.chartAlert &&
          <ChartBody
            containerId={this.containerId}
            vizType={this.props.formData.viz_type}
            height={this.height}
            width={this.width}
            ref={(inner) => {
              this.container = inner;
            }}
          />
        }
      </div>
    );
  }
}

Chart.propTypes = propTypes;
Chart.defaultProps = defaultProps;

export default Chart;
