'use strict';

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

var _typeof2 = require('babel-runtime/helpers/typeof');

var _typeof3 = _interopRequireDefault(_typeof2);

var _defineProperty2 = require('babel-runtime/helpers/defineProperty');

var _defineProperty3 = _interopRequireDefault(_defineProperty2);

var _extends2 = require('babel-runtime/helpers/extends');

var _extends3 = _interopRequireDefault(_extends2);

var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');

var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

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

var _asyncValidator = require('async-validator');

var _asyncValidator2 = _interopRequireDefault(_asyncValidator);

var _warning = require('warning');

var _warning2 = _interopRequireDefault(_warning);

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

var _lodash2 = _interopRequireDefault(_lodash);

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

var _lodash4 = _interopRequireDefault(_lodash3);

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

var _lodash6 = _interopRequireDefault(_lodash5);

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

var DEFAULT_VALIDATE_TRIGGER = 'onChange';
var DEFAULT_TRIGGER = DEFAULT_VALIDATE_TRIGGER;
var atom = {};

function createBaseForm() {
  var option = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var mixins = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  var mapPropsToFields = option.mapPropsToFields,
      onFieldsChange = option.onFieldsChange,
      onValuesChange = option.onValuesChange,
      fieldNameProp = option.fieldNameProp,
      fieldMetaProp = option.fieldMetaProp,
      validateMessages = option.validateMessages,
      _option$mapProps = option.mapProps,
      mapProps = _option$mapProps === undefined ? _utils.mirror : _option$mapProps,
      _option$formPropName = option.formPropName,
      formPropName = _option$formPropName === undefined ? 'form' : _option$formPropName,
      withRef = option.withRef;


  function decorate(WrappedComponent) {
    var Form = _react2["default"].createClass({
      displayName: 'Form',

      mixins: mixins,

      getInitialState: function getInitialState() {
        var fields = void 0;
        if (mapPropsToFields) {
          fields = mapPropsToFields(this.props);
        }
        this.fields = fields || {};
        this.instances = {};
        this.fieldsMeta = {};
        this.cachedBind = {};
        return {
          submitting: false
        };
      },
      componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
        if (mapPropsToFields) {
          this.fields = mapPropsToFields(nextProps);
        }
      },
      onCollectCommon: function onCollectCommon(name_, action, args) {
        var name = name_;
        var fieldMeta = this.getFieldMeta(name);
        if (fieldMeta[action]) {
          fieldMeta[action].apply(fieldMeta, (0, _toConsumableArray3["default"])(args));
        } else if (fieldMeta.originalProps && fieldMeta.originalProps[action]) {
          var _fieldMeta$originalPr;

          (_fieldMeta$originalPr = fieldMeta.originalProps)[action].apply(_fieldMeta$originalPr, (0, _toConsumableArray3["default"])(args));
        }
        var value = fieldMeta.getValueFromEvent ? fieldMeta.getValueFromEvent.apply(fieldMeta, (0, _toConsumableArray3["default"])(args)) : _utils.getValueFromEvent.apply(undefined, (0, _toConsumableArray3["default"])(args));
        if (onValuesChange) {
          onValuesChange(this.props, (0, _lodash6["default"])({}, name, value));
        }
        var nameKeyObj = (0, _utils.getNameIfNested)(name);
        if (this.getFieldMeta(nameKeyObj.name).exclusive) {
          name = nameKeyObj.name;
        }
        var field = this.getField(name);
        return { name: name, field: (0, _extends3["default"])({}, field, { value: value, touched: true }), fieldMeta: fieldMeta };
      },
      onCollect: function onCollect(name_, action) {
        for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
          args[_key - 2] = arguments[_key];
        }

        var _onCollectCommon = this.onCollectCommon(name_, action, args),
            name = _onCollectCommon.name,
            field = _onCollectCommon.field,
            fieldMeta = _onCollectCommon.fieldMeta;

        var validate = fieldMeta.validate;

        var fieldContent = (0, _extends3["default"])({}, field, {
          dirty: (0, _utils.hasRules)(validate)
        });
        this.setFields((0, _defineProperty3["default"])({}, name, fieldContent));
      },
      onCollectValidate: function onCollectValidate(name_, action) {
        for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
          args[_key2 - 2] = arguments[_key2];
        }

        var _onCollectCommon2 = this.onCollectCommon(name_, action, args),
            field = _onCollectCommon2.field,
            fieldMeta = _onCollectCommon2.fieldMeta;

        var fieldContent = (0, _extends3["default"])({}, field, {
          dirty: true
        });
        this.validateFieldsInternal([fieldContent], {
          action: action,
          options: {
            firstFields: !!fieldMeta.validateFirst
          }
        });
      },
      getCacheBind: function getCacheBind(name, action, fn) {
        var cache = this.cachedBind[name] = this.cachedBind[name] || {};
        if (!cache[action]) {
          cache[action] = fn.bind(this, name, action);
        }
        return cache[action];
      },
      getFieldMeta: function getFieldMeta(name) {
        return this.fieldsMeta[name];
      },
      getField: function getField(name) {
        var fields = this.fields;

        return (0, _extends3["default"])({}, fields[name], {
          name: name
        });
      },
      getFieldDecorator: function getFieldDecorator(name, fieldOption) {
        var _this = this;

        var props = this.getFieldProps(name, fieldOption);
        return function (fieldElem) {
          var fieldMeta = _this.getFieldMeta(name);
          var originalProps = fieldElem.props;
          if (process.env.NODE_ENV !== 'production') {
            var valuePropName = fieldMeta.valuePropName;
            (0, _warning2["default"])(!(valuePropName in originalProps), '`getFieldDecorator` will override `' + valuePropName + '`, ' + ('so please don\'t set `' + valuePropName + '` directly ') + 'and use `setFieldsValue` to set it.');
            var defaultValuePropName = 'default' + valuePropName[0].toUpperCase() + valuePropName.slice(1);
            (0, _warning2["default"])(!(defaultValuePropName in originalProps), '`' + defaultValuePropName + '` is invalid ' + ('for `getFieldDecorator` will set `' + valuePropName + '`,') + ' please use `option.initialValue` instead.');
          }
          fieldMeta.originalProps = originalProps;
          fieldMeta.ref = fieldElem.ref;
          return _react2["default"].cloneElement(fieldElem, (0, _extends3["default"])({}, props, _this.getFieldValuePropValue(fieldMeta)));
        };
      },
      getFieldProps: function getFieldProps(name) {
        var _this2 = this;

        var usersFieldOption = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

        if (!name) {
          throw new Error('Must call `getFieldProps` with valid name string!');
        }

        var nameIfNested = (0, _utils.getNameIfNested)(name);
        var leadingName = nameIfNested.name;
        var fieldOption = (0, _extends3["default"])({
          valuePropName: 'value',
          validate: [],
          trigger: DEFAULT_TRIGGER,
          validateTrigger: DEFAULT_VALIDATE_TRIGGER,
          leadingName: leadingName,
          name: name
        }, usersFieldOption);

        var rules = fieldOption.rules,
            trigger = fieldOption.trigger,
            exclusive = fieldOption.exclusive,
            validateTrigger = fieldOption.validateTrigger,
            validate = fieldOption.validate;
        var fieldsMeta = this.fieldsMeta;

        var fieldMeta = void 0;
        fieldMeta = fieldsMeta[name] = fieldsMeta[name] || {};
        if ('initialValue' in fieldOption) {
          fieldMeta.initialValue = fieldOption.initialValue;
        }

        var leadingFieldMeta = fieldsMeta[leadingName];
        if (nameIfNested.isNested) {
          leadingFieldMeta = fieldsMeta[leadingName] = fieldsMeta[leadingName] || {};
          leadingFieldMeta.virtual = !exclusive;
          // exclusive allow getFieldProps('x', {initialValue})
          // non-exclusive does not allow getFieldProps('x', {initialValue})
          leadingFieldMeta.hidden = !exclusive;
          leadingFieldMeta.exclusive = exclusive;
        }

        var inputProps = (0, _extends3["default"])({}, this.getFieldValuePropValue(fieldOption), {
          ref: this.getCacheBind(name, name + '__ref', this.saveRef)
        });
        if (fieldNameProp) {
          inputProps[fieldNameProp] = name;
        }

        var validateRules = (0, _utils.normalizeValidateRules)(validate, rules, validateTrigger);
        var validateTriggers = validateRules.filter(function (item) {
          return !!item.rules && item.rules.length;
        }).map(function (item) {
          return item.trigger;
        }).reduce(function (pre, curr) {
          return pre.concat(curr);
        }, []);
        validateTriggers.forEach(function (action) {
          if (inputProps[action]) return;
          inputProps[action] = _this2.getCacheBind(name, action, _this2.onCollectValidate);
        });

        // make sure that the value will be collect
        if (trigger && validateTriggers.indexOf(trigger) === -1) {
          inputProps[trigger] = this.getCacheBind(name, trigger, this.onCollect);
        }

        var meta = (0, _extends3["default"])({}, fieldMeta, fieldOption, {
          validate: validateRules
        });
        fieldsMeta[name] = meta;
        if (fieldMetaProp) {
          inputProps[fieldMetaProp] = meta;
        }

        return inputProps;
      },
      getFieldValuePropValue: function getFieldValuePropValue(fieldMeta) {
        var exclusive = fieldMeta.exclusive,
            leadingName = fieldMeta.leadingName,
            name = fieldMeta.name,
            getValueProps = fieldMeta.getValueProps,
            valuePropName = fieldMeta.valuePropName;
        var fieldsMeta = this.fieldsMeta;

        var field = exclusive ? this.getField(leadingName) : this.getField(name);
        var fieldValue = atom;
        if (field && 'value' in field) {
          fieldValue = field.value;
        }
        if (fieldValue === atom) {
          fieldValue = exclusive ? fieldsMeta[leadingName].initialValue : fieldMeta.initialValue;
        }
        if (getValueProps) {
          return getValueProps(fieldValue);
        }
        return (0, _defineProperty3["default"])({}, valuePropName, fieldValue);
      },
      getFieldMember: function getFieldMember(name, member) {
        var field = this.getField(name);
        return field && field[member];
      },
      getFieldsError: function getFieldsError(names) {
        var _this3 = this;

        var fields = names || (0, _utils.flatFieldNames)(this.getValidFieldsName());
        var allErrors = {};
        fields.forEach(function (f) {
          (0, _lodash6["default"])(allErrors, f, _this3.getFieldError(f));
        });
        return allErrors;
      },
      getFieldError: function getFieldError(name) {
        return (0, _utils.getErrorStrs)(this.getFieldMember(name, 'errors'));
      },
      getValidFieldsName: function getValidFieldsName() {
        var fieldsMeta = this.fieldsMeta;
        return fieldsMeta ? Object.keys(fieldsMeta).filter(function (name) {
          return !fieldsMeta[name].hidden;
        }) : [];
      },
      getFieldsValue: function getFieldsValue(names) {
        var _this4 = this;

        var fields = names || (0, _utils.flatFieldNames)(this.getValidFieldsName());
        var allValues = {};
        fields.forEach(function (f) {
          (0, _lodash6["default"])(allValues, f, _this4.getFieldValue(f));
        });
        return allValues;
      },
      getFieldValue: function getFieldValue(name) {
        var fields = this.fields;

        return this.getValueFromFields(name, fields);
      },
      getFieldInstance: function getFieldInstance(name) {
        return this.instances[name];
      },
      getValueFromFieldsInternal: function getValueFromFieldsInternal(name, fields) {
        var field = fields[name];
        if (field && 'value' in field) {
          return field.value;
        }
        var fieldMeta = this.fieldsMeta[name];
        return fieldMeta && fieldMeta.initialValue;
      },
      getValueFromFields: function getValueFromFields(name, fields) {
        var _this5 = this;

        var fieldsMeta = this.fieldsMeta;

        if (fieldsMeta[name] && fieldsMeta[name].virtual) {
          var _ret = function () {
            var ret = {};
            Object.keys(fieldsMeta).forEach(function (fieldKey) {
              var nameIfNested = (0, _utils.getNameIfNested)(fieldKey);
              if (nameIfNested.name === name && nameIfNested.isNested) {
                (0, _lodash6["default"])(ret, fieldKey, _this5.getValueFromFieldsInternal(fieldKey, fields));
              }
            });
            return {
              v: ret[name]
            };
          }();

          if ((typeof _ret === 'undefined' ? 'undefined' : (0, _typeof3["default"])(_ret)) === "object") return _ret.v;
        }
        return this.getValueFromFieldsInternal(name, fields);
      },
      getRules: function getRules(fieldMeta, action) {
        var actionRules = fieldMeta.validate.filter(function (item) {
          return !action || item.trigger.indexOf(action) >= 0;
        }).map(function (item) {
          return item.rules;
        });
        return (0, _utils.flattenArray)(actionRules);
      },
      setFields: function setFields(fields_) {
        var _this6 = this;

        var fieldsMeta = this.fieldsMeta;
        var fields = fields_;
        var nowFields = (0, _extends3["default"])({}, this.fields, fields);
        var nowValues = {};
        Object.keys(fieldsMeta).forEach(function (f) {
          var _getNameIfNested = (0, _utils.getNameIfNested)(f),
              name = _getNameIfNested.name,
              isNested = _getNameIfNested.isNested;

          if (isNested && fieldsMeta[name].exclusive) {
            return;
          }
          nowValues[f] = _this6.getValueFromFields(f, nowFields);
        });
        Object.keys(nowValues).forEach(function (f) {
          var value = nowValues[f];
          var fieldMeta = fieldsMeta[f];
          if (fieldMeta && fieldMeta.normalize) {
            var nowValue = fieldMeta.normalize(value, _this6.getValueFromFields(f, _this6.fields), nowValues);
            if (nowValue !== value) {
              nowFields[f] = (0, _extends3["default"])({}, nowFields[f], {
                value: nowValue
              });
            }
          }
        });
        this.fields = nowFields;
        if (onFieldsChange) {
          (function () {
            var changedFieldsName = Object.keys(fields);
            var changedFields = {};
            changedFieldsName.forEach(function (f) {
              changedFields[f] = _this6.getField(f);
            });
            onFieldsChange(_this6.props, changedFields);
          })();
        }
        this.forceUpdate();
      },
      setFieldsValue: function setFieldsValue(fieldsValue) {
        if (onValuesChange) {
          onValuesChange(this.props, fieldsValue);
        }
        var newFields = {};
        var fieldsMeta = this.fieldsMeta,
            fields = this.fields;

        var virtualPaths = (0, _utils.getVirtualPaths)(fieldsMeta);
        Object.keys(fieldsValue).forEach(function (name) {
          var value = fieldsValue[name];
          if (fieldsMeta[name] && fieldsMeta[name].virtual) {
            (0, _utils.clearVirtualField)(name, fields, fieldsMeta);
            for (var i = 0, len = virtualPaths[name].length; i < len; i++) {
              var path = virtualPaths[name][i];
              if ((0, _lodash4["default"])(fieldsValue, path)) {
                newFields[path] = {
                  name: path,
                  value: (0, _lodash2["default"])(fieldsValue, path)
                };
              }
            }
          } else if (fieldsMeta[name]) {
            newFields[name] = {
              name: name,
              value: value
            };
          }
        });
        this.setFields(newFields);
      },
      setFieldsInitialValue: function setFieldsInitialValue(initialValues) {
        var fieldsMeta = this.fieldsMeta;
        var virtualPaths = (0, _utils.getVirtualPaths)(fieldsMeta);
        Object.keys(initialValues).forEach(function (name) {
          if (fieldsMeta[name] && fieldsMeta[name].virtual) {
            for (var i = 0, len = virtualPaths[name].length; i < len; i++) {
              var path = virtualPaths[name][i];
              if ((0, _lodash4["default"])(initialValues, path)) {
                fieldsMeta[path] = (0, _extends3["default"])({}, fieldsMeta[path], {
                  initialValue: (0, _lodash2["default"])(initialValues, path)
                });
              }
            }
          } else if (fieldsMeta[name]) {
            fieldsMeta[name] = (0, _extends3["default"])({}, fieldsMeta[name], {
              initialValue: initialValues[name]
            });
          }
        });
      },
      saveRef: function saveRef(name, _, component) {
        if (!component) {
          // after destroy, delete data
          delete this.fieldsMeta[name];
          delete this.fields[name];
          delete this.instances[name];
          delete this.cachedBind[name];
          return;
        }
        var fieldMeta = this.getFieldMeta(name);
        if (fieldMeta) {
          var ref = fieldMeta.ref;
          if (ref) {
            if (typeof ref === 'string') {
              throw new Error('can not set ref string for ' + name);
            }
            ref(component);
          }
        }
        this.instances[name] = component;
      },
      validateFieldsInternal: function validateFieldsInternal(fields, _ref2, callback) {
        var _this7 = this;

        var fieldNames = _ref2.fieldNames,
            action = _ref2.action,
            _ref2$options = _ref2.options,
            options = _ref2$options === undefined ? {} : _ref2$options;

        var allRules = {};
        var allValues = {};
        var allFields = {};
        var alreadyErrors = {};
        fields.forEach(function (field) {
          var name = field.name;
          if (options.force !== true && field.dirty === false) {
            if (field.errors) {
              (0, _lodash6["default"])(alreadyErrors, name, { errors: field.errors });
            }
            return;
          }
          var fieldMeta = _this7.getFieldMeta(name);
          var newField = (0, _extends3["default"])({}, field);
          newField.errors = undefined;
          newField.validating = true;
          newField.dirty = true;
          allRules[name] = _this7.getRules(fieldMeta, action);
          allValues[name] = newField.value;
          allFields[name] = newField;
        });
        this.setFields(allFields);
        // in case normalize
        Object.keys(allValues).forEach(function (f) {
          allValues[f] = _this7.getFieldValue(f);
        });
        if (callback && (0, _utils.isEmptyObject)(allFields)) {
          callback((0, _utils.isEmptyObject)(alreadyErrors) ? null : alreadyErrors, this.getFieldsValue((0, _utils.flatFieldNames)(fieldNames)));
          return;
        }
        var validator = new _asyncValidator2["default"](allRules);
        if (validateMessages) {
          validator.messages(validateMessages);
        }
        validator.validate(allValues, options, function (errors) {
          var errorsGroup = (0, _extends3["default"])({}, alreadyErrors);
          if (errors && errors.length) {
            errors.forEach(function (e) {
              var fieldName = e.field;
              if (!(0, _lodash4["default"])(errorsGroup, fieldName)) {
                (0, _lodash6["default"])(errorsGroup, fieldName, { errors: [] });
              }
              var fieldErrors = (0, _lodash2["default"])(errorsGroup, fieldName.concat('.errors'));
              fieldErrors.push(e);
            });
          }
          var expired = [];
          var nowAllFields = {};
          Object.keys(allRules).forEach(function (name) {
            var fieldErrors = (0, _lodash2["default"])(errorsGroup, name);
            var nowField = _this7.getField(name);
            // avoid concurrency problems
            if (nowField.value !== allValues[name]) {
              expired.push({
                name: name
              });
            } else {
              nowField.errors = fieldErrors && fieldErrors.errors;
              nowField.value = allValues[name];
              nowField.validating = false;
              nowField.dirty = false;
              nowAllFields[name] = nowField;
            }
          });
          _this7.setFields(nowAllFields);
          if (callback) {
            if (expired.length) {
              expired.forEach(function (_ref3) {
                var name = _ref3.name;

                var fieldErrors = [{
                  message: name + ' need to revalidate',
                  field: name
                }];
                (0, _lodash6["default"])(errorsGroup, name, {
                  expired: true,
                  errors: fieldErrors
                });
              });
            }

            callback((0, _utils.isEmptyObject)(errorsGroup) ? null : errorsGroup, _this7.getFieldsValue((0, _utils.flatFieldNames)(fieldNames)));
          }
        });
      },
      validateFields: function validateFields(ns, opt, cb) {
        var _this8 = this;

        var _getParams = (0, _utils.getParams)(ns, opt, cb),
            names = _getParams.names,
            callback = _getParams.callback,
            options = _getParams.options;

        var fieldNames = names || this.getValidFieldsName();
        var fields = fieldNames.filter(function (name) {
          var fieldMeta = _this8.getFieldMeta(name);
          return (0, _utils.hasRules)(fieldMeta.validate);
        }).map(function (name) {
          var field = _this8.getField(name);
          field.value = _this8.getFieldValue(name);
          return field;
        });
        if (!fields.length) {
          if (callback) {
            callback(null, this.getFieldsValue((0, _utils.flatFieldNames)(fieldNames)));
          }
          return;
        }
        if (!('firstFields' in options)) {
          options.firstFields = fieldNames.filter(function (name) {
            var fieldMeta = _this8.getFieldMeta(name);
            return !!fieldMeta.validateFirst;
          });
        }
        this.validateFieldsInternal(fields, {
          fieldNames: fieldNames,
          options: options
        }, callback);
      },
      isFieldValidating: function isFieldValidating(name) {
        return this.getFieldMember(name, 'validating');
      },
      isFieldsValidating: function isFieldsValidating(ns) {
        var _this9 = this;

        var names = ns || this.getValidFieldsName();
        return names.some(function (n) {
          return _this9.isFieldValidating(n);
        });
      },
      isFieldTouched: function isFieldTouched(name) {
        return this.getFieldMember(name, 'touched');
      },
      isFieldsTouched: function isFieldsTouched(ns) {
        var _this10 = this;

        var names = ns || this.getValidFieldsName();
        return names.some(function (n) {
          return _this10.isFieldTouched(n);
        });
      },
      isSubmitting: function isSubmitting() {
        return this.state.submitting;
      },
      submit: function submit(callback) {
        var _this11 = this;

        var fn = function fn() {
          _this11.setState({
            submitting: false
          });
        };
        this.setState({
          submitting: true
        });
        callback(fn);
      },
      resetFields: function resetFields(ns) {
        var newFields = {};
        var fields = this.fields;

        var changed = false;
        var names = ns || Object.keys(fields);
        names.forEach(function (name) {
          var field = fields[name];
          if (field && 'value' in field) {
            changed = true;
            newFields[name] = {};
          }
        });
        if (changed) {
          this.setFields(newFields);
        }
      },
      render: function render() {
        var formProps = (0, _defineProperty3["default"])({}, formPropName, this.getForm());
        if (withRef) {
          formProps.ref = 'wrappedComponent';
        }
        var props = mapProps.call(this, (0, _extends3["default"])({}, formProps, this.props));
        return _react2["default"].createElement(WrappedComponent, props);
      }
    });

    return (0, _utils.argumentContainer)(Form, WrappedComponent);
  }

  return decorate;
}

exports["default"] = createBaseForm;
module.exports = exports['default'];