
// remove a package.  if it has dependents, then fail, and demand that they be
// uninstalled first.  If activee, then fail, and depand that it be deactivated
// first.

module.exports = uninstall;

var rm = require("./utils/rm-rf"),
  fs = require("fs"),
  log = require("./utils/log"),
  readJson = require("./utils/read-json"),
  path = require("path"),
  npm = require("../npm"),
  chain = require("./utils/chain"),
  lifecycle = require("./utils/lifecycle"),
  semver = require("./utils/semver");

function uninstall (name, version, cb) {
  var pkgdir = path.join(npm.dir, name, version),
    jsonFile = path.join(pkgdir, "package", "package.json"),
    active = path.join(npm.dir, name, "active"),
    libdir = path.join(npm.root, name+"-"+version),
    mainjs = libdir + ".js",
    dependents = path.join(pkgdir, "dependents");
  
  chain(
    // if active, then fail.
    [log, "about to remove: " + pkgdir, "uninstall"],
    function (cb) {
      fs.readlink(active, function (er, active) {
      if (path.basename(active||"") === version) return cb(new Error(
        "cannot remove active package.\n"+
        "      npm deactivate "+name+" "+version+"\n"+
        "and then retry."));
      return cb();
    })},
    [log, "not active", "uninstall"],
    // if has dependents, then fail
    function (cb) {
      fs.readdir(dependents, function (er, children) {
      if (children && children.length) return cb(new Error(
        name+"-"+version+" depended upon by \n"+
        "      " + require("sys").inspect(children)+"\n"+
        "remove them first."));
      return cb();
    })},
    // remove the whole thing.
    function (cb) { readJson(jsonFile, function (er, data) {
      if (er) return cb(er);
      chain(
        [lifecycle, data, "preuninstall"],
        [lifecycle, data, "uninstall"],
        // clean out any dependent links, since this is going away.
        [cleanupDependent, pkgdir, data],
        [rm, pkgdir],
        [removeBins, data],
        [rm, mainjs],
        [rm, libdir],
        // if that was the last one, then remove the whole thing.
        function (cb) { pkgdir = path.dirname(pkgdir); cb() },
        function (cb) { fs.readdir(pkgdir, function (versions) {
          if (versions && versions.length) return cb();
          rm(pkgdir, cb);
        })},
        [lifecycle, data, "postuninstall"],
        cb
      );
    })},
    cb
  );
}

function removeBins (data, cb) {
  if (!data.bin) return cb();
  var binroot = path.join(process.installPrefix, "bin");
  (function R (bins) {
    if (!bins.length) return cb();
    rm(binroot + "/" + bins.pop(), cb);
  })(Object.getOwnProperties(data.bin));    
}

function cleanupDependent (pkgdir, data, cb) {
  var dependencies = path.join(pkgdir, "dependencies");
  fs.readdir(dependencies, function (er, deps) {
    if (er) return cb(er);
    (function R () {
      var dep = deps.pop();
      if (!dep) return cb();
      dep = path.join(dependencies, dep);
      fs.readlink(dep, function (er, realdep) {
        if (er) return cb(er);
        var p = semver.parsePackage(path.basename(realdep, ".js")),
          depPath = path.join(
            npm.dir, p[1], p[2], "dependents", data.name+"-"+data.version);
        log(depPath, "removeDependent");
        rm(depPath, R);
      });
    })();
  });
}

@@ -1889,32 +1889,225 @@
        %5B
-cleanupDependent
+removeDependent, pkgdir, data%5D,%0A        // clean out anything that's been linked in.  This is an ugly kludgey%0A        // awful artifact that is caused by linking stuff into the pkgdir%0A        %5BremoveLinkedDeps
 , pkgdir
@@ -2865,15 +2865,14 @@
 ion 
-cleanup
+remove
 Depe
@@ -3495,10 +3495,429 @@
 ;%0A  %7D);%0A
-
 %7D%0A
+%0Afunction removeLinkedDeps (pkgdir, data, cb) %7B%0A  if (!data.link) return cb();%0A  var depLinks = %5B%5D;%0A  for (var i in data.link) %7B%0A    depLinks.push(path.join(pkgdir, %22package%22, data.link%5Bi%5D));%0A  %7D%0A  (function R () %7B%0A    var dep = depLinks.pop();%0A    if (!dep) return cb();%0A    chain(%0A      %5Brm, dep%5D,%0A      %5Brm, dep+%22.js%22%5D,%0A      function (er, ok) %7B%0A        if (er) return cb(er);%0A        R();%0A      %7D%0A    );%0A  %7D)();%0A%7D%0A%0A

Clean up the things that are linked into the (otherwise pristine) package dir. This is not relevant for regular installs, but for link installs, it would be litter left behind.