'use strict';

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

var _regenerator;

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

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

function _load_graphlib() {
  return _graphlib = _interopRequireDefault(require('graphlib'));
}

var _semver;

function _load_semver() {
  return _semver = _interopRequireDefault(require('semver'));
}

var _ramda;

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

var _objects;

function _load_objects() {
  return _objects = require('../objects');
}

var _bitId;

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

var _models;

function _load_models() {
  return _models = require('../models');
}

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

var Graph = (_graphlib || _load_graphlib()).default.Graph;

var DependencyGraph = function () {
  function DependencyGraph(repository, graph) {
    _classCallCheck(this, DependencyGraph);

    this.repository = repository;
    this.graph = graph;
  }

  _createClass(DependencyGraph, [{
    key: 'getComponent',
    value: function getComponent(id) {
      return this.graph.node(id.toStringWithoutVersion());
    }
  }, {
    key: 'getComponentVersion',
    value: function getComponentVersion(id) {
      if (id.version === (_constants || _load_constants()).LATEST_BIT_VERSION) {
        var component = this.getComponent();
        var versions = Object.keys(component.versions);
        var latestVersion = (_semver || _load_semver()).default.maxSatisfying(versions, '*');
        return this.graph.node(`${id.toStringWithoutVersion()}${(_constants || _load_constants()).VERSION_DELIMITER}${latestVersion}`);
      }
      return this.graph.node(id.toString());
    }
  }, {
    key: 'getDependentsPerId',
    value: function getDependentsPerId(id) {
      var nodeEdges = this.graph.inEdges(id.toString());
      if (!nodeEdges) return [];
      return nodeEdges.map(function (node) {
        return node.v;
      });
    }
  }, {
    key: 'getDependenciesPerId',
    value: function getDependenciesPerId(id) {
      var nodeEdges = this.graph.outEdges(id.toString());
      if (!nodeEdges) return [];
      return nodeEdges.map(function (node) {
        return node.v;
      });
    }
  }, {
    key: 'getComponentVersions',
    value: function getComponentVersions(id) {
      var _this = this;

      var component = this.getComponent(id);
      if (!component) return;
      return Object.keys(component.versions).map(function (version) {
        return _this.getComponentVersion(`${id.toStringWithoutVersion()}${(_constants || _load_constants()).VERSION_DELIMITER}${version}`);
      }).filter(function (x) {
        return x;
      });
    }

    /**
     * findDependentBits
     * foreach component in array find the componnet that uses that component
     * dont return local components
     */

  }, {
    key: 'findDependentBitsByGraph',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(bitIds) {
        var _this2 = this;

        var dependentIds;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                dependentIds = {};

                bitIds.forEach(function (id) {
                  if (id.version === (_constants || _load_constants()).LATEST_BIT_VERSION) {
                    // check if another component in the scope is using any of the versions
                    var children = _this2.graph.children(id.toString());
                    children.forEach(function (child) {
                      var requiredBy = _this2.graph.predecessors(child);
                      if (requiredBy && !(_ramda || _load_ramda()).default.isEmpty(requiredBy)) {
                        if (dependentIds[id.toStringWithoutVersion()]) {
                          dependentIds[id.toStringWithoutVersion()] = dependentIds[id.toStringWithoutVersion()].concat(requiredBy);
                        } else dependentIds[id.toStringWithoutVersion()] = requiredBy;
                      }
                    });
                  } else {
                    var requiredBy = _this2.graph.predecessors(id.toString());
                    if (!(_ramda || _load_ramda()).default.isEmpty(requiredBy)) {
                      if (dependentIds[id.toString()]) dependentIds[id.toString()] = dependentIds[id.toString()].concat(requiredBy);else dependentIds[id.toString()] = requiredBy;
                    }
                  }
                });
                return _context.abrupt('return', dependentIds);

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

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

      return findDependentBitsByGraph;
    }()
  }], [{
    key: 'load',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(repository) {
        var graph;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.next = 2;
                return DependencyGraph.buildDependenciesGraph(repository);

              case 2:
                graph = _context2.sent;
                return _context2.abrupt('return', new DependencyGraph(repository, graph));

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

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

      return load;
    }()
  }, {
    key: 'buildDependenciesGraph',
    value: function () {
      var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5(repository) {
        var _this3 = this;

        var graph, depObj, allComponents;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                graph = new Graph({ compound: true });
                depObj = {};
                _context5.next = 4;
                return repository.listComponents(false);

              case 4:
                allComponents = _context5.sent;
                _context5.next = 7;
                return Promise.all(allComponents.map(function () {
                  var _ref4 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(component) {
                    return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
                      while (1) {
                        switch (_context4.prev = _context4.next) {
                          case 0:
                            graph.setNode(component.id(), component);
                            _context4.next = 3;
                            return Promise.all(Object.keys(component.versions).map(function () {
                              var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(version) {
                                var componentVersion, idWithVersion;
                                return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
                                  while (1) {
                                    switch (_context3.prev = _context3.next) {
                                      case 0:
                                        _context3.next = 2;
                                        return component.loadVersion(version, repository);

                                      case 2:
                                        componentVersion = _context3.sent;

                                        if (componentVersion) {
                                          _context3.next = 5;
                                          break;
                                        }

                                        return _context3.abrupt('return');

                                      case 5:
                                        idWithVersion = `${component.id()}${(_constants || _load_constants()).VERSION_DELIMITER}${version}`;

                                        graph.setNode(idWithVersion, componentVersion);
                                        graph.setParent(idWithVersion, component.id());
                                        componentVersion.id = component.toBitId();
                                        depObj[idWithVersion] = componentVersion;

                                      case 10:
                                      case 'end':
                                        return _context3.stop();
                                    }
                                  }
                                }, _callee3, _this3);
                              }));

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

                          case 3:
                          case 'end':
                            return _context4.stop();
                        }
                      }
                    }, _callee4, _this3);
                  }));

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

              case 7:
                // set all edges
                // @todo: currently the label is "require". Change it to be "direct" and "indirect" depends on whether it comes from
                // flattenedDependencies or from dependencies.
                Object.keys(depObj).forEach(function (id) {
                  return depObj[id].flattenedDependencies.forEach(function (dep) {
                    return graph.setEdge(id, dep.toString(), 'require');
                  });
                });
                return _context5.abrupt('return', Promise.resolve(graph));

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

      function buildDependenciesGraph(_x3) {
        return _ref3.apply(this, arguments);
      }

      return buildDependenciesGraph;
    }()
  }]);

  return DependencyGraph;
}();

exports.default = DependencyGraph;