'use strict';

var _keys = require('babel-runtime/core-js/object/keys');

var _keys2 = _interopRequireDefault(_keys);

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var Filter = require('./filter');
var Router = require('./router');
var Message = require('./message');

var _require = require('../../../util/logger'),
    logger = _require.logger;

var _hubInstances = {};

var Hub = function () {
  function Hub(id) {
    (0, _classCallCheck3.default)(this, Hub);

    var self = this;
    if (_hubInstances[id]) {
      return _hubInstances[id];
    }
    _hubInstances[id] = this;
    this.id = id;
    this.terminalMap = {};
    this.filterChain = [];
    this._pushToRouter = new Filter( /*#__PURE__*/_regenerator2.default.mark(function _callee(message) {
      var responseMessage;
      return _regenerator2.default.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              _context.next = 2;
              return self.router._fetchMessage(message);

            case 2:
              responseMessage = _context.sent;

              self.response = responseMessage;
              return _context.abrupt('return', responseMessage);

            case 5:
            case 'end':
              return _context.stop();
          }
        }
      }, _callee, this);
    }));
  }

  (0, _createClass3.default)(Hub, [{
    key: 'join',
    value: function join(terminal, forced) {
      if (!this.router) {
        throw new Error('A Hub must be linked with a Router before join terminals');
      }
      if (!this.terminalMap[terminal.id]) {
        terminal.hub = this.id;
        this.terminalMap[terminal.id] = terminal;
        this._setupTerminal(terminal, forced);
      } else {
        throw new Error('can not add the same port');
      }
    }
  }, {
    key: 'setChannel',
    value: function setChannel(terminalId, channelId) {
      if (this.terminalMap[terminalId]) {
        this.terminalMap[terminalId].channelId = channelId;
      } else {
        throw new Error('can not find terminal[' + terminalId + ']');
      }
    }
  }, {
    key: '_setupTerminal',
    value: function _setupTerminal(terminal, forced) {
      var _this = this;

      terminal.on('destroy', function () {
        if (_this.terminalMap[terminal.id]) {
          delete _this.terminalMap[terminal.id];
          _this.router._event({
            type: Router.Event.TERMINAL_LEAVED,
            terminalId: terminal.id,
            hubId: _this.id,
            channelId: terminal.channelId
          });
          terminal = null;
        } else {
          logger.warn('try to delete a non-exist terminal');
        }
      });
      terminal.on('message', function (message) {
        _this.send(new Message(message, _this.id, terminal.id, terminal.channelId));
      });
      this.router._event({
        type: Router.Event.TERMINAL_JOINED,
        terminalId: terminal.id,
        hubId: this.id,
        channelId: terminal.channelId,
        forced: forced
      });
    }
  }, {
    key: 'broadcast',
    value: function broadcast(message) {
      for (var id in this.terminalMap) {
        if (this.terminalMap.hasOwnProperty(id)) {
          this.terminalMap[id].read(message.payload);
        }
      }
      message.destroy();
    }
  }, {
    key: 'pushToTerminal',
    value: function pushToTerminal(terminalId, message) {
      if (this.terminalMap[terminalId]) {
        this.terminalMap[terminalId].read(message.payload);
      } else {
        throw new Error('Terminal [' + terminalId + '] not found! @' + this.id);
      }
    }
  }, {
    key: 'send',
    value: function send(message) {
      if (!this.router) {
        throw new Error('this hub not linked with a router,message send failed!');
      }
      Filter.resolveFilterChain(message, this.filterChain.concat(this._pushToRouter)).then(function () {}).catch(function (e) {
        return logger.error(e);
      });
    }
  }, {
    key: 'filter',
    value: function filter(_filter, condition) {
      this.filterChain.push(new Filter(_filter, condition));
    }
  }], [{
    key: 'get',
    value: function get(id) {
      return _hubInstances[id] || new Hub(id);
    }
  }, {
    key: 'check',
    value: function check() {
      (0, _keys2.default)(_hubInstances).forEach(function (id) {
        if (!_hubInstances[id].router) {
          console.warn('mlink warning: Hub[' + id + '] not join in any router.make sure your id is correct');
        }
      });
    }
  }]);
  return Hub;
}();

module.exports = Hub;