'use strict';

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

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

var React = require('react');
var PropTypes = require('prop-types');
var app = require('hadron-app');
var _ = require('lodash');
var FormGroup = require('react-bootstrap').FormGroup;
var InputGroup = require('react-bootstrap').InputGroup;
var FormControl = require('react-bootstrap').FormControl;
var DropdownButton = require('react-bootstrap').DropdownButton;
var MenuItem = require('react-bootstrap').MenuItem;
var TypeChecker = require('hadron-type-checker');

// const debug = require('debug')('mongodb-compass:validation:action-selector');

/**
  * The version at which high precision values are available.
  */
var HP_VERSION = '3.4.0';

/**
 * A RangeInput represents a numeric lower or upper bound and the value of the
 * lower or upper bound, if the bound exists.
 *
 * The `validationState` of this RangeInput can be set to 'error' either:
 *  - by the RangeInput validating the `value` prop is not of type `number`, or
 *  - by the RangeInput's parent `RuleCategoryRange` component validating the
 *    combined range expression, such as `5 < x < 5` is displayed as red/error
 *    even though the RangeInputs `5 < x` and `5 > x` are individually valid.
 */

var RangeInput = function (_React$Component) {
  _inherits(RangeInput, _React$Component);

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

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

    var op = _this._getOperatorString(props);
    _this.state = {
      hidden: op === 'none',
      operator: op,
      value: _this.props.value,
      isValid: true,
      hasStartedValidating: false
    };
    _this._ENABLE_HP = app.instance && app.instance.build.version >= HP_VERSION;
    return _this;
  }

  /**
   * called whenever the input changes (i.e. user is typing). We don't bubble up the
   * value at this stage yet, but wait until the user blurs the input field.
   *
   * @param {Object} evt   The onChange event
   */


  _createClass(RangeInput, [{
    key: 'onInputChange',
    value: function onInputChange(evt) {
      this.setState({
        value: evt.target.value
      });
    }

    /**
     * called whenever the field is blurred (loses focus). At this point, we want to
     * validate the input and if it is valid, report the value change up to the parent.
     */

  }, {
    key: 'onInputBlur',
    value: function onInputBlur() {
      this.validate(true);
      this.props.onRangeInputBlur({
        value: this.state.value,
        operator: this.state.operator
      });
    }

    /**
     * called when the user chooses a value from the operator dropdown. As there are
     * no invalid values, we can always immediately report up the value/operator change.
     *
     * @param {Object} evtKey    the selected value from the dropdown (e.g. "<=", "none", ...)
     */

  }, {
    key: 'onDropdownSelect',
    value: function onDropdownSelect(evtKey) {
      this.setState({
        hidden: evtKey === 'none',
        operator: evtKey
      });
      this.props.onRangeInputBlur({
        value: this.state.value,
        operator: evtKey
      });
    }

    /**
     * determines if the input by itself is valid (e.g. a value that can be
     * cast to a number).
     *
     * @param {Boolean} force    forces validation from now on.
     * @return {Boolean}         whether the input is valid or not.
     */

  }, {
    key: 'validate',
    value: function validate(force) {
      if (!force && !this.state.hasStartedValidating) {
        return true;
      }
      if (this.state.hidden) {
        return true;
      }
      var value = this.state.value;
      var valueTypes = TypeChecker.castableTypes(value, this._ENABLE_HP);

      // Not sure if hadron-type-checker should make NUMBER_TYPES public
      var NUMBER_TYPES = ['Long', 'Int32', 'Double', 'Decimal128'];

      var isValid = _.intersection(valueTypes, NUMBER_TYPES).length > 0;
      this.setState({
        isValid: isValid,
        hasStartedValidating: true
      });
      return isValid;
    }
  }, {
    key: '_getOperatorString',
    value: function _getOperatorString(props) {
      props = props || this.props;

      if (props.hidden) {
        return 'none';
      }
      if (props.upperBound) {
        return props.boundIncluded ? '<=' : '<';
      }
      return props.boundIncluded ? '>=' : '>';
    }
  }, {
    key: 'renderMenuItems',
    value: function renderMenuItems() {
      if (this.props.upperBound) {
        return [React.createElement(
          MenuItem,
          { key: '<', eventKey: '<', href: '#' },
          '<'
        ), React.createElement(
          MenuItem,
          { key: '<=', eventKey: '<=', href: '#' },
          '<='
        ), React.createElement(
          MenuItem,
          { key: 'none', eventKey: 'none', href: '#' },
          'none'
        )];
      }
      return [React.createElement(
        MenuItem,
        { key: '>', eventKey: '>', href: '#' },
        '>'
      ), React.createElement(
        MenuItem,
        { key: '>=', eventKey: '>=', href: '#' },
        '>='
      ), React.createElement(
        MenuItem,
        { key: 'none', eventKey: 'none', href: '#' },
        'none'
      )];
    }

    /**
     * Render validation range input component.
     *
     * @returns {React.Component} The component.
     */

  }, {
    key: 'render',
    value: function render() {
      var boundString = (this.props.upperBound ? 'Upper' : 'Lower') + ' bound';

      // hidden, only show dropdown
      if (this.state.hidden) {
        return React.createElement(
          FormGroup,
          null,
          React.createElement(
            DropdownButton,
            {
              id: 'range-input-' + (this.props.upperBound ? 'upper' : 'lower'),
              style: { width: this.props.width },
              title: this.state.operator,
              onSelect: this.onDropdownSelect.bind(this),
              disabled: this.props.disabled },
            this.renderMenuItems()
          )
        );
      }
      // not hidden, render input group with value input and operator dropdown
      var placeholder = '' + boundString;
      var validationState = this.state.isValid ? null : 'error';
      return React.createElement(
        FormGroup,
        { validationState: this.props.validationState || validationState },
        React.createElement(
          InputGroup,
          { style: { width: this.props.width } },
          React.createElement(
            DropdownButton,
            {
              className: 'range-dropdown',
              id: 'range-input-' + (this.props.upperBound ? 'upper' : 'lower'),
              componentClass: InputGroup.Button,
              title: this.state.operator,
              onSelect: this.onDropdownSelect.bind(this),
              disabled: this.props.disabled },
            this.renderMenuItems()
          ),
          React.createElement(FormControl, {
            ref: 'input',
            type: 'text',
            placeholder: placeholder,
            value: this.state.value,
            onChange: this.onInputChange.bind(this),
            onBlur: this.onInputBlur.bind(this),
            disabled: this.props.disabled })
        )
      );
    }
  }]);

  return RangeInput;
}(React.Component);

RangeInput.propTypes = {
  value: PropTypes.string, // Can't be required to allow "none" in GUI,
  // can't be number to work with Decimal128.
  upperBound: PropTypes.bool,
  validationState: PropTypes.string,
  boundIncluded: PropTypes.bool.isRequired,
  hidden: PropTypes.bool.isRequired,
  onRangeInputBlur: PropTypes.func,
  width: PropTypes.number,
  disabled: PropTypes.bool
};

RangeInput.defaultProps = {
  hidden: false,
  boundIncluded: false,
  upperBound: false,
  validationState: null,
  value: '',
  width: 160,
  disabled: false
};

RangeInput.displayName = 'RangeInput';

module.exports = RangeInput;