'use strict';

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

var _defaults;

exports.observe = observe;
exports.observer = observer;
exports.shallowEquals = shallowEquals;

var _helpers = require('./helpers');

var _helpers2 = _interopRequireDefault(_helpers);

var _constants = require('./constants');

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

// Default options.
var defaults = (_defaults = {}, _defineProperty(_defaults, _constants.SKIP, true), _defineProperty(_defaults, _constants.EQUALS, shallowEquals), _defaults);

function observe(store, observers) {
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  if (!_helpers2.default.isStore(store)) {
    throw new Error('observers: invalid `store` argument; ' + 'expected a Redux store object.');
  }

  if (!_helpers2.default.isObserverArray(observers)) {
    throw new Error('observers: invalid `observers` argument; ' + 'expected an array of observer() functions.');
  }

  // Create globally-applicable options for the given observer set.
  var globals = [_constants.SKIP, _constants.EQUALS].reduce(function (globals, key) {
    globals[key] = _helpers2.default.hasKey(options, key) ? options[key] : defaults[key];
    return globals;
  }, {});

  var dispatch = store.dispatch,
      getState = store.getState,
      subscribe = store.subscribe;

  var apply = function apply(state) {
    observers.forEach(function (fn) {
      fn(state, dispatch, globals);
    });
  };
  var listen = function listen() {
    apply(getState());
  };

  var unsubscribe = subscribe(listen);
  listen();
  return unsubscribe;
}

function observer(mapper, dispatcher) {
  var locals = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  mapper = mapper || defaultMapper;
  if (typeof dispatcher !== 'function') {
    throw new Error('observers: a `dispatcher` function must be provided.');
  }

  var initialized = false;
  var current = void 0;
  var observer = function observer(state, dispatch, globals) {
    var previous = current;
    current = mapper(state

    // This branch is run only once, before the Redux reducers
    // return their initial state.
    );if (!initialized) {
      initialized = true;
      var skip = _helpers2.default.hasKey(locals, _constants.SKIP) ? !!locals[_constants.SKIP] : globals[_constants.SKIP];
      if (skip) {
        return;
      }
    }

    var equals = locals[_constants.EQUALS] || globals[_constants.EQUALS];
    if (!equals(current, previous)) {
      dispatcher(dispatch, current, previous);
    }
  };

  observer[_constants.OBSERVER] = true;

  return observer;
}

var defaultMapper = function defaultMapper(state) {
  return state;
};

// Adapted from https://github.com/rackt/react-redux/blob/master/src/utils/shallowEqual.js
function shallowEquals(a, b) {
  if (a === b) {
    return true;
  }

  if (_helpers2.default.isPrimitive(a) || _helpers2.default.isPrimitive(b)) {
    return a === b;
  }

  var _ref = [Object.keys(a), Object.keys(b)],
      aKeys = _ref[0],
      bKeys = _ref[1];

  if (aKeys.length !== bKeys.length) {
    return false;
  }

  for (var i = 0; i < bKeys.length; i++) {
    var key = bKeys[i];
    if (!_helpers2.default.hasKey(a, key) || a[key] !== b[key]) {
      return false;
    }
  }

  return true;
}