text: var assert = require("assert")
    , path = require("path")
    , mkdir = require("mkdirp")
    , chownr = require("chownr")
    , pathIsInside = require("path-is-inside")
    , readJson = require("read-package-json")
    , log = require("npmlog")
    , npm = require("../npm.js")
    , tar = require("../utils/tar.js")
    , deprCheck = require("../utils/depr-check.js")
    , getCacheStat = require("./get-stat.js")
    , cachedPackageRoot = require("./cached-package-root.js")
    , addLocalTarball = require("./add-local-tarball.js")
    , sha = require("sha")
    , inflight = require("inflight")
  
  module.exports = addLocal
  
  function addLocal (p, pkgData, cb_) {
    assert(typeof p === "object", "must have spec info")
    assert(typeof cb === "function", "must have callback")
  
    pkgData = pkgData || {}
  
    function cb (er, data) {
      if (er) {
        log.error("addLocal", "Could not install %s", p.spec)
        return cb_(er)
      }
      if (data && !data._fromGithub) {
        data._from = path.relative(npm.prefix, p.spec) || "."
        var resolved = path.relative(npm.prefix, p.spec)
        if (resolved) data._resolved = "file:"+resolved
      }
      return cb_(er, data)
    }
  
    if (p.type === "directory") {
      addLocalDirectory(p.spec, pkgData, null, cb)
    }
    else {
      addLocalTarball(p.spec, pkgData, null, cb)
    }
  }
  
  // At this point, if shasum is set, it's something that we've already
  // read and checked.  Just stashing it in the data at this point.
  function addLocalDirectory (p, pkgData, shasum, cb) {
    assert(pkgData, "must pass package data")
    assert(typeof cb === "function", "must have callback")
  
    // if it's a folder, then read the package.json,
    // tar it to the proper place, and add the cache tar
    if (pathIsInside(p, npm.cache)) return cb(new Error(
      "Adding a cache directory to the cache will make the world implode."))
  
    readJson(path.join(p, "package.json"), false, function (er, data) {
      if (er) return cb(er)
  
      if (!data.name) {
        return cb(new Error("No name provided in package.json"))
      }
      else if (pkgData.name && pkgData.name !== data.name) {
        return cb(new Error(
          "Invalid package: expected " + pkgData.name + " but found " + data.name
        ))
      }
  
      if (!data.version) {
        return cb(new Error("No version provided in package.json"))
      }
      else if (pkgData.version && pkgData.version !== data.version) {
        return cb(new Error(
          "Invalid package: expected " + pkgData.name + "@" + pkgData.version +
            " but found " + data.name + "@" + data.version
        ))
      }
  
      deprCheck(data)
  
      // pack to {cache}/name/ver/package.tgz
      var root = cachedPackageRoot(data)
      var tgz = path.resolve(root, "package.tgz")
      var pj = path.resolve(root, "package/package.json")
  
      var wrapped = inflight(tgz, next)
      if (!wrapped) return log.verbose("addLocalDirectory", tgz, "already in flight; waiting")
      log.verbose("addLocalDirectory", tgz, "not in flight; packing")
  
      getCacheStat(function (er, cs) {
        mkdir(path.dirname(pj), function (er, made) {
          if (er) return cb(er)
          var fancy = !pathIsInside(p, npm.tmp)
          tar.pack(tgz, p, data, fancy, function (er) {
            if (er) {
              log.error("addLocalDirectory", "Could not pack", p, "to", tgz)
              return cb(er)
            }
  
            if (!cs || isNaN(cs.uid) || isNaN(cs.gid)) wrapped()
  
            chownr(made || tgz, cs.uid, cs.gid, wrapped)
          })
        })
      })
  
      function next (er) {
        if (er) return cb(er)
        // if we have the shasum already, just add it
        if (shasum) {
          return addLocalTarball(tgz, data, shasum, cb)
        } else {
          sha.get(tgz, function (er, shasum) {
            if (er) {
              return cb(er)
            }
            data._shasum = shasum
            return addLocalTarball(tgz, data, shasum, cb)
          })
        }
      }
    })
  }
  