text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var tslib_1 = require("tslib");
  var _ = require("lodash");
  var crypto = require("crypto");
  var builder_1 = require("../core/builder");
  var event_loop_spinner_1 = require("./event-loop-spinner");
  var objectHash = require("object-hash");
  function addLabel(dep, key, value) {
      if (!dep.labels) {
          dep.labels = {};
      }
      dep.labels[key] = value;
  }
  function depTreeToGraph(depTree, pkgManagerName) {
      return tslib_1.__awaiter(this, void 0, void 0, function () {
          var rootPkg, pkgManagerInfo, targetOS, builder, eventLoopSpinner, depGraph;
          return tslib_1.__generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      rootPkg = {
                          name: depTree.name,
                          version: depTree.version || undefined,
                      };
                      pkgManagerInfo = {
                          name: pkgManagerName,
                      };
                      targetOS = depTree.targetOS;
                      if (targetOS) {
                          pkgManagerInfo.repositories = [
                              {
                                  alias: targetOS.name + ":" + targetOS.version,
                              },
                          ];
                      }
                      builder = new builder_1.DepGraphBuilder(pkgManagerInfo, rootPkg);
                      eventLoopSpinner = new event_loop_spinner_1.EventLoopSpinner();
                      return [4 /*yield*/, buildGraph(builder, depTree, depTree.name, eventLoopSpinner, true)];
                  case 1:
                      _a.sent();
                      return [4 /*yield*/, builder.build()];
                  case 2:
                      depGraph = _a.sent();
                      return [2 /*return*/, shortenNodeIds(depGraph, eventLoopSpinner)];
              }
          });
      });
  }
  exports.depTreeToGraph = depTreeToGraph;
  function buildGraph(builder, depTree, pkgName, eventLoopSpinner, isRoot) {
      if (isRoot === void 0) { isRoot = false; }
      return tslib_1.__awaiter(this, void 0, void 0, function () {
          var getNodeId, depNodesIds, hash, deps, depNames, _i, _a, depName, dep, subtreeHash, depPkg, depNodeId, nodeInfo, treeHash, pkgNodeId, pkg, nodeInfo, _b, depNodesIds_1, depNodeId;
          return tslib_1.__generator(this, function (_c) {
              switch (_c.label) {
                  case 0:
                      getNodeId = function (name, version, hashId) {
                          return name + "@" + (version || '') + "|" + hashId;
                      };
                      depNodesIds = [];
                      hash = crypto.createHash('sha1');
                      if (depTree.versionProvenance) {
                          hash.update(objectHash(depTree.versionProvenance));
                      }
                      if (depTree.labels) {
                          hash.update(objectHash(depTree.labels));
                      }
                      deps = depTree.dependencies || {};
                      depNames = _.keys(deps).filter(function (d) { return !!deps[d]; });
                      _i = 0, _a = depNames.sort();
                      _c.label = 1;
                  case 1:
                      if (!(_i < _a.length)) return [3 /*break*/, 4];
                      depName = _a[_i];
                      dep = deps[depName];
                      return [4 /*yield*/, buildGraph(builder, dep, depName, eventLoopSpinner)];
                  case 2:
                      subtreeHash = _c.sent();
                      depPkg = {
                          name: depName,
                          version: dep.version,
                      };
                      depNodeId = getNodeId(depPkg.name, depPkg.version, subtreeHash);
                      depNodesIds.push(depNodeId);
                      nodeInfo = {};
                      if (dep.versionProvenance) {
                          nodeInfo.versionProvenance = dep.versionProvenance;
                      }
                      if (dep.labels) {
                          nodeInfo.labels = dep.labels;
                      }
                      builder.addPkgNode(depPkg, depNodeId, nodeInfo);
                      hash.update(depNodeId);
                      _c.label = 3;
                  case 3:
                      _i++;
                      return [3 /*break*/, 1];
                  case 4:
                      treeHash = hash.digest('hex');
                      if (isRoot) {
                          pkgNodeId = builder.rootNodeId;
                      }
                      else {
                          pkg = {
                              name: pkgName,
                              version: depTree.version,
                          };
                          pkgNodeId = getNodeId(pkg.name, pkg.version, treeHash);
                          nodeInfo = {};
                          if (depTree.versionProvenance) {
                              nodeInfo.versionProvenance = depTree.versionProvenance;
                          }
                          if (depTree.labels) {
                              nodeInfo.labels = depTree.labels;
                          }
                          builder.addPkgNode(pkg, pkgNodeId, nodeInfo);
                      }
                      for (_b = 0, depNodesIds_1 = depNodesIds; _b < depNodesIds_1.length; _b++) {
                          depNodeId = depNodesIds_1[_b];
                          builder.connectDep(pkgNodeId, depNodeId);
                      }
                      if (!(depNodesIds.length > 0 && eventLoopSpinner.isStarving())) return [3 /*break*/, 6];
                      return [4 /*yield*/, eventLoopSpinner.spin()];
                  case 5:
                      _c.sent();
                      _c.label = 6;
                  case 6: return [2 /*return*/, treeHash];
              }
          });
      });
  }
  function shortenNodeIds(depGraph, eventLoopSpinner) {
      return tslib_1.__awaiter(this, void 0, void 0, function () {
          var builder, nodesMap, _i, _a, pkg, nodeIds, i, nodeId, nodeInfo, newNodeId, _b, _c, pkg, _d, _e, nodeId, _f, _g, depNodeId, parentNode, childNode;
          return tslib_1.__generator(this, function (_h) {
              switch (_h.label) {
                  case 0:
                      builder = new builder_1.DepGraphBuilder(depGraph.pkgManager, depGraph.rootPkg);
                      nodesMap = {};
                      _i = 0, _a = depGraph.getPkgs();
                      _h.label = 1;
                  case 1:
                      if (!(_i < _a.length)) return [3 /*break*/, 4];
                      pkg = _a[_i];
                      nodeIds = depGraph.getPkgNodeIds(pkg);
                      for (i = 0; i < nodeIds.length; i++) {
                          nodeId = nodeIds[i];
                          if (nodeId === depGraph.rootNodeId) {
                              continue;
                          }
                          nodeInfo = depGraph.getNode(nodeId);
                          newNodeId = void 0;
                          if (nodeIds.length === 1) {
                              newNodeId = "" + trimAfterLastSep(nodeId, '|');
                          }
                          else {
                              newNodeId = trimAfterLastSep(nodeId, '|') + "|" + (i + 1);
                          }
                          nodesMap[nodeId] = newNodeId;
                          builder.addPkgNode(pkg, newNodeId, nodeInfo);
                      }
                      if (!eventLoopSpinner.isStarving()) return [3 /*break*/, 3];
                      return [4 /*yield*/, eventLoopSpinner.spin()];
                  case 2:
                      _h.sent();
                      _h.label = 3;
                  case 3:
                      _i++;
                      return [3 /*break*/, 1];
                  case 4:
                      _b = 0, _c = depGraph.getPkgs();
                      _h.label = 5;
                  case 5:
                      if (!(_b < _c.length)) return [3 /*break*/, 8];
                      pkg = _c[_b];
                      for (_d = 0, _e = depGraph.getPkgNodeIds(pkg); _d < _e.length; _d++) {
                          nodeId = _e[_d];
                          for (_f = 0, _g = depGraph.getNodeDepsNodeIds(nodeId); _f < _g.length; _f++) {
                              depNodeId = _g[_f];
                              parentNode = nodesMap[nodeId] || nodeId;
                              childNode = nodesMap[depNodeId] || depNodeId;
                              builder.connectDep(parentNode, childNode);
                          }
                      }
                      if (!eventLoopSpinner.isStarving()) return [3 /*break*/, 7];
                      return [4 /*yield*/, eventLoopSpinner.spin()];
                  case 6:
                      _h.sent();
                      _h.label = 7;
                  case 7:
                      _b++;
                      return [3 /*break*/, 5];
                  case 8: return [2 /*return*/, builder.build()];
              }
          });
      });
  }
  function graphToDepTree(depGraphInterface, pkgType, opts) {
      if (opts === void 0) { opts = { deduplicateWithinTopLevelDeps: false }; }
      return tslib_1.__awaiter(this, void 0, void 0, function () {
          var depGraph, eventLoopSpinner, depTree, targetOS;
          return tslib_1.__generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      depGraph = depGraphInterface;
                      // TODO: implement cycles support
                      if (depGraph.hasCycles()) {
                          throw new Error('Conversion to DepTree does not support cyclic graphs yet');
                      }
                      eventLoopSpinner = new event_loop_spinner_1.EventLoopSpinner();
                      return [4 /*yield*/, buildSubtree(depGraph, depGraph.rootNodeId, eventLoopSpinner, opts.deduplicateWithinTopLevelDeps ? null : false)];
                  case 1:
                      depTree = _a.sent();
                      depTree.type = depGraph.pkgManager.name;
                      depTree.packageFormatVersion = constructPackageFormatVersion(pkgType);
                      targetOS = constructTargetOS(depGraph);
                      if (targetOS) {
                          depTree.targetOS = targetOS;
                      }
                      return [2 /*return*/, depTree];
              }
          });
      });
  }
  exports.graphToDepTree = graphToDepTree;
  function constructPackageFormatVersion(pkgType) {
      if (pkgType === 'maven') {
          pkgType = 'mvn';
      }
      return pkgType + ":0.0.1";
  }
  function constructTargetOS(depGraph) {
      if (['apk', 'apt', 'deb', 'rpm'].indexOf(depGraph.pkgManager.name) === -1) {
          // .targetOS is undefined unless its a linux pkgManager
          return;
      }
      if (!depGraph.pkgManager.repositories
          || !depGraph.pkgManager.repositories.length
          || !depGraph.pkgManager.repositories[0].alias) {
          throw new Error('Incomplete .pkgManager, could not create .targetOS');
      }
      var _a = depGraph.pkgManager.repositories[0].alias.split(':'), name = _a[0], version = _a[1];
      return { name: name, version: version };
  }
  function buildSubtree(depGraph, nodeId, eventLoopSpinner, maybeDeduplicationSet) {
      if (maybeDeduplicationSet === void 0) { maybeDeduplicationSet = null; }
      return tslib_1.__awaiter(this, void 0, void 0, function () {
          var isRoot, nodePkg, nodeInfo, depTree, depInstanceIds, _i, depInstanceIds_1, depInstId, subtree;
          return tslib_1.__generator(this, function (_a) {
              switch (_a.label) {
                  case 0:
                      isRoot = nodeId === depGraph.rootNodeId;
                      nodePkg = depGraph.getNodePkg(nodeId);
                      nodeInfo = depGraph.getNode(nodeId);
                      depTree = {};
                      depTree.name = nodePkg.name;
                      depTree.version = nodePkg.version;
                      if (nodeInfo.versionProvenance) {
                          depTree.versionProvenance = nodeInfo.versionProvenance;
                      }
                      if (nodeInfo.labels) {
                          depTree.labels = tslib_1.__assign({}, nodeInfo.labels);
                      }
                      depInstanceIds = depGraph.getNodeDepsNodeIds(nodeId);
                      if (!depInstanceIds || depInstanceIds.length === 0) {
                          return [2 /*return*/, depTree];
                      }
                      if (maybeDeduplicationSet) {
                          if (maybeDeduplicationSet.has(nodeId)) {
                              if (depInstanceIds.length > 0) {
                                  addLabel(depTree, 'pruned', 'true');
                              }
                              return [2 /*return*/, depTree];
                          }
                          maybeDeduplicationSet.add(nodeId);
                      }
                      _i = 0, depInstanceIds_1 = depInstanceIds;
                      _a.label = 1;
                  case 1:
                      if (!(_i < depInstanceIds_1.length)) return [3 /*break*/, 4];
                      depInstId = depInstanceIds_1[_i];
                      // Deduplication of nodes occurs only within a scope of a top-level dependency.
                      // Therefore, every top-level dep gets an independent set to track duplicates.
                      if (isRoot && maybeDeduplicationSet !== false) {
                          maybeDeduplicationSet = new Set();
                      }
                      return [4 /*yield*/, buildSubtree(depGraph, depInstId, eventLoopSpinner, maybeDeduplicationSet)];
                  case 2:
                      subtree = _a.sent();
                      if (!subtree) {
                          return [3 /*break*/, 3];
                      }
                      if (!depTree.dependencies) {
                          depTree.dependencies = {};
                      }
                      depTree.dependencies[subtree.name] = subtree;
                      _a.label = 3;
                  case 3:
                      _i++;
                      return [3 /*break*/, 1];
                  case 4:
                      if (!eventLoopSpinner.isStarving()) return [3 /*break*/, 6];
                      return [4 /*yield*/, eventLoopSpinner.spin()];
                  case 5:
                      _a.sent();
                      _a.label = 6;
                  case 6: return [2 /*return*/, depTree];
              }
          });
      });
  }
  function trimAfterLastSep(str, sep) {
      return str.slice(0, str.lastIndexOf(sep));
  }
  //# sourceMappingURL=index.js.map