'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Scatterplot = undefined;

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

var _utils = require('./utils');

var _style = require('./style');

var _lodash = require('lodash.isequal');

var _lodash2 = _interopRequireDefault(_lodash);

var _d3Scale = require('d3-scale');

var scale = _interopRequireWildcard(_d3Scale);

var _d3Array = require('d3-array');

var d3array = _interopRequireWildcard(_d3Array);

var _lodash3 = require('lodash.merge');

var _lodash4 = _interopRequireDefault(_lodash3);

var _core = require('echarts-for-react/lib/core');

var _core2 = _interopRequireDefault(_core);

var _echarts = require('echarts/lib/echarts');

var _echarts2 = _interopRequireDefault(_echarts);

require('echarts/lib/chart/line');

require('echarts/lib/chart/lines');

require('echarts/lib/chart/scatter');

require('echarts/lib/chart/effectScatter');

require('echarts/lib/chart/custom');

require('echarts/lib/component/grid');

require('echarts/lib/component/legend');

require('echarts/lib/component/tooltip');

require('echarts/lib/component/title');

require('echarts/lib/component/dataZoom');

require('echarts/lib/component/visualMap');

require('echarts/lib/component/markPoint');

require('echarts/lib/component/markLine');

require('echarts/lib/component/markArea');

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

// import the core library.


// manually import required echarts components


// import 'zrender/lib/vml/vml';


/**
 * Gets the range for the provided dataset, while filtering
 * out extreme outliers
 * @param {object} data 
 */
var getDataRange = function getDataRange(data) {
  var values = Object.keys(data).map(function (k) {
    return parseFloat(data[k]);
  }).filter(function (v) {
    return v > -9999;
  }).sort(function (a, b) {
    return a - b;
  });
  return [d3array.quantile(values, 0.001), d3array.quantile(values, 0.999)];
};

/**
 * Returns a scale function that can be used to map data values
 * to dot sizes
 * @param {object} data data to generate scale for
 * @param {object} options range and exponent options for scale
 */
var getDataScale = function getDataScale(data, _ref) {
  var _ref$range = _ref.range,
      range = _ref$range === undefined ? [0, 1] : _ref$range,
      _ref$exponent = _ref.exponent,
      exponent = _ref$exponent === undefined ? 1 : _ref$exponent;

  if (!data) {
    return function () {
      return 0;
    };
  }
  return scale.scalePow().exponent(exponent).domain(getDataRange(data)).range(range).clamp(true);
};

var getDataSeries = function getDataSeries(id) {
  var series = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  return series && series.length ? series.find(function (s) {
    return s.id === id;
  }) : null;
};

var makeId = function makeId() {
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
};

var Scatterplot = exports.Scatterplot = function (_Component) {
  _inherits(Scatterplot, _Component);

  function Scatterplot(props) {
    _classCallCheck(this, Scatterplot);

    var _this = _possibleConstructorReturn(this, (Scatterplot.__proto__ || Object.getPrototypeOf(Scatterplot)).call(this, props));

    _this._getScatterplotOptions = function () {
      var options = _this.props.options;

      var series = _this._getScatterplotSeries();
      var fullOptions = _extends({}, options, {
        series: series
      });
      return fullOptions;
    };

    _this.state = {
      options: false
    };
    return _this;
  }

  /** Do not render updates if component is frozen */


  _createClass(Scatterplot, [{
    key: 'shouldComponentUpdate',
    value: function shouldComponentUpdate(nextProps) {
      return !nextProps.freeze;
    }
  }, {
    key: 'componentDidMount',
    value: function componentDidMount() {
      var theme = this.props.theme;

      this.updateOptions();
      if (theme) {
        if (typeof theme === 'string') {
          this.setState({ themeId: theme });
        }
        if ((typeof theme === 'undefined' ? 'undefined' : _typeof(theme)) === 'object') {
          var themeId = makeId();
          this.setState({ themeId: themeId });
          _echarts2.default.registerTheme(themeId, this.props.theme);
        }
      }
    }

    /**
     * update the scatterplot options when any of the following happen:
     * - one of the x,y,z vars change
     * - data boolean changes
     * - selected ids change
     */

  }, {
    key: 'componentDidUpdate',
    value: function componentDidUpdate(prevProps) {
      var _props = this.props,
          loading = _props.loading,
          data = _props.data,
          xVar = _props.xVar,
          yVar = _props.yVar,
          zVar = _props.zVar,
          selected = _props.selected,
          highlighted = _props.highlighted,
          options = _props.options,
          freeze = _props.freeze;

      if (loading) {
        return;
      }
      if (Boolean(prevProps.data) !== Boolean(data) || prevProps.xVar !== xVar || prevProps.zVar !== zVar || prevProps.yVar !== yVar || !(0, _lodash2.default)(prevProps.highlighted, highlighted) || // highlighted dots changed
      !(0, _lodash2.default)(prevProps.selected, selected) || // selected dots changed
      !(0, _lodash2.default)(prevProps.options, options) || // new options
      prevProps.freeze && !freeze // unfreeze
      ) {
          this.updateOptions();
        }
    }
  }, {
    key: 'updateOptions',
    value: function updateOptions() {
      var options = (0, _style.getScatterplotOptions)(this._getScatterplotOptions());
      this.setState({ options: options });
    }

    /**
     * Gets an echart series for all of the data corresponding to IDs
     * in `props.highlighted`
     * @param {array} scatterData data from the base series
     * @param {function} sizeScale a function that returns circle size based on zVar
     */

  }, {
    key: '_getHighlightedSeries',
    value: function _getHighlightedSeries(scatterData, sizeScale) {
      var _props2 = this.props,
          _props2$highlighted = _props2.highlighted,
          highlighted = _props2$highlighted === undefined ? [] : _props2$highlighted,
          options = _props2.options,
          zVar = _props2.zVar;
      // data index for the id property

      var idDim = zVar ? 3 : 2;
      var baseSeries = {
        id: 'highlighted',
        type: 'scatter',
        symbolSize: zVar ? function (value) {
          return sizeScale(value[2]);
        } : 10,
        z: 3
      };
      var overrides = options ? getDataSeries('highlighted', options.series) : {};
      var data = highlighted.map(function (id, i) {
        return scatterData.find(function (d) {
          return d[idDim] === id;
        });
      }).filter(function (d) {
        return Boolean(d);
      });
      return (0, _lodash4.default)(_extends({}, baseSeries, { data: data }), overrides ? overrides : {});
    }

    /**
     * Gets a data series with selected items
     */

  }, {
    key: '_getSelectedSeries',
    value: function _getSelectedSeries(scatterData, sizeScale) {
      var _props3 = this.props,
          _props3$selected = _props3.selected,
          selected = _props3$selected === undefined ? [] : _props3$selected,
          options = _props3.options,
          zVar = _props3.zVar;
      // data index for the id property

      var idDim = zVar ? 3 : 2;
      var baseSeries = {
        id: 'selected',
        type: 'scatter',
        symbolSize: function symbolSize(value) {
          return sizeScale(value[2]);
        },
        z: 4
      };
      var overrides = options ? getDataSeries('selected', options.series) : {};
      var data = selected.map(function (id, i) {
        return scatterData.find(function (d) {
          return d[idDim] === id;
        });
      }).filter(function (d) {
        return Boolean(d);
      });
      return (0, _lodash4.default)(_extends({}, baseSeries, { data: data }), overrides ? overrides : {});
    }

    /** 
     * Get series with default styles and selected highlights 
     */

  }, {
    key: '_getBaseSeries',
    value: function _getBaseSeries(scatterData, sizeScale) {
      var options = this.props.options;

      var overrides = options ? getDataSeries('base', options.series) : {};
      var series = (0, _lodash4.default)({
        id: 'base',
        type: 'scatter',
        data: scatterData,
        symbolSize: function symbolSize(value) {
          return sizeScale(value[2]);
        },
        z: 2
      }, overrides ? overrides : {});
      return series;
    }

    /**
     * Gets scatterplot data series, or return empty array if 
     * data is not ready yet
     */

  }, {
    key: '_getScatterplotSeries',
    value: function _getScatterplotSeries() {
      var _props4 = this.props,
          data = _props4.data,
          xVar = _props4.xVar,
          yVar = _props4.yVar,
          zVar = _props4.zVar,
          options = _props4.options;

      var otherSeries = options && options.series ? options.series.filter(function (s) {
        return ['base', 'selected', 'highlighted'].indexOf(s.id) === -1;
      }) : [];
      if (data && data[xVar] && data[yVar] && (zVar && data[zVar] || !zVar)) {
        var sizeScale = zVar ? getDataScale(data[zVar], { range: [6, 48] }) : function () {
          return 10;
        };
        var scatterData = zVar ? (0, _utils.getScatterplotData)(data[xVar], data[yVar], data[zVar]) : (0, _utils.getScatterplotData)(data[xVar], data[yVar]);
        var series = [this._getBaseSeries(scatterData, sizeScale), this._getSelectedSeries(scatterData, sizeScale), this._getHighlightedSeries(scatterData, sizeScale)].concat(_toConsumableArray(otherSeries));
        return series;
      }
      return [];
    }

    /**
     * Gets the options with overrides and series data for the scatterplot
     */

  }, {
    key: '_onChartReady',


    /** 
     * Bind events when the chart is ready
     */
    value: function _onChartReady(e) {
      this.props.onHover && e.on('mouseover', this.props.onHover);
      this.props.onHover && e.on('mouseout', this.props.onHover);
      this.props.onMouseMove && e.on('mousemove', this.props.onMouseMove);
      this.props.onClick && e.on('click', this.props.onClick);
      this.props.onMouseOver && e.getDom().addEventListener('mouseover', this.props.onMouseEnter);
      this.props.onMouseOut && e.getDom().addEventListener('mouseout', this.props.onMouseLeave);
      this.echart = e;
      this.props.onReady && this.props.onReady(e);
    }
  }, {
    key: 'getOptionOverrides',
    value: function getOptionOverrides() {
      return this.state.options;
    }
  }, {
    key: 'render',
    value: function render() {
      return this.state.options && _react2.default.createElement(_core2.default, {
        echarts: _echarts2.default,
        onChartReady: this._onChartReady.bind(this),
        style: _extends({ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }, this.props.style),
        option: this.state.options,
        notMerge: this.props.notMerge,
        theme: this.state.themeId,
        showLoading: this.props.loading,
        loadingOption: {
          color: '#0078d4'
        }
      });
    }
  }]);

  return Scatterplot;
}(_react.Component);

Scatterplot.propTypes = process.env.NODE_ENV !== "production" ? {
  options: _propTypes2.default.object,
  style: _propTypes2.default.object,
  xVar: _propTypes2.default.string,
  yVar: _propTypes2.default.string,
  zVar: _propTypes2.default.string,
  data: _propTypes2.default.object,
  selected: _propTypes2.default.array,
  highlighted: _propTypes2.default.array,
  onHover: _propTypes2.default.func,
  onClick: _propTypes2.default.func,
  onReady: _propTypes2.default.func,
  onMouseMove: _propTypes2.default.func,
  notMerge: _propTypes2.default.bool,
  theme: _propTypes2.default.object,
  freeze: _propTypes2.default.bool
} : {};
exports.default = Scatterplot;