'use strict';

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

var _regenerator;

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

var _bluebird;

function _load_bluebird() {
  return _bluebird = require('bluebird');
}

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

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

var _capsule;

function _load_capsule() {
  return _capsule = _interopRequireDefault(require('../../components/core/capsule'));
}

var _capsuleFactory;

function _load_capsuleFactory() {
  return _capsuleFactory = _interopRequireDefault(require('./capsule-factory'));
}

var _consumer;

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

var _scope;

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

var _bitId;

function _load_bitId() {
  return _bitId = require('../bit-id');
}

var _manyComponentsWriter;

function _load_manyComponentsWriter() {
  return _manyComponentsWriter = _interopRequireDefault(require('../consumer/component-ops/many-components-writer'));
}

var _logger;

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

var _loadFlattenedDependencies;

function _load_loadFlattenedDependencies() {
  return _loadFlattenedDependencies = _interopRequireDefault(require('../consumer/component-ops/load-flattened-dependencies'));
}

var _installPackages;

function _load_installPackages() {
  return _installPackages = require('../npm-client/install-packages');
}

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

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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Isolator = function () {
  function Isolator(capsule, scope, consumer) {
    _classCallCheck(this, Isolator);

    this.capsule = capsule;
    this.scope = scope;
    this.consumer = consumer;
  }

  _createClass(Isolator, [{
    key: 'isolate',
    value: function () {
      var _ref = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(componentId, opts) {
        var componentWithDependencies, writeToPath, concreteOpts, manyComponentsWriter, allRootDirs, links;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return this._loadComponent(componentId);

              case 2:
                componentWithDependencies = _context.sent;
                writeToPath = opts.writeToPath;
                concreteOpts = {
                  // consumer: this.consumer,
                  componentsWithDependencies: [componentWithDependencies],
                  writeToPath,
                  override: opts.override,
                  writePackageJson: !opts.noPackageJson,
                  writeConfig: opts.conf,
                  writeBitDependencies: opts.writeBitDependencies,
                  createNpmLinkFiles: opts.createNpmLinkFiles,
                  saveDependenciesAsComponents: opts.saveDependenciesAsComponents !== false,
                  writeDists: opts.dist,
                  installNpmPackages: !!opts.installPackages, // convert to boolean
                  installPeerDependencies: !!opts.installPackages, // convert to boolean
                  addToRootPackageJson: false,
                  verbose: opts.verbose,
                  excludeRegistryPrefix: !!opts.excludeRegistryPrefix,
                  silentPackageManagerResult: opts.silentPackageManagerResult,
                  isolated: true,
                  capsule: this.capsule
                };
                manyComponentsWriter = new (_manyComponentsWriter || _load_manyComponentsWriter()).default(concreteOpts);

                (_logger || _load_logger()).default.debug('ManyComponentsWriter, writeAllToIsolatedCapsule');
                _context.next = 9;
                return manyComponentsWriter._populateComponentsFilesToWrite();

              case 9:
                _context.next = 11;
                return manyComponentsWriter._populateComponentsDependenciesToWrite();

              case 11:
                _context.next = 13;
                return this._persistComponentsDataToCapsule([componentWithDependencies]);

              case 13:
                (_logger || _load_logger()).default.debug('ManyComponentsWriter, install packages on capsule');
                allRootDirs = (0, (_installPackages || _load_installPackages()).getAllRootDirectoriesFor)([componentWithDependencies]);
                _context.next = 17;
                return this.installPackagesOnDirs(allRootDirs);

              case 17:
                _context.next = 19;
                return manyComponentsWriter._getAllLinks();

              case 19:
                links = _context.sent;
                _context.next = 22;
                return links.persistAllToCapsule(this.capsule);

              case 22:
                return _context.abrupt('return', componentWithDependencies);

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

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

      return isolate;
    }()

    /**
     * To write a component into an isolated environment, we need not only its dependencies, but
     * also the dependencies of its dependencies and so on.
     * When loading a component from the model, it's easy to get them all from the
     * flattenedDependencies. However, when loading from the consumer, we have only the dependencies
     * loaded, not the flattened. To get the flattened, we have to load the dependencies and each one
     * of the dependency we need to load its dependencies as well until we got them all.
     */

  }, {
    key: '_loadComponent',
    value: function () {
      var _ref2 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(id) {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                if (!this.consumer) {
                  _context2.next = 2;
                  break;
                }

                return _context2.abrupt('return', this._loadComponentFromConsumer(id));

              case 2:
                throw new Error('loading components from scope is not implemented yet');

              case 3:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

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

      return _loadComponent;
    }()
  }, {
    key: '_loadComponentFromConsumer',
    value: function () {
      var _ref3 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(id) {
        var consumer, component;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                consumer = this.consumer;

                if (consumer) {
                  _context3.next = 3;
                  break;
                }

                throw new Error('missing consumer');

              case 3:
                _context3.next = 5;
                return consumer.loadComponent(id);

              case 5:
                component = _context3.sent;
                return _context3.abrupt('return', (0, (_loadFlattenedDependencies || _load_loadFlattenedDependencies()).default)(consumer, component));

              case 7:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

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

      return _loadComponentFromConsumer;
    }()
  }, {
    key: '_persistComponentsDataToCapsule',
    value: function () {
      var _ref4 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(componentsWithDependencies) {
        var _this = this;

        var persistP;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                persistP = componentsWithDependencies.map(function (componentWithDeps) {
                  var allComponents = [componentWithDeps.component].concat(_toConsumableArray(componentWithDeps.allDependencies));
                  return allComponents.map(function (component) {
                    return component.dataToPersist ? component.dataToPersist.persistAllToCapsule(_this.capsule) : Promise.resolve();
                  });
                });
                return _context4.abrupt('return', Promise.all((_ramda || _load_ramda()).default.flatten(persistP)));

              case 2:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function _persistComponentsDataToCapsule(_x5) {
        return _ref4.apply(this, arguments);
      }

      return _persistComponentsDataToCapsule;
    }()
  }, {
    key: 'installPackagesOnDirs',
    value: function () {
      var _ref5 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5(dirs) {
        var _this2 = this;

        return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                return _context5.abrupt('return', Promise.all(dirs.map(function (dir) {
                  return _this2._installPackagesOnOneDirectory(dir);
                })));

              case 1:
              case 'end':
                return _context5.stop();
            }
          }
        }, _callee5, this);
      }));

      function installPackagesOnDirs(_x6) {
        return _ref5.apply(this, arguments);
      }

      return installPackagesOnDirs;
    }()
  }, {
    key: '_installPackagesOnOneDirectory',
    value: function () {
      var _ref6 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee6(directory) {
        var execResults, output;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                _context6.next = 2;
                return this.capsule.exec('npm version 1.0.0', { cwd: directory });

              case 2:
                _context6.next = 4;
                return this.capsule.exec('npm install', { cwd: directory });

              case 4:
                execResults = _context6.sent;
                output = '';
                return _context6.abrupt('return', new Promise(function (resolve, reject) {
                  execResults.stdout.on('data', function (data) {
                    output += data;
                  });
                  execResults.stdout.on('error', function (error) {
                    return reject(error);
                  });
                  // @ts-ignore
                  execResults.on('close', function () {
                    return resolve(output);
                  });
                }));

              case 7:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, this);
      }));

      function _installPackagesOnOneDirectory(_x7) {
        return _ref6.apply(this, arguments);
      }

      return _installPackagesOnOneDirectory;
    }()
  }], [{
    key: 'getInstance',
    value: function () {
      var _ref7 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee7() {
        var containerType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'fs';
        var scope = arguments[1];
        var consumer = arguments[2];
        var dir = arguments[3];
        var capsule;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee7$(_context7) {
          while (1) {
            switch (_context7.prev = _context7.next) {
              case 0:
                (_logger || _load_logger()).default.debug(`Isolator.getInstance, creating a capsule with an ${containerType} container`);
                _context7.next = 3;
                return (0, (_capsuleFactory || _load_capsuleFactory()).default)(containerType, dir);

              case 3:
                capsule = _context7.sent;
                return _context7.abrupt('return', new Isolator(capsule, scope, consumer));

              case 5:
              case 'end':
                return _context7.stop();
            }
          }
        }, _callee7, this);
      }));

      function getInstance() {
        return _ref7.apply(this, arguments);
      }

      return getInstance;
    }()
  }]);

  return Isolator;
}();

exports.default = Isolator;