text: 'use strict';
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  
  var _actionTypes = require('./actionTypes');
  
  var _deleteInWithCleanUp = require('./deleteInWithCleanUp');
  
  var _deleteInWithCleanUp2 = _interopRequireDefault(_deleteInWithCleanUp);
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return 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; }
  
  var createReducer = function createReducer(structure) {
    var _behaviors;
  
    var deepEqual = structure.deepEqual,
        empty = structure.empty,
        getIn = structure.getIn,
        setIn = structure.setIn,
        deleteIn = structure.deleteIn,
        fromJS = structure.fromJS,
        keys = structure.keys,
        size = structure.size,
        splice = structure.splice;
  
    var deleteInWithCleanUp = (0, _deleteInWithCleanUp2.default)(structure);
    var doSplice = function doSplice(state, key, field, index, removeNum, value, force) {
      var existing = getIn(state, key + '.' + field);
      return existing || force ? setIn(state, key + '.' + field, splice(existing, index, removeNum, value)) : state;
    };
    var rootKeys = ['values', 'fields', 'submitErrors', 'asyncErrors'];
    var arraySplice = function arraySplice(state, field, index, removeNum, value) {
      var result = state;
      var nonValuesValue = value != null ? empty : undefined;
      result = doSplice(result, 'values', field, index, removeNum, value, true);
      result = doSplice(result, 'fields', field, index, removeNum, nonValuesValue);
      result = doSplice(result, 'syncErrors', field, index, removeNum, undefined);
      result = doSplice(result, 'submitErrors', field, index, removeNum, undefined);
      result = doSplice(result, 'asyncErrors', field, index, removeNum, undefined);
      return result;
    };
  
    var behaviors = (_behaviors = {}, _defineProperty(_behaviors, _actionTypes.ARRAY_INSERT, function (state, _ref) {
      var _ref$meta = _ref.meta,
          field = _ref$meta.field,
          index = _ref$meta.index,
          payload = _ref.payload;
  
      return arraySplice(state, field, index, 0, payload);
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_MOVE, function (state, _ref2) {
      var _ref2$meta = _ref2.meta,
          field = _ref2$meta.field,
          from = _ref2$meta.from,
          to = _ref2$meta.to;
  
      var array = getIn(state, 'values.' + field);
      var length = array ? size(array) : 0;
      var result = state;
      if (length) {
        rootKeys.forEach(function (key) {
          var path = key + '.' + field;
          if (getIn(result, path)) {
            var value = getIn(result, path + '[' + from + ']');
            result = setIn(result, path, splice(getIn(result, path), from, 1)); // remove
            result = setIn(result, path, splice(getIn(result, path), to, 0, value)); // insert
          }
        });
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_POP, function (state, _ref3) {
      var field = _ref3.meta.field;
  
      var array = getIn(state, 'values.' + field);
      var length = array ? size(array) : 0;
      return length ? arraySplice(state, field, length - 1, 1) : state;
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_PUSH, function (state, _ref4) {
      var field = _ref4.meta.field,
          payload = _ref4.payload;
  
      var array = getIn(state, 'values.' + field);
      var length = array ? size(array) : 0;
      return arraySplice(state, field, length, 0, payload);
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_REMOVE, function (state, _ref5) {
      var _ref5$meta = _ref5.meta,
          field = _ref5$meta.field,
          index = _ref5$meta.index;
  
      return arraySplice(state, field, index, 1);
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_REMOVE_ALL, function (state, _ref6) {
      var field = _ref6.meta.field;
  
      var array = getIn(state, 'values.' + field);
      var length = array ? size(array) : 0;
      return length ? arraySplice(state, field, 0, length) : state;
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_SHIFT, function (state, _ref7) {
      var field = _ref7.meta.field;
  
      return arraySplice(state, field, 0, 1);
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_SPLICE, function (state, _ref8) {
      var _ref8$meta = _ref8.meta,
          field = _ref8$meta.field,
          index = _ref8$meta.index,
          removeNum = _ref8$meta.removeNum,
          payload = _ref8.payload;
  
      return arraySplice(state, field, index, removeNum, payload);
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_SWAP, function (state, _ref9) {
      var _ref9$meta = _ref9.meta,
          field = _ref9$meta.field,
          indexA = _ref9$meta.indexA,
          indexB = _ref9$meta.indexB;
  
      var result = state;
      rootKeys.forEach(function (key) {
        var valueA = getIn(result, key + '.' + field + '[' + indexA + ']');
        var valueB = getIn(result, key + '.' + field + '[' + indexB + ']');
        if (valueA !== undefined || valueB !== undefined) {
          result = setIn(result, key + '.' + field + '[' + indexA + ']', valueB);
          result = setIn(result, key + '.' + field + '[' + indexB + ']', valueA);
        }
      });
      return result;
    }), _defineProperty(_behaviors, _actionTypes.ARRAY_UNSHIFT, function (state, _ref10) {
      var field = _ref10.meta.field,
          payload = _ref10.payload;
  
      return arraySplice(state, field, 0, 0, payload);
    }), _defineProperty(_behaviors, _actionTypes.AUTOFILL, function (state, _ref11) {
      var field = _ref11.meta.field,
          payload = _ref11.payload;
  
      var result = state;
      result = deleteInWithCleanUp(result, 'asyncErrors.' + field);
      result = deleteInWithCleanUp(result, 'submitErrors.' + field);
      result = setIn(result, 'fields.' + field + '.autofilled', true);
      result = setIn(result, 'values.' + field, payload);
      return result;
    }), _defineProperty(_behaviors, _actionTypes.BLUR, function (state, _ref12) {
      var _ref12$meta = _ref12.meta,
          field = _ref12$meta.field,
          touch = _ref12$meta.touch,
          payload = _ref12.payload;
  
      var result = state;
      var initial = getIn(result, 'initial.' + field);
      if (initial === undefined && payload === '') {
        result = deleteInWithCleanUp(result, 'values.' + field);
      } else if (payload !== undefined) {
        result = setIn(result, 'values.' + field, payload);
      }
      if (field === getIn(result, 'active')) {
        result = deleteIn(result, 'active');
      }
      result = deleteIn(result, 'fields.' + field + '.active');
      if (touch) {
        result = setIn(result, 'fields.' + field + '.touched', true);
        result = setIn(result, 'anyTouched', true);
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.CHANGE, function (state, _ref13) {
      var _ref13$meta = _ref13.meta,
          field = _ref13$meta.field,
          touch = _ref13$meta.touch,
          persistentSubmitErrors = _ref13$meta.persistentSubmitErrors,
          payload = _ref13.payload;
  
      var result = state;
      var initial = getIn(result, 'initial.' + field);
      if (initial === undefined && payload === '') {
        result = deleteInWithCleanUp(result, 'values.' + field);
      } else if (payload !== undefined) {
        result = setIn(result, 'values.' + field, payload);
      }
      result = deleteInWithCleanUp(result, 'asyncErrors.' + field);
      if (!persistentSubmitErrors) {
        result = deleteInWithCleanUp(result, 'submitErrors.' + field);
      }
      result = deleteInWithCleanUp(result, 'fields.' + field + '.autofilled');
      if (touch) {
        result = setIn(result, 'fields.' + field + '.touched', true);
        result = setIn(result, 'anyTouched', true);
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.CLEAR_SUBMIT, function (state) {
      return deleteIn(state, 'triggerSubmit');
    }), _defineProperty(_behaviors, _actionTypes.CLEAR_SUBMIT_ERRORS, function (state) {
      return deleteInWithCleanUp(state, 'submitErrors');
    }), _defineProperty(_behaviors, _actionTypes.CLEAR_ASYNC_ERROR, function (state, _ref14) {
      var field = _ref14.meta.field;
  
      return deleteIn(state, 'asyncErrors.' + field);
    }), _defineProperty(_behaviors, _actionTypes.FOCUS, function (state, _ref15) {
      var field = _ref15.meta.field;
  
      var result = state;
      var previouslyActive = getIn(state, 'active');
      result = deleteIn(result, 'fields.' + previouslyActive + '.active');
      result = setIn(result, 'fields.' + field + '.visited', true);
      result = setIn(result, 'fields.' + field + '.active', true);
      result = setIn(result, 'active', field);
      return result;
    }), _defineProperty(_behaviors, _actionTypes.INITIALIZE, function (state, _ref16) {
      var payload = _ref16.payload,
          _ref16$meta = _ref16.meta,
          keepDirty = _ref16$meta.keepDirty,
          keepSubmitSucceeded = _ref16$meta.keepSubmitSucceeded;
  
      var mapData = fromJS(payload);
      var result = empty; // clean all field state
  
      // persist old warnings, they will get recalculated if the new form values are different from the old values
      var warning = getIn(state, 'warning');
      if (warning) {
        result = setIn(result, 'warning', warning);
      }
      var syncWarnings = getIn(state, 'syncWarnings');
      if (syncWarnings) {
        result = setIn(result, 'syncWarnings', syncWarnings);
      }
  
      // persist old errors, they will get recalculated if the new form values are different from the old values
      var error = getIn(state, 'error');
      if (error) {
        result = setIn(result, 'error', error);
      }
      var syncErrors = getIn(state, 'syncErrors');
      if (syncErrors) {
        result = setIn(result, 'syncErrors', syncErrors);
      }
  
      var registeredFields = getIn(state, 'registeredFields');
      if (registeredFields) {
        result = setIn(result, 'registeredFields', registeredFields);
      }
      var newValues = mapData;
      if (keepDirty && registeredFields) {
        //
        // Keep the value of dirty fields while updating the value of
        // pristine fields. This way, apps can reinitialize forms while
        // avoiding stomping on user edits.
        //
        // Note 1: The initialize action replaces all initial values
        // regardless of keepDirty.
        //
        // Note 2: When a field is dirty, keepDirty is enabled, and the field
        // value is the same as the new initial value for the field, the
        // initialize action causes the field to become pristine. That effect
        // is what we want.
        //
        var previousValues = getIn(state, 'values');
        var previousInitialValues = getIn(state, 'initial');
        keys(registeredFields).forEach(function (name) {
          var previousInitialValue = getIn(previousInitialValues, name);
          var previousValue = getIn(previousValues, name);
          if (!deepEqual(previousValue, previousInitialValue)) {
            // This field was dirty. Restore the dirty value.
            newValues = setIn(newValues, name, previousValue);
          }
        });
      }
      if (keepSubmitSucceeded && getIn(state, 'submitSucceeded')) {
        result = setIn(result, 'submitSucceeded', true);
      }
      result = setIn(result, 'values', newValues);
      result = setIn(result, 'initial', mapData);
      return result;
    }), _defineProperty(_behaviors, _actionTypes.REGISTER_FIELD, function (state, _ref17) {
      var _ref17$payload = _ref17.payload,
          name = _ref17$payload.name,
          type = _ref17$payload.type;
  
      var key = 'registeredFields[\'' + name + '\']';
      var field = getIn(state, key);
      if (field) {
        var count = getIn(field, 'count') + 1;
        field = setIn(field, 'count', count);
      } else {
        field = fromJS({ name: name, type: type, count: 1 });
      }
      return setIn(state, key, field);
    }), _defineProperty(_behaviors, _actionTypes.RESET, function (state) {
      var result = empty;
      var registeredFields = getIn(state, 'registeredFields');
      if (registeredFields) {
        result = setIn(result, 'registeredFields', registeredFields);
      }
      var values = getIn(state, 'initial');
      if (values) {
        result = setIn(result, 'values', values);
        result = setIn(result, 'initial', values);
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.SUBMIT, function (state) {
      return setIn(state, 'triggerSubmit', true);
    }), _defineProperty(_behaviors, _actionTypes.START_ASYNC_VALIDATION, function (state, _ref18) {
      var field = _ref18.meta.field;
  
      return setIn(state, 'asyncValidating', field || true);
    }), _defineProperty(_behaviors, _actionTypes.START_SUBMIT, function (state) {
      return setIn(state, 'submitting', true);
    }), _defineProperty(_behaviors, _actionTypes.STOP_ASYNC_VALIDATION, function (state, _ref19) {
      var payload = _ref19.payload;
  
      var result = state;
      result = deleteIn(result, 'asyncValidating');
      if (payload && Object.keys(payload).length) {
        var _error = payload._error,
            fieldErrors = _objectWithoutProperties(payload, ['_error']);
  
        if (_error) {
          result = setIn(result, 'error', _error);
        }
        if (Object.keys(fieldErrors).length) {
          result = setIn(result, 'asyncErrors', fromJS(fieldErrors));
        } else {
          result = deleteIn(result, 'asyncErrors');
        }
      } else {
        result = deleteIn(result, 'error');
        result = deleteIn(result, 'asyncErrors');
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.STOP_SUBMIT, function (state, _ref20) {
      var payload = _ref20.payload;
  
      var result = state;
      result = deleteIn(result, 'submitting');
      result = deleteIn(result, 'submitFailed');
      result = deleteIn(result, 'submitSucceeded');
      if (payload && Object.keys(payload).length) {
        var _error = payload._error,
            fieldErrors = _objectWithoutProperties(payload, ['_error']);
  
        if (_error) {
          result = setIn(result, 'error', _error);
        } else {
          result = deleteIn(result, 'error');
        }
        if (Object.keys(fieldErrors).length) {
          result = setIn(result, 'submitErrors', fromJS(fieldErrors));
        } else {
          result = deleteIn(result, 'submitErrors');
        }
        result = setIn(result, 'submitFailed', true);
      } else {
        result = setIn(result, 'submitSucceeded', true);
        result = deleteIn(result, 'error');
        result = deleteIn(result, 'submitErrors');
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.SET_SUBMIT_FAILED, function (state, _ref21) {
      var fields = _ref21.meta.fields;
  
      var result = state;
      result = setIn(result, 'submitFailed', true);
      result = deleteIn(result, 'submitSucceeded');
      result = deleteIn(result, 'submitting');
      fields.forEach(function (field) {
        return result = setIn(result, 'fields.' + field + '.touched', true);
      });
      if (fields.length) {
        result = setIn(result, 'anyTouched', true);
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.SET_SUBMIT_SUCCEEDED, function (state) {
      var result = state;
      result = deleteIn(result, 'submitFailed');
      result = setIn(result, 'submitSucceeded', true);
      return result;
    }), _defineProperty(_behaviors, _actionTypes.TOUCH, function (state, _ref22) {
      var fields = _ref22.meta.fields;
  
      var result = state;
      fields.forEach(function (field) {
        return result = setIn(result, 'fields.' + field + '.touched', true);
      });
      result = setIn(result, 'anyTouched', true);
      return result;
    }), _defineProperty(_behaviors, _actionTypes.UNREGISTER_FIELD, function (state, _ref23) {
      var _ref23$payload = _ref23.payload,
          name = _ref23$payload.name,
          destroyOnUnmount = _ref23$payload.destroyOnUnmount;
  
      var result = state;
      var key = 'registeredFields[\'' + name + '\']';
      var field = getIn(result, key);
      if (!field) {
        return result;
      }
  
      var count = getIn(field, 'count') - 1;
      if (count <= 0 && destroyOnUnmount) {
        result = deleteIn(result, key);
        if (deepEqual(getIn(result, 'registeredFields'), empty)) {
          result = deleteIn(result, 'registeredFields');
        }
      } else {
        field = setIn(field, 'count', count);
        result = setIn(result, key, field);
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.UNTOUCH, function (state, _ref24) {
      var fields = _ref24.meta.fields;
  
      var result = state;
      fields.forEach(function (field) {
        return result = deleteIn(result, 'fields.' + field + '.touched');
      });
      var anyTouched = keys(getIn(result, 'registeredFields')).some(function (key) {
        return getIn(result, 'fields.' + key + '.touched');
      });
      result = anyTouched ? setIn(result, 'anyTouched', true) : deleteIn(result, 'anyTouched');
      return result;
    }), _defineProperty(_behaviors, _actionTypes.UPDATE_SYNC_ERRORS, function (state, _ref25) {
      var _ref25$payload = _ref25.payload,
          syncErrors = _ref25$payload.syncErrors,
          error = _ref25$payload.error;
  
      var result = state;
      if (error) {
        result = setIn(result, 'error', error);
        result = setIn(result, 'syncError', true);
      } else {
        result = deleteIn(result, 'error');
        result = deleteIn(result, 'syncError');
      }
      if (Object.keys(syncErrors).length) {
        result = setIn(result, 'syncErrors', syncErrors);
      } else {
        result = deleteIn(result, 'syncErrors');
      }
      return result;
    }), _defineProperty(_behaviors, _actionTypes.UPDATE_SYNC_WARNINGS, function (state, _ref26) {
      var _ref26$payload = _ref26.payload,
          syncWarnings = _ref26$payload.syncWarnings,
          warning = _ref26$payload.warning;
  
      var result = state;
      if (warning) {
        result = setIn(result, 'warning', warning);
      } else {
        result = deleteIn(result, 'warning');
      }
      if (Object.keys(syncWarnings).length) {
        result = setIn(result, 'syncWarnings', syncWarnings);
      } else {
        result = deleteIn(result, 'syncWarnings');
      }
      return result;
    }), _behaviors);
  
    var reducer = function reducer() {
      var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : empty;
      var action = arguments[1];
  
      var behavior = behaviors[action.type];
      return behavior ? behavior(state, action) : state;
    };
  
    var byForm = function byForm(reducer) {
      return function () {
        var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : empty;
        var action = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  
        var form = action && action.meta && action.meta.form;
        if (!form) {
          return state;
        }
        if (action.type === _actionTypes.DESTROY) {
          return action.meta.form.reduce(function (result, form) {
            return deleteInWithCleanUp(result, form);
          }, state);
        }
        var formState = getIn(state, form);
        var result = reducer(formState, action);
        return result === formState ? state : setIn(state, form, result);
      };
    };
  
    /**
     * Adds additional functionality to the reducer
     */
    function decorate(target) {
      target.plugin = function plugin(reducers) {
        var _this = this;
  
        // use 'function' keyword to enable 'this'
        return decorate(function () {
          var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : empty;
          var action = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
          return Object.keys(reducers).reduce(function (accumulator, key) {
            var previousState = getIn(accumulator, key);
            var nextState = reducers[key](previousState, action, getIn(state, key));
            return nextState === previousState ? accumulator : setIn(accumulator, key, nextState);
          }, _this(state, action));
        });
      };
  
      return target;
    }
  
    return decorate(byForm(reducer));
  };
  
  exports.default = createReducer;