'use strict';

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj['default'] : obj; };

var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } };

/**
 * Actions
 *
 * Instances of the Actions class represent a set of actions. (In Flux parlance,
 * these might be more accurately denoted as Action Creators, while Action
 * refers to the payload sent to the dispatcher, but this is... confusing. We
 * will use Action to mean the function you call to trigger a dispatch.)
 *
 * Create actions by extending from the base Actions class and adding methods.
 * All methods on the prototype (except the constructor) will be
 * converted into actions. The return value of an action is used as the body
 * of the payload sent to the dispatcher.
 */

var _uniqueId = require('uniqueid');

var uniqueId = _interopRequire(_uniqueId);

var Actions = (function () {
  function Actions() {
    _classCallCheck(this, Actions);

    this._baseId = uniqueId();

    var methodNames = this._getActionMethodNames();
    for (var i = 0; i < methodNames.length; i++) {
      var methodName = methodNames[i];
      this._wrapAction(methodName);
    }

    this.getConstants = this.getActionIds;
  }

  Actions.prototype.getActionIds = function getActionIds() {
    var _this = this;

    return this._getActionMethodNames().reduce(function (result, actionName) {
      result[actionName] = _this[actionName]._id;
      return result;
    }, {});
  };

  Actions.prototype._getActionMethodNames = function _getActionMethodNames(instance) {
    var _this2 = this;

    return Object.getOwnPropertyNames(this.constructor.prototype).filter(function (name) {
      return name !== 'constructor' && typeof _this2[name] === 'function';
    });
  };

  Actions.prototype._wrapAction = function _wrapAction(methodName) {
    var _this3 = this;

    var originalMethod = this[methodName];
    var actionId = this._createActionId(methodName);

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

      var body = originalMethod.apply(_this3, args);

      if (isPromise(body)) {
        var promise = body;
        _this3._dispatchAsync(actionId, promise, args, methodName);
      } else {
        _this3._dispatch(actionId, body, args, methodName);
      }

      // Return original method's return value to caller
      return body;
    };

    action._id = actionId;

    this[methodName] = action;
  };

  /**
   * Create unique string constant for an action method, using
   * @param {string} methodName - Name of the action method
   */

  Actions.prototype._createActionId = function _createActionId(methodName) {
    return '' + this._baseId + '-' + methodName;
  };

  Actions.prototype._dispatch = function _dispatch(actionId, body, args, methodName) {
    if (typeof this.dispatch === 'function') {
      if (typeof body !== 'undefined') {
        this.dispatch(actionId, body, args);
      }
    } else {
      if (process.env.NODE_ENV !== 'production') {
        console.warn('You\'ve attempted to perform the action ' + ('' + this.constructor.name + '#' + methodName + ', but it hasn\'t been added ') + 'to a Flux instance.');
      }
    }

    return body;
  };

  Actions.prototype._dispatchAsync = function _dispatchAsync(actionId, promise, args, methodName) {
    if (typeof this.dispatchAsync === 'function') {
      this.dispatchAsync(actionId, promise, args);
    } else {
      if (process.env.NODE_ENV !== 'production') {
        console.warn('You\'ve attempted to perform the asynchronous action ' + ('' + this.constructor.name + '#' + methodName + ', but it hasn\'t been added ') + 'to a Flux instance.');
      }
    }
  };

  return Actions;
})();

module.exports = Actions;

function isPromise(value) {
  return value && typeof value.then === 'function';
}