'use strict';

exports.__esModule = true;

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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _AvInputContainer = require('./AvInputContainer');

var _AvInputContainer2 = _interopRequireDefault(_AvInputContainer);

var _AvValidator = require('./AvValidator');

var _AvValidator2 = _interopRequireDefault(_AvValidator);

var _reactstrap = require('reactstrap');

var _classnames = require('classnames');

var _classnames2 = _interopRequireDefault(_classnames);

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

var _lodash2 = _interopRequireDefault(_lodash);

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

var _lodash4 = _interopRequireDefault(_lodash3);

var _lodash5 = require('lodash.isstring');

var _lodash6 = _interopRequireDefault(_lodash5);

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

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; }

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 getInputErrorMessage = function getInputErrorMessage(input, ruleName) {
  var errorMessage = input.props.errorMessage;

  if ((typeof errorMessage === 'undefined' ? 'undefined' : _typeof(errorMessage)) === 'object') {
    return errorMessage[ruleName];
  }
  return errorMessage;
};

var AvForm = function (_InputContainer) {
  _inherits(AvForm, _InputContainer);

  function AvForm() {
    var _this2 = this;

    var _temp, _this, _ret;

    _classCallCheck(this, AvForm);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return _ret = (_temp = (_this = _possibleConstructorReturn(this, _InputContainer.call.apply(_InputContainer, [this].concat(args))), _this), _this.state = {
      invalidInputs: {},
      dirtyInputs: {},
      touchedInputs: {},
      badInputs: {},
      submitted: false
    }, _this.validations = {}, _this.handleSubmit = function () {
      var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(e) {
        var values, _ref2, isValid, errors;

        return regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                if (e && typeof e.preventDefault === 'function') {
                  e.preventDefault();
                }

                values = _this.getValues();
                _context.next = 4;
                return _this.validateAll(values);

              case 4:
                _ref2 = _context.sent;
                isValid = _ref2.isValid;
                errors = _ref2.errors;


                _this.setTouched(Object.keys(_this._inputs));

                _this.props.onSubmit(e, errors, values);
                if (isValid) {
                  _this.props.onValidSubmit(e, values);
                } else {
                  _this.props.onInvalidSubmit(e, errors, values);
                }

                !_this.state.submitted && _this.setState({ submitted: true });

              case 11:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, _this2);
      }));

      return function (_x) {
        return _ref.apply(this, arguments);
      };
    }(), _this.handleNonFormSubmission = function (event) {
      if (_this.props.onKeyDown(event) !== false) {
        if (event.type === 'keydown' && (event.which === 13 || event.keyCode === 13 || event.key === 'Enter')) {
          event.stopPropagation();
          event.preventDefault();
          _this.handleSubmit(event);
        }
      }
    }, _temp), _possibleConstructorReturn(_this, _ret);
  }

  AvForm.prototype.getChildContext = function getChildContext() {
    return {
      FormCtrl: {
        inputs: this._inputs,
        getDefaultValue: this.getDefaultValue.bind(this),
        getInputState: this.getInputState.bind(this),
        hasError: this.state.invalidInputs,
        isDirty: this.state.dirtyInputs,
        isTouched: this.state.touchedInputs,
        isBad: this.state.badInputs,
        setDirty: this.setDirty.bind(this),
        setTouched: this.setTouched.bind(this),
        setBad: this.setBad.bind(this),
        register: this.registerInput.bind(this),
        unregister: this.unregisterInput.bind(this),
        validate: this.validateInput.bind(this),
        validationEvent: this.props.validationEvent,
        parent: this.context.FormCtrl || null
      }
    };
  };

  AvForm.prototype.componentWillMount = function componentWillMount() {
    _InputContainer.prototype.componentWillMount.call(this);

    this._validators = {};
  };

  AvForm.prototype.registerInput = function registerInput(input) {
    _InputContainer.prototype.registerInput.call(this, input);

    if (_typeof(input.validations) === 'object') {
      this._validators[input.props.name] = this.compileValidationRules(input, input.validations);
    }
  };

  AvForm.prototype.unregisterInput = function unregisterInput(input) {
    _InputContainer.prototype.unregisterInput.call(this, input);

    delete this._validators[input.props.name];
  };

  AvForm.prototype.render = function render() {
    var _props = this.props;
    var Tag = _props.tag;
    var omit1 = _props.errorMessage;
    var omit2 = _props.model;
    var omit3 = _props.onValidSubmit;
    var omit4 = _props.onInvalidSubmit;
    var omit5 = _props.validate;
    var omit6 = _props.validateOne;
    var omit7 = _props.validateAll;
    var omit8 = _props.validationEvent;
    var className = _props.className;

    var attributes = _objectWithoutProperties(_props, ['tag', 'errorMessage', 'model', 'onValidSubmit', 'onInvalidSubmit', 'validate', 'validateOne', 'validateAll', 'validationEvent', 'className']);

    var classes = (0, _classnames2.default)(className, this.state.submitted ? 'av-submitted' : false, Object.keys(this.state.invalidInputs).length > 0 ? 'av-invalid' : 'av-valid');

    if (Tag !== 'form' && Tag !== _reactstrap.Form) {
      attributes.onKeyDown = this.handleNonFormSubmission;
    }

    return _react2.default.createElement(Tag, _extends({ noValidate: true,
      action: '#'
    }, attributes, {
      className: classes,
      onSubmit: this.handleSubmit
    }));
  };

  AvForm.prototype.getValues = function getValues() {
    var _this3 = this;

    return Object.keys(this._inputs).reduce(function (values, inputName) {
      (0, _lodash4.default)(values, inputName, _this3.getValue(inputName));

      return values;
    }, {});
  };

  AvForm.prototype.submit = function submit() {
    this.handleSubmit.apply(this, arguments);
  };

  AvForm.prototype.reset = function reset() {
    var _this4 = this;

    Object.keys(this._inputs).forEach(function (inputName) {
      return _this4._inputs[inputName].reset();
    });
  };

  AvForm.prototype.validateInput = function validateInput(name) {
    this.validateOne(name, this.getValues());
  };

  AvForm.prototype.getInputState = function getInputState(inputName) {
    var errorMessage = void 0;
    var error = this.isTouched(inputName) && this.hasError(inputName);
    var color = void 0;

    if (error) {
      errorMessage = this.state.invalidInputs[inputName];
      color = 'danger';
      if (!(0, _lodash6.default)(errorMessage)) {
        errorMessage = 'This field is invalid';
      }
    }

    return { color: color, error: error, errorMessage: errorMessage };
  };

  AvForm.prototype.hasError = function hasError(inputName) {
    return inputName ? !!this.state.invalidInputs[inputName] : Object.keys(this.state.invalidInputs).length > 0;
  };

  AvForm.prototype.isDirty = function isDirty(inputName) {
    return inputName ? !!this.state.dirtyInputs[inputName] : Object.keys(this.state.dirtyInputs).length > 0;
  };

  AvForm.prototype.isTouched = function isTouched(inputName) {
    return inputName ? !!this.state.touchedInputs[inputName] : Object.keys(this.state.touchedInputs).length > 0;
  };

  AvForm.prototype.isBad = function isBad(inputName) {
    return inputName ? !!this.state.badInputs[inputName] : Object.keys(this.state.badInputs).length > 0;
  };

  AvForm.prototype.setError = function setError(inputName) {
    var error = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
    var errText = arguments.length <= 2 || arguments[2] === undefined ? error : arguments[2];

    if (error && !(0, _lodash6.default)(errText) && typeof errText !== 'boolean') {
      errText = errText + '';
    }

    var currentError = this.hasError(inputName);
    if (currentError === errText) return;

    var invalidInputs = this.state.invalidInputs;
    if (error) {
      invalidInputs[inputName] = errText || true;
    } else {
      delete invalidInputs[inputName];
    }

    invalidInputs = _extends({}, this.state.invalidInputs);
    this.setState({ invalidInputs: invalidInputs });
  };

  AvForm.prototype.setDirty = function setDirty(inputs) {
    var dirty = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];

    var dirtyInputs = this.state.dirtyInputs;
    if (!Array.isArray(inputs)) {
      inputs = [inputs];
    }
    inputs.forEach(function (inputName) {
      if (dirty) {
        dirtyInputs[inputName] = true;
      } else {
        delete dirtyInputs[inputName];
      }
    });

    dirtyInputs = _extends({}, this.state.dirtyInputs);
    this.setState({ dirtyInputs: dirtyInputs });
  };

  AvForm.prototype.setTouched = function setTouched(inputs) {
    var touched = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];

    var touchedInputs = this.state.touchedInputs;
    if (!Array.isArray(inputs)) {
      inputs = [inputs];
    }
    inputs.forEach(function (inputName) {
      if (touched) {
        touchedInputs[inputName] = true;
      } else {
        delete touchedInputs[inputName];
      }
    });

    touchedInputs = _extends({}, this.state.touchedInputs);
    this.setState({ touchedInputs: touchedInputs });
  };

  AvForm.prototype.setBad = function setBad(inputs) {
    var isBad = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];

    var badInputs = this.state.badInputs;
    if (!Array.isArray(inputs)) {
      inputs = [inputs];
    }
    inputs.forEach(function (inputName) {
      if (isBad) {
        badInputs[inputName] = true;
      } else {
        delete badInputs[inputName];
      }
    });

    badInputs = _extends({}, this.state.badInputs);
    this.setState({ badInputs: badInputs });
  };

  AvForm.prototype.validateOne = function () {
    var _ref3 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(inputName, context) {
      var input, value, validate, isValid, result, error;
      return regeneratorRuntime.wrap(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              input = this._inputs[inputName];

              if (!Array.isArray(input)) {
                _context2.next = 3;
                break;
              }

              throw new Error('Multiple inputs cannot use the same name: "' + inputName + '"');

            case 3:
              value = (0, _lodash2.default)(context, inputName);
              validate = input.validations;
              isValid = true;
              result = void 0;
              error = void 0;

              if (!(typeof validate === 'function')) {
                _context2.next = 14;
                break;
              }

              _context2.next = 11;
              return validate(value, context, input);

            case 11:
              result = _context2.sent;
              _context2.next = 21;
              break;

            case 14:
              if (!((typeof validate === 'undefined' ? 'undefined' : _typeof(validate)) === 'object')) {
                _context2.next = 20;
                break;
              }

              _context2.next = 17;
              return this._validators[inputName](value, context);

            case 17:
              result = _context2.sent;
              _context2.next = 21;
              break;

            case 20:
              result = true;

            case 21:

              if (result !== true) {
                isValid = false;

                if ((0, _lodash6.default)(result)) {
                  error = result;
                }
              }

              this.setError(inputName, !isValid, error);

              return _context2.abrupt('return', isValid);

            case 24:
            case 'end':
              return _context2.stop();
          }
        }
      }, _callee2, this);
    }));

    function validateOne(_x7, _x8) {
      return _ref3.apply(this, arguments);
    }

    return validateOne;
  }();

  AvForm.prototype.validateAll = function () {
    var _ref4 = _asyncToGenerator(regeneratorRuntime.mark(function _callee3(context) {
      var errors, isValid, inputName, valid, formLevelValidation;
      return regeneratorRuntime.wrap(function _callee3$(_context3) {
        while (1) {
          switch (_context3.prev = _context3.next) {
            case 0:
              errors = [];
              isValid = true;
              _context3.t0 = regeneratorRuntime.keys(this._inputs);

            case 3:
              if ((_context3.t1 = _context3.t0()).done) {
                _context3.next = 12;
                break;
              }

              inputName = _context3.t1.value;

              if (!this._inputs.hasOwnProperty(inputName)) {
                _context3.next = 10;
                break;
              }

              _context3.next = 8;
              return this.validateOne(inputName, context);

            case 8:
              valid = _context3.sent;

              if (!valid) {
                isValid = false;
                errors.push(inputName);
              }

            case 10:
              _context3.next = 3;
              break;

            case 12:

              if (this.props.validate) {
                formLevelValidation = this.props.validate;

                if (!Array.isArray(formLevelValidation)) {
                  formLevelValidation = [formLevelValidation];
                }

                if (!formLevelValidation.every(function (validationFn) {
                  return validationFn(context);
                })) {
                  isValid = false;
                  errors.push('*');
                }
              }

              return _context3.abrupt('return', {
                isValid: isValid,
                errors: errors
              });

            case 14:
            case 'end':
              return _context3.stop();
          }
        }
      }, _callee3, this);
    }));

    function validateAll(_x9) {
      return _ref4.apply(this, arguments);
    }

    return validateAll;
  }();

  AvForm.prototype.compileValidationRules = function compileValidationRules(input, ruleProp) {
    var _this5 = this;

    return function () {
      var _ref5 = _asyncToGenerator(regeneratorRuntime.mark(function _callee4(val, context) {
        var result, validations, _loop, rule;

        return regeneratorRuntime.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                if (!_this5.isBad(input.props.name)) {
                  _context4.next = 2;
                  break;
                }

                return _context4.abrupt('return', false);

              case 2:
                result = true;
                validations = [];

                _loop = function _loop(rule) {
                  /* istanbul ignore else  */
                  if (ruleProp.hasOwnProperty(rule)) {
                    (function () {
                      var ruleResult = void 0;

                      var promise = new Promise(function (resolve, reject) {
                        var callback = function callback(value) {
                          return resolve({ value: value, rule: rule });
                        };

                        if (typeof ruleProp[rule] === 'function') {
                          ruleResult = ruleProp[rule](val, context, input, callback);
                        } else {
                          if (typeof _AvValidator2.default[rule] !== 'function') {
                            return reject(new Error('Invalid input validation rule: "' + rule + '"'));
                          }

                          ruleResult = _AvValidator2.default[rule](val, context, ruleProp[rule], input, callback);
                        }

                        if (ruleResult && typeof ruleResult.then === 'function') {
                          ruleResult.then(callback);
                        } else if (ruleResult !== undefined) {
                          callback(ruleResult);
                        } else {
                          // they are using the callback
                        }
                      });

                      validations.push(promise);
                    })();
                  }
                };

                for (rule in ruleProp) {
                  _loop(rule);
                }

                _context4.next = 8;
                return Promise.all(validations).then(function (results) {
                  results.every(function (ruleResult) {
                    if (result === true && ruleResult.value !== true) {
                      result = (0, _lodash6.default)(ruleResult.value) && ruleResult.value || getInputErrorMessage(input, ruleResult.rule) || getInputErrorMessage(_this5, ruleResult.rule) || false;
                    }
                    return result === true;
                  });
                });

              case 8:
                return _context4.abrupt('return', result);

              case 9:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, _this5);
      }));

      return function (_x10, _x11) {
        return _ref5.apply(this, arguments);
      };
    }();
  };

  AvForm.prototype.getDefaultValue = function getDefaultValue(inputName) {
    return (0, _lodash2.default)(this.props.model, inputName);
  };

  AvForm.prototype.getValue = function getValue(inputName) {
    var input = this._inputs[inputName];

    if (Array.isArray(input)) {
      throw new Error('Multiple inputs cannot use the same name: "' + inputName + '"');
    }

    return input.getValue();
  };

  return AvForm;
}(_AvInputContainer2.default);

AvForm.childContextTypes = {
  FormCtrl: _react.PropTypes.object.isRequired
};
AvForm.contextTypes = {
  FormCtrl: _react.PropTypes.object
};
AvForm.propTypes = {
  tag: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.string]),
  className: _react.PropTypes.string,
  model: _react.PropTypes.object,
  method: _react.PropTypes.oneOf(['get', 'post']),
  onSubmit: _react.PropTypes.func,
  validate: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.array]),
  onValidSubmit: _react.PropTypes.func,
  onInvalidSubmit: _react.PropTypes.func,
  validationEvent: _react.PropTypes.oneOf(['onInput', 'onChange', 'onBlur', 'onFocus']),
  errorMessage: _react.PropTypes.oneOfType([_react.PropTypes.object, _react.PropTypes.string, _react.PropTypes.node])
};
AvForm.defaultProps = {
  tag: _reactstrap.Form,
  model: {},
  validationEvent: 'onChange',
  method: 'get',
  onSubmit: function onSubmit() {},
  onKeyDown: function onKeyDown() {},
  onValidSubmit: function onValidSubmit() {},
  onInvalidSubmit: function onInvalidSubmit() {}
};
exports.default = AvForm;