'use strict';

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

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 _class, _temp;

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

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

var _propTypes2 = _interopRequireDefault(_propTypes);

var _classnames = require('classnames');

var _classnames2 = _interopRequireDefault(_classnames);

var _Scatterplot = require('./Scatterplot');

var _Scatterplot2 = _interopRequireDefault(_Scatterplot);

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

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

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; }

/**
 * Gets the scatterplot data for a given ID
 * @param {*} id 
 * @param {*} data 
 */
var getDataForId = function getDataForId(id, data) {
  return Object.keys(data).reduce(function (acc, curr) {
    // only add data if it exists
    if (data[curr][id] || data[curr][id] === 0) {
      acc[curr] = data[curr][id];
    }
    return acc;
  }, {});
};

/**
 * Gets the data index for the given ID in scatterplot data
 * @param {*} id 
 * @param {*} series 
 */
var getDataIndex = function getDataIndex(id, series) {
  return series.findIndex(function (d) {
    return d[3] === id;
  });
};

var SedaScatterplot = exports.SedaScatterplot = (_temp = _class = function (_Component) {
  _inherits(SedaScatterplot, _Component);

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

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

    _this._onClick = function (e) {
      if (!_this.props.onClick) {
        return;
      }
      var prefix = _this.props.prefix || 'unprefixed';
      var data = _this.props.data;

      var locationData = _extends({
        id: e.data[3]
      }, getDataForId(e.data[3], data[prefix]));
      _this.props.onClick && _this.props.onClick(locationData, e);
    };

    _this._onReady = function (echart) {
      _this.echart = echart;
    };

    _this._onHover = function (e) {
      if (!_this.props.onHover) {
        return;
      }
      var prefix = _this.props.prefix || 'unprefixed';
      var data = _this.props.data;
      // index of the id property in the scatterplot data

      var idIndex = _this.props.zVar ? 3 : 2;
      // get the data array for the hovered location
      var hoverData = e && e.data && e.data.hasOwnProperty('value') ? e.data['value'] : e.data;
      // get the data from the state for the location
      var locationData = hoverData && e.type === 'mouseover' ? _extends({
        id: hoverData[idIndex]
      }, getDataForId(hoverData[idIndex], data[prefix])) : null;
      // if there is a location then call onHover immediately
      if (locationData) {
        _this.props.onHover(locationData, e);
        // clear the timeout if it is waiting to clear the hovered feature
        if (_this.hoverTimeout) {
          clearTimeout(_this.hoverTimeout);
          _this.hoverTimeout = null;
        }
      } else {
        // set a timeout to inform the callback no items are hovered
        _this.hoverTimeout = setTimeout(function () {
          _this.props.onHover(null, e);
        }, 200);
      }
    };

    _this._onMouseMove = function (e) {
      if (!_this.props.onMouseMove) {
        return;
      }
      _this.props.onMouseMove(e);
    };

    _this.loaded = false;
    _this.hoverTimeout = null;
    _this.state = { hovered: false, error: false, fetching: false };
    return _this;
  }

  /** 
   * Set initial data if it exists, 
   * call load to load any missing data 
   */


  _createClass(SedaScatterplot, [{
    key: 'componentDidMount',
    value: function componentDidMount() {
      this._loadScatterplotData();
    }

    /**
     * Call load if variable or region (prefix) has changed
     * Toggle highlights on the hovered ID if needed
     */

  }, {
    key: 'componentDidUpdate',
    value: function componentDidUpdate(prevProps) {
      var _props = this.props,
          prefix = _props.prefix,
          xVar = _props.xVar,
          yVar = _props.yVar,
          zVar = _props.zVar,
          hovered = _props.hovered,
          stateFips = _props.stateFips;

      if (this.isDataReady()) {
        if (this.echart && !this.ready) {
          this.ready = true;
          this.props.onReady && this.props.onReady(this.echart);
        }
        if (!this.loaded) {
          this._setLoaded(true);
        }
      } else {
        if (this.loaded) {
          this._setLoaded(false);
        }
      }
      // load data if needed
      if (prefix === 'schools' && (prevProps.xVar !== xVar || prevProps.yVar !== yVar || prevProps.stateFips !== stateFips)) {
        this._loadScatterplotData();
      } else if (prevProps.prefix !== prefix || prevProps.xVar !== xVar || prevProps.zVar !== zVar || prevProps.yVar !== yVar) {
        this._loadScatterplotData();
      }
      /** No updating rendered props if freeze */
      if (this.props.freeze) {
        return;
      }
      // update highlighted dots when hovered changes
      if (prevProps.hovered !== hovered && prevProps.hovered) {
        this._toggleHighlight(prevProps.hovered, { show: false });
      }
      if (prevProps.hovered !== hovered && hovered && !this.state.hovered) {
        this._toggleHighlight(hovered, { show: true });
      }
    }

    /**
     * Gets the state data for the scatterplot
     */

  }, {
    key: 'getData',
    value: function getData() {
      return this.props.data && this.props.data[this.props.prefix] ? this.props.data[this.props.prefix] : {};
    }

    /** 
     * Get the data series echart configuration for the givn ID
     * 
     * @returns {object} echart options for series https://ecomfe.github.io/echarts-doc/public/en/option.html#series-scatter.type
     */

  }, {
    key: 'getDataSeries',
    value: function getDataSeries() {
      var id = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'meta';

      var options = this.getOption();
      return options.series && options.series.length ? options.series.find(function (s) {
        return s.id === id;
      }) : null;
    }

    /** Gets the echart options, alias for echart function */

  }, {
    key: 'getOption',
    value: function getOption() {
      return this.echart ? this.echart.getOption() : {};
    }

    /** Set echart options, alias for echart function */

  }, {
    key: 'setOption',
    value: function setOption() {
      return this.echart && this.echart.apply(this, arguments);
    }

    /** Gets the option overrides for the scatterplot */

  }, {
    key: 'getOptionOverrides',
    value: function getOptionOverrides() {
      return this.scatterplot && this.scatterplot.getOptionOverrides();
    }

    /** returns true if all data to render the scatterplot is available */

  }, {
    key: 'isDataReady',
    value: function isDataReady() {
      var _props2 = this.props,
          xVar = _props2.xVar,
          yVar = _props2.yVar,
          zVar = _props2.zVar,
          prefix = _props2.prefix;

      var data = this.props.data[prefix] || {};
      return Boolean(data[xVar]) && Boolean(data[yVar]) && Boolean(!zVar || data[zVar]);
    }

    /** Sets data for the given data category */

  }, {
    key: '_setData',
    value: function _setData(data, prefix) {
      prefix = prefix ? prefix : 'unprefixed';
      this.props.onData && this.props.onData(data, prefix);
    }

    /** Sets the loading state for the scatterplot */

  }, {
    key: '_setLoaded',
    value: function _setLoaded(loaded) {
      this.loaded = loaded;
      this.props.onLoading && this.props.onLoading(!loaded);
    }

    /**
     * Loads variables for a region if they do not exist in the data
     */

  }, {
    key: '_loadScatterplotData',
    value: function _loadScatterplotData() {
      var _props3 = this.props,
          xVar = _props3.xVar,
          yVar = _props3.yVar,
          zVar = _props3.zVar,
          prefix = _props3.prefix,
          stateFips = _props3.stateFips;
      // always need to fetch schools

      if (prefix === 'schools' && !stateFips) {
        return this._fetchSchoolPair(xVar, yVar);
      }
      var data = this.props.data[prefix] || {};
      var vars = [];
      zVar && (!data || !data[zVar]) && vars.push(zVar);
      xVar && (!data || !data[xVar]) && vars.push(xVar);
      yVar && (!data || !data[yVar]) && vars.push(yVar);
      // no need to fetch if vars are loaded
      if (vars.length === 0 && prefix !== 'schools') {
        return;
      }
      return this._fetchVariables(vars);
    }
  }, {
    key: '_fetchVariables',
    value: function _fetchVariables(vars) {
      var _this2 = this;

      var _props4 = this.props,
          prefix = _props4.prefix,
          endpoint = _props4.endpoint,
          metaVars = _props4.metaVars,
          stateFips = _props4.stateFips;

      this.setState({ fetching: true });
      if (!endpoint) {
        throw new Error('No endpoint specified for scatterplot');
      }
      // get meta collection variables if any
      var collectionVars = metaVars && metaVars[prefix] || [];
      return (0, _utils.fetchScatterplotVars)(vars, prefix, endpoint, collectionVars, stateFips).then(function (data) {
        _this2._setData(data, prefix);
        if (_this2.state.error || _this2.state.fetching) {
          _this2.setState({ error: false, fetching: false });
        }
        return data;
      }).catch(function (err) {
        _this2._handleFetchError(err, vars);
      });
    }
  }, {
    key: '_fetchSchoolPair',
    value: function _fetchSchoolPair(xVar, yVar) {
      var _this3 = this;

      var endpoint = this.props.endpoint;

      if (!endpoint) {
        throw new Error('No endpoint specified for scatterplot');
      }
      this.setState({ fetching: true });
      return (0, _utils.fetchReducedPair)(endpoint, xVar, yVar).then(function (data) {
        _this3._setData(data, 'schools');
        if (_this3.state.error || _this3.state.fetching) {
          _this3.setState({ error: false, fetching: false });
        }
        return data;
      }).catch(function (err) {
        _this3._handleFetchError(err, [xVar, yVar]);
      });
    }

    /** Handles the fetch error */

  }, {
    key: '_handleFetchError',
    value: function _handleFetchError(err, vars) {
      this.props.onError && this.props.onError(err);
      this.setState({ 'fetching': false, 'error': vars });
    }

    /**
     * Gets the series and data index for a given ID 
     * @param {string} id 
     * @returns {object} { seriesIndex, dataIndex }
     */

  }, {
    key: '_getIndiciesForId',
    value: function _getIndiciesForId(id) {
      var _echart$getOption = this.echart.getOption(),
          series = _echart$getOption.series;
      // check in reverse order to get the top-most level


      for (var i = series.length - 1; i > -1; i--) {
        if (series[i] && series[i].data) {
          var dataIndex = getDataIndex(id, series[i].data);
          if (dataIndex > -1) {
            return { seriesIndex: i, dataIndex: dataIndex };
          }
        }
      }
      // no id found, return -1
      return { seriesIndex: -1, dataIndex: -1 };
    }

    /**
     * Toggle highlighted state for items in the scatterplot
     */

  }, {
    key: '_toggleHighlight',
    value: function _toggleHighlight(hoveredId, _ref) {
      var _ref$show = _ref.show,
          show = _ref$show === undefined ? true : _ref$show;

      var _getIndiciesForId2 = this._getIndiciesForId(hoveredId),
          seriesIndex = _getIndiciesForId2.seriesIndex,
          dataIndex = _getIndiciesForId2.dataIndex;

      if (seriesIndex > -1 && dataIndex > -1) {
        this.echart && this.echart.dispatchAction({
          type: show ? 'highlight' : 'downplay',
          seriesIndex: seriesIndex,
          dataIndex: dataIndex
        });
      } else {
        console.warn('no id to toggle highlight', hoveredId);
      }
    }

    /**
     * Get this clicked location data and pass it to the handler
     * if it exists.
     */


    /**
     * Set the echart instance on ready and set the
     * component status to ready if data has loaded.
     */


    /**
     * Get the data for the hovered feature and call the
     * handler if it exists
     */


    /**
     * Call the mouse move handler if it exists
     */

  }, {
    key: 'render',
    value: function render() {
      var _this4 = this;

      var data = this.isDataReady() ? this.props.data[this.props.prefix ? this.props.prefix : 'unprefixed'] : null;
      return _react2.default.createElement(
        'div',
        { className: (0, _classnames2.default)("seda-scatterplot", this.props.classes.root) },
        _react2.default.createElement(_Scatterplot2.default, {
          ref: function ref(_ref2) {
            return _this4.scatterplot = _ref2;
          },
          onReady: this._onReady,
          onHover: this._onHover,
          onMouseEnter: function onMouseEnter() {
            _this4.setState({ hovered: true });
          },
          onMouseLeave: function onMouseLeave() {
            _this4.setState({ hovered: false });
          },
          onMouseMove: this._onMouseMove,
          onClick: this._onClick,
          data: data,
          xVar: this.props.xVar,
          yVar: this.props.yVar,
          zVar: this.props.zVar,
          selected: this.props.selected,
          highlighted: this.props.highlighted,
          options: this.props.options,
          notMerge: this.props.notMerge,
          theme: this.props.theme,
          loading: !this.isDataReady(),
          freeze: this.props.freeze
        }),
        this.state.error && !this.state.fetching && _react2.default.createElement(
          'div',
          {
            className: (0, _classnames2.default)("seda-scatterplot__error", this.props.classes.error)
          },
          _react2.default.createElement(
            'p',
            null,
            'There was an error fetching chart data.'
          ),
          _react2.default.createElement(
            'button',
            { onClick: function onClick() {
                return _this4._loadScatterplotData();
              } },
            'Retry'
          )
        )
      );
    }
  }]);

  return SedaScatterplot;
}(_react.Component), _class.defaultProps = {
  classes: {}
}, _temp);
SedaScatterplot.propTypes = process.env.NODE_ENV !== "production" ? {
  endpoint: _propTypes2.default.string.isRequired,
  xVar: _propTypes2.default.string,
  yVar: _propTypes2.default.string,
  zVar: _propTypes2.default.string,
  stateFips: _propTypes2.default.string,
  data: _propTypes2.default.object,
  theme: _propTypes2.default.object,
  prefix: _propTypes2.default.string,
  options: _propTypes2.default.object,
  hovered: _propTypes2.default.string,
  selected: _propTypes2.default.array,
  onHover: _propTypes2.default.func,
  onClick: _propTypes2.default.func,
  onMouseMove: _propTypes2.default.func,
  onReady: _propTypes2.default.func,
  onData: _propTypes2.default.func,
  onError: _propTypes2.default.func,
  onLoading: _propTypes2.default.func,
  metaVars: _propTypes2.default.object,
  classes: _propTypes2.default.object
} : {};
exports.default = SedaScatterplot;