'use strict';

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

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

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 _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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _ramda;

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _baseExtension;

function _load_baseExtension() {
  return _baseExtension = _interopRequireDefault(require('./base-extension'));
}

var _logger;

function _load_logger() {
  return _logger = _interopRequireDefault(require('../logger/logger'));
}

var _extensionCommand;

function _load_extensionCommand() {
  return _extensionCommand = _interopRequireDefault(require('./extension-command'));
}

var _environment;

function _load_environment() {
  return _environment = _interopRequireDefault(require('../environment'));
}

var _scope;

function _load_scope() {
  return _scope = require('../scope');
}

var _consumer;

function _load_consumer() {
  return _consumer = require('../consumer');
}

var _loader;

function _load_loader() {
  return _loader = _interopRequireDefault(require('../cli/loader'));
}

var _hooks;

function _load_hooks() {
  return _hooks = _interopRequireDefault(require('../hooks'));
}

var _hooks2;

function _load_hooks2() {
  return _hooks2 = require('../hooks');
}

var _constants;

function _load_constants() {
  return _constants = require('../constants');
}

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

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

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 HooksManagerInstance = (_hooks || _load_hooks()).default.getInstance();

/**
 * A class which represent an extension
 * The different attributes,
 * Extension API,
 * Load extension
 * Config
 */
var Extension = function (_BaseExtension) {
  _inherits(Extension, _BaseExtension);

  function Extension(extensionProps) {
    _classCallCheck(this, Extension);

    var _this = _possibleConstructorReturn(this, (Extension.__proto__ || Object.getPrototypeOf(Extension)).call(this, extensionProps));

    _this.api = _extends({
      /**
       * API to resiter new command to bit
       */
      registerCommand: function registerCommand(newCommand) {
        // TODO: validate new command format
        (_logger || _load_logger()).default.info(`registering new command ${newCommand.name}`);
        // $FlowFixMe
        _this.commands.push(new (_extensionCommand || _load_extensionCommand()).default(newCommand));
      },
      /**
       * API to register action to an existing hook (hook name might be a hook defined by another extension)
       */
      registerActionToHook: function registerActionToHook(hookName, hookAction) {
        (_logger || _load_logger()).default.info(`registering ${hookAction.name} to hook ${hookName}`);
        _this.registeredHooksActions[hookName] = hookAction;
      },
      /**
       * API to register a new hook name, usuful for communicate between different extensions
       */
      registerNewHook: function registerNewHook(hookName) {
        (_logger || _load_logger()).default.info(`registering new global hook ${hookName}`);
        _this.newHooks.push(hookName);
        // Register the new hook in the global hooks manager
        HooksManagerInstance.registerNewHook(hookName, { extension: _this.name });
      },
      /**
       * API to trigger a hook registered by this extension.
       * trigger hook are available only for hooks registered by you.
       */
      triggerHook: function triggerHook(hookName, args) {
        if (!(_ramda || _load_ramda()).default.contains(hookName, _this.newHooks)) {
          (_logger || _load_logger()).default.debug(`trying to trigger the hook ${hookName} which not registered by this extension`);
          return;
        }
        HooksManagerInstance.triggerHook(hookName, args);
      },
      getLoader: function getLoader() {
        return (_loader || _load_loader()).default;
      },
      HOOKS_NAMES: _getHooksNames(),
      createIsolatedEnv: _createIsolatedEnv
    }, _get(Extension.prototype.__proto__ || Object.getPrototypeOf(Extension.prototype), 'api', _this));

    _this.extendAPI(extensionProps.api, _this.api);
    _this.commands = extensionProps.commands || [];
    _this.registeredHooksActions = extensionProps.registeredHooksActions || {};
    _this.newHooks = extensionProps.newHooks || [];
    return _this;
  }

  /**
   * Load extension by name
   * The extension will be from scope by default or from file
   * if there is file(path) in the options
   * The file path is relative to the bit.json of the project or absolute
   * @param {string} props - loading properties with the following fields:
   * {string} name - name of the extension
   * {Object} rawConfig - raw config for the extension
   * {Object} options - extension options such as - disabled, file, core
   * {string} consumerPath - path to the consumer folder (to load the file relatively)
   * {string} scopePath - scope which stores the extension code
   */
  // $FlowFixMe


  _createClass(Extension, [{
    key: 'registerHookActionsOnHooksManager',


    /**
     * Register the hooks on the global hooks manager
     * We don't do this directly on the api in order to be able to register to hooks defined by another extensions
     * So we want to make sure to first load and register all new hooks from all extensions and only then register the actions
     */
    value: function registerHookActionsOnHooksManager() {
      var _this2 = this;

      var registerAction = function registerAction(hookAction, hookName) {
        HooksManagerInstance.registerActionToHook(hookName, hookAction, { extension: _this2.name });
      };
      (_ramda || _load_ramda()).default.forEachObjIndexed(registerAction, this.registeredHooksActions);
    }
  }], [{
    key: 'load',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(props) {
        var baseExtensionProps, extensionProps, extension;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                props.rawConfig = props.rawConfig || {};
                props.options = props.options || {};
                _context.next = 4;
                return _get(Extension.__proto__ || Object.getPrototypeOf(Extension), 'load', this).call(this, props);

              case 4:
                baseExtensionProps = _context.sent;

                // const extensionProps: ExtensionProps = ((await super.load(props): BaseExtensionProps): ExtensionProps);
                extensionProps = _extends({
                  commands: [],
                  registeredHooksActions: {},
                  newHooks: []
                }, baseExtensionProps);
                extension = new Extension(extensionProps);

                if (!extension.loaded) {
                  _context.next = 10;
                  break;
                }

                _context.next = 10;
                return extension.init();

              case 10:
                return _context.abrupt('return', extension);

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

      function load(_x) {
        return _ref.apply(this, arguments);
      }

      return load;
    }()
  }]);

  return Extension;
}((_baseExtension || _load_baseExtension()).default);

exports.default = Extension;


var _createIsolatedEnv = function () {
  var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(scopePath, dirPath) {
    var scope, isolatedEnvironment;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            _context2.next = 2;
            return _loadScope(scopePath);

          case 2:
            scope = _context2.sent;
            isolatedEnvironment = new (_environment || _load_environment()).default(scope, dirPath);
            _context2.next = 6;
            return isolatedEnvironment.create();

          case 6:
            return _context2.abrupt('return', isolatedEnvironment);

          case 7:
          case 'end':
            return _context2.stop();
        }
      }
    }, _callee2, undefined);
  }));

  return function _createIsolatedEnv(_x2, _x3) {
    return _ref2.apply(this, arguments);
  };
}();

var _loadScope = function () {
  var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(scopePath) {
    var consumer;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            if (!scopePath) {
              _context3.next = 2;
              break;
            }

            return _context3.abrupt('return', (0, (_scope || _load_scope()).loadScope)(scopePath));

          case 2:
            _context3.next = 4;
            return (0, (_consumer || _load_consumer()).loadConsumer)();

          case 4:
            consumer = _context3.sent;
            return _context3.abrupt('return', consumer.scope);

          case 6:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, undefined);
  }));

  return function _loadScope(_x4) {
    return _ref3.apply(this, arguments);
  };
}();

var _getHooksNames = function _getHooksNames() {
  var hooks = {};
  (_constants || _load_constants()).HOOKS_NAMES.forEach(function (hook) {
    hooks[hook] = hook;
  });
  return hooks;
};