'use strict';

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

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 _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 _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _shallowCompare = require('react/lib/shallowCompare');

var _shallowCompare2 = _interopRequireDefault(_shallowCompare);

var _connect = require('react-redux/lib/components/connect');

var _connect2 = _interopRequireDefault(_connect);

var _get2 = require('../utils/get');

var _get3 = _interopRequireDefault(_get2);

var _identity = require('lodash/identity');

var _identity2 = _interopRequireDefault(_identity);

var _omit = require('lodash/omit');

var _omit2 = _interopRequireDefault(_omit);

var _pick = require('lodash/pick');

var _pick2 = _interopRequireDefault(_pick);

var _actions = require('../actions');

var _actions2 = _interopRequireDefault(_actions);

var _controlComponent = require('./control-component');

var _controlComponent2 = _interopRequireDefault(_controlComponent);

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

var change = _actions2.default.change;


function mapStateToProps(state, _ref) {
  var model = _ref.model;

  var modelString = typeof model === 'function' ? model(state) : model;

  return {
    model: modelString
  };
}

function isChecked(props) {
  if ((0, _utils.isMulti)(props.model)) {
    return (props.modelValue || []).filter(function (item) {
      return item === props.value;
    }).length;
  }

  return !!props.modelValue;
}

function getTextValue(value) {
  if (typeof value === 'string' || typeof value === 'number') {
    return '' + value;
  }

  return '';
}

var fieldPropTypes = {
  model: _react.PropTypes.string.isRequired,
  component: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.string]),
  parser: _react.PropTypes.func,
  updateOn: _react.PropTypes.oneOf(['change', 'blur', 'focus']),
  changeAction: _react.PropTypes.func,
  validators: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.object]),
  asyncValidators: _react.PropTypes.object,
  validateOn: _react.PropTypes.string,
  asyncValidateOn: _react.PropTypes.string,
  errors: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.object]),
  mapProps: _react.PropTypes.func,
  componentMap: _react.PropTypes.object,
  dispatch: _react.PropTypes.func,
  getter: _react.PropTypes.func
};

var controlPropsMap = {
  default: function _default(props) {
    return controlPropsMap.text(props);
  },
  checkbox: function checkbox(props) {
    return _extends({
      name: props.name || props.model,
      checked: props.defaultChecked ? props.checked : isChecked(props)
    }, props);
  },
  radio: function radio(props) {
    return _extends({
      name: props.name || props.model,
      checked: props.defaultChecked ? props.checked : props.modelValue === props.value,
      value: props.value
    }, props);
  },
  select: function select(props) {
    return _extends({
      name: props.name || props.model,
      value: props.modelValue
    }, props);
  },
  text: function text(props) {
    return _extends({
      value: props.updateOn === 'change' && !props.defaultValue && !props.hasOwnProperty('value') ? getTextValue(props.modelValue) : props.value,
      name: props.name || props.model
    }, props);
  },
  file: function file(props) {
    return _extends({
      name: props.name || props.model
    }, props);
  },
  textarea: function textarea(props) {
    return controlPropsMap.text(props);
  },
  reset: function reset(props) {
    return {
      onClick: function onClick(event) {
        event.preventDefault();

        props.dispatch(_actions2.default.reset(props.model));
      }
    };
  }
};

function getControlType(control, props, options) {
  var componentMap = props.componentMap;
  var _controlPropsMap = options.controlPropsMap;


  var controlDisplayNames = Object.keys(componentMap).filter(function (controlName) {
    return control.type === componentMap[controlName];
  });

  if (controlDisplayNames.length) return controlDisplayNames[0];

  try {
    var controlDisplayName = control.constructor.displayName || control.type.displayName || control.type.name || control.type;

    if (controlDisplayName === 'input') {
      controlDisplayName = _controlPropsMap[control.props.type] ? control.props.type : 'text';
    }

    return _controlPropsMap[controlDisplayName] ? controlDisplayName : null;
  } catch (error) {
    return undefined;
  }
}

/* eslint-disable no-use-before-define */
function mapFieldChildrenToControl(children, props, options) {
  if (_react2.default.Children.count(children) > 1) {
    return _react2.default.Children.map(children, function (child) {
      return createFieldControlComponent(child, _extends({}, props, child && child.props ? child.props : {}), options);
    });
  }

  return createFieldControlComponent(children, props, options);
}

function createFieldControlComponent(control, props, options) {
  if (!control || !control.props || control instanceof _controlComponent2.default) {
    return control;
  }

  /* eslint-disable react/prop-types */
  var _props$mapProps = props.mapProps;
  var mapProps = _props$mapProps === undefined ? options.controlPropsMap[getControlType(control, props, options)] : _props$mapProps;

  // const controlProps = omit(props, ['children', 'className']);

  var controlProps = (0, _pick2.default)(props, Object.keys(fieldPropTypes));

  if (!mapProps) {
    return _react2.default.cloneElement(control, null, mapFieldChildrenToControl(control.props.children, props, options));
  }

  return _react2.default.createElement(_controlComponent2.default, _extends({}, controlProps, {
    control: control,
    controlProps: control.props,
    component: control.type,
    mapProps: mapProps
  }));
  /* eslint-enable react/prop-types */
}
/* eslint-enable no-use-before-define */

function getFieldWrapper(props) {
  if (props.component) {
    return props.component;
  }

  if (props.className || props.children && props.children.length > 1) {
    return 'div';
  }

  return null;
}

function createFieldClass() {
  var customControlPropsMap = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  var defaultProps = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

  var options = {
    controlPropsMap: _extends({}, controlPropsMap, customControlPropsMap)
  };

  var Field = function (_Component) {
    _inherits(Field, _Component);

    function Field() {
      _classCallCheck(this, Field);

      return _possibleConstructorReturn(this, (Field.__proto__ || Object.getPrototypeOf(Field)).apply(this, arguments));
    }

    _createClass(Field, [{
      key: 'shouldComponentUpdate',
      value: function shouldComponentUpdate(nextProps, nextState) {
        return (0, _shallowCompare2.default)(this, nextProps, nextState);
      }
    }, {
      key: 'render',
      value: function render() {
        var props = this.props;

        var component = getFieldWrapper(props);

        var allowedProps = (0, _omit2.default)(props, Object.keys(fieldPropTypes));

        if (component) {
          return _react2.default.createElement(component, allowedProps, _react2.default.Children.map(props.children, function (child) {
            return createFieldControlComponent(child, props, options);
          }));
        }

        return createFieldControlComponent(_react2.default.Children.only(props.children), props, options);
      }
    }]);

    return Field;
  }(_react.Component);

  Field.propTypes = fieldPropTypes;

  Field.defaultProps = _extends({
    updateOn: 'change',
    validateOn: 'change',
    asyncValidateOn: 'blur',
    parser: _identity2.default,
    changeAction: change,
    componentMap: {}
  }, defaultProps);

  return (0, _connect2.default)(mapStateToProps)(Field);
}

exports.controlPropsMap = controlPropsMap;
exports.createFieldClass = createFieldClass;
exports.default = createFieldClass(controlPropsMap);