'use strict';

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

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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

exports.default = createData;

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function createData(initial) {
  var listeners = {};
  var state = initial;

  var data = {
    emit: emit,
    subscribe: subscribe,
    get state() {
      return state;
    },
    set state(extend) {
      throw new Error('Expected extend state by emit instead of mutate directly');
    }
  };

  for (var _len = arguments.length, middlewares = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    middlewares[_key - 1] = arguments[_key];
  }

  var transform = pipe.apply(undefined, _toConsumableArray(middlewares.map(function (mw) {
    return mw(data);
  })))(merge);

  function pipe() {
    for (var _len2 = arguments.length, fns = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
      fns[_key2] = arguments[_key2];
    }

    if (fns.length === 0) {
      return function (arg) {
        return arg;
      };
    }

    if (fns.length === 1) {
      return fns[0];
    }

    return fns.reduce(function (a, b) {
      return function () {
        return a(b.apply(undefined, arguments));
      };
    });
  }

  function notify(stateKey, state) {
    var nested = listeners[stateKey];

    if (nested) {
      for (var i = 0; i < nested.length; i++) {
        nested[i](state);
      }
    }
  }

  function merge(extend) {
    if (!extend || state === extend) {
      return extend;
    }

    if ((typeof extend === 'undefined' ? 'undefined' : _typeof(extend)) !== 'object') {
      throw new TypeError('Expected state extender to be an object');
    }

    state = _extends({}, state, extend);

    for (var extendKey in extend) {
      if (extend.hasOwnProperty(extendKey)) {
        notify(extendKey, state);
      }
    }

    notify('*', state);

    return extend;
  }

  function emit(extend) {
    if (typeof extend !== 'function' && (typeof extend === 'undefined' ? 'undefined' : _typeof(extend)) !== 'object') {
      throw new TypeError('Expected emitted value to be an object or function');
    }

    if ((typeof extend === 'undefined' ? 'undefined' : _typeof(extend)) === 'object') {
      return transform(extend);
    }

    var meta = extend && extend.name;

    for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
      args[_key3 - 1] = arguments[_key3];
    }

    var result = extend.apply(undefined, args.concat([data]));

    if (result && typeof result.then === 'function') {
      return result.then(function (res) {
        return transform(res, meta);
      });
    }

    return transform(result, meta);
  }

  function subscribe(stateKey, listener) {
    if (typeof stateKey === 'function') {
      listener = stateKey;
      stateKey = '*';
    }

    if (typeof listener !== 'function') {
      throw new TypeError('Expected listener to be a function');
    }

    if (!listeners[stateKey]) {
      listeners[stateKey] = [];
    }

    listeners[stateKey].push(listener);
  }

  return data;
}
module.exports = exports['default'];