// defaults, types, and shorthands.


var path = require("path")
  , url = require("url")
  , Stream = require("stream").Stream
  , semver = require("semver")
  , stableFamily = semver.parse(process.version)
  , nopt = require("nopt")
  , os = require("os")
  , osenv = require("osenv")
  , umask = require("../utils/umask")

var log
try {
  log = require("npmlog")
} catch (er) {
  var util = require("util")
  log = { warn: function (m) {
    console.warn(m + " " + util.format.apply(util, [].slice.call(arguments, 1)))
  } }
}

exports.Umask = Umask
function Umask () {}
function validateUmask (data, k, val) {
  return umask.validate(data, k, val)
}

function validateSemver (data, k, val) {
  if (!semver.valid(val)) return false
  data[k] = semver.valid(val)
}

function validateStream (data, k, val) {
  if (!(val instanceof Stream)) return false
  data[k] = val
}

nopt.typeDefs.semver = { type: semver, validate: validateSemver }
nopt.typeDefs.Stream = { type: Stream, validate: validateStream }
nopt.typeDefs.Umask = { type: Umask, validate: validateUmask }

nopt.invalidHandler = function (k, val, type) {
  log.warn("invalid config", k + "=" + JSON.stringify(val))

  if (Array.isArray(type)) {
    if (type.indexOf(url) !== -1) type = url
    else if (type.indexOf(path) !== -1) type = path
  }

  switch (type) {
    case Umask:
      log.warn("invalid config", "Must be umask, octal number in range 0000..0777")
      break
    case url:
      log.warn("invalid config", "Must be a full url with 'http://'")
      break
    case path:
      log.warn("invalid config", "Must be a valid filesystem path")
      break
    case Number:
      log.warn("invalid config", "Must be a numeric value")
      break
    case Stream:
      log.warn("invalid config", "Must be an instance of the Stream class")
      break
  }
}

if (!stableFamily || (+stableFamily.minor % 2)) stableFamily = null
else stableFamily = stableFamily.major + "." + stableFamily.minor

var defaults

var temp = osenv.tmpdir()
var home = osenv.home()

var uidOrPid = process.getuid ? process.getuid() : process.pid

if (home) process.env.HOME = home
else home = path.resolve(temp, "npm-" + uidOrPid)

var cacheExtra = process.platform === "win32" ? "npm-cache" : ".npm"
var cacheRoot = process.platform === "win32" && process.env.APPDATA || home
var cache = path.resolve(cacheRoot, cacheExtra)


var globalPrefix
Object.defineProperty(exports, "defaults", {get: function () {
  if (defaults) return defaults

  if (process.env.PREFIX) {
    globalPrefix = process.env.PREFIX
  } else if (process.platform === "win32") {
    // c:\node\node.exe --> prefix=c:\node\
    globalPrefix = path.dirname(process.execPath)
  } else {
    // /usr/local/bin/node --> prefix=/usr/local
    globalPrefix = path.dirname(path.dirname(process.execPath))

    // destdir only is respected on Unix
    if (process.env.DESTDIR) {
      globalPrefix = path.join(process.env.DESTDIR, globalPrefix)
    }
  }

  defaults = {
    access : null
    , "always-auth" : false

    , "bin-links" : true
    , browser : null

    , ca: null
    , cafile: null

    , cache : cache

    , "cache-lock-stale": 60000
    , "cache-lock-retries": 10
    , "cache-lock-wait": 10000

    , "cache-max": Infinity
    , "cache-min": 10

    , cert: null

    , color : true
    , depth: Infinity
    , description : true
    , dev : false
    , editor : osenv.editor()
    , "engine-strict": false
    , force : false

    , "fetch-retries": 2
    , "fetch-retry-factor": 10
    , "fetch-retry-mintimeout": 10000
    , "fetch-retry-maxtimeout": 60000

    , git: "git"
    , "git-tag-version": true

    , global : false
    , globalconfig : path.resolve(globalPrefix, "etc", "npmrc")
    , group : process.platform === "win32" ? 0
            : process.env.SUDO_GID || (process.getgid && process.getgid())
    , heading: "npm"
    , "if-present": false
    , "ignore-scripts": false
    , "init-module": path.resolve(home, ".npm-init.js")
    , "init-author-name" : ""
    , "init-author-email" : ""
    , "init-author-url" : ""
    , "init-version": "1.0.0"
    , "init-license": "ISC"
    , json: false
    , key: null
    , link: false
    , "local-address" : undefined
    , loglevel : "warn"
    , logstream : process.stderr
    , long : false
    , maxsockets : 50
    , message : "%s"
    , "node-version" : process.version
    , npat : false
    , "onload-script" : false
    , optional: true
    , parseable : false
    , prefix : globalPrefix
    , production: process.env.NODE_ENV === "production"
    , "proprietary-attribs": true
    , proxy :  null
    , "https-proxy" : null
    , "user-agent" : "npm/{npm-version} "
                     + "node/{node-version} "
                     + "{platform} "
                     + "{arch}"
    , "rebuild-bundle" : true
    , registry : "https://registry.npmjs.org/"
    , rollback : true
    , save : false
    , "save-bundle": false
    , "save-dev" : false
    , "save-exact" : false
    , "save-optional" : false
    , "save-prefix": "^"
    , scope : ""
    , searchopts: ""
    , searchexclude: null
    , searchsort: "name"
    , shell : osenv.shell()
    , shrinkwrap: true
    , "sign-git-tag": false
    , spin: true
    , "strict-ssl": true
    , tag : "latest"
    , "tag-version-prefix" : "v"
    , tmp : temp
    , unicode : true
    , "unsafe-perm" : process.platform === "win32"
                    || process.platform === "cygwin"
                    || !( process.getuid && process.setuid
                       && process.getgid && process.setgid )
                    || process.getuid() !== 0
    , usage : false
    , user : process.platform === "win32" ? 0 : "nobody"
    , userconfig : path.resolve(home, ".npmrc")
    , umask: process.umask ? process.umask() : umask.fromString("022")
    , version : false
    , versions : false
    , viewer: process.platform === "win32" ? "browser" : "man"

    , _exit : true
  }

  return defaults
}})

exports.types =
  { access : [null, "restricted", "public"]
  , "always-auth" : Boolean
  , "bin-links": Boolean
  , browser : [null, String]
  , ca: [null, String, Array]
  , cafile : path
  , cache : path
  , "cache-lock-stale": Number
  , "cache-lock-retries": Number
  , "cache-lock-wait": Number
  , "cache-max": Number
  , "cache-min": Number
  , cert: [null, String]
  , color : ["always", Boolean]
  , depth : Number
  , description : Boolean
  , dev : Boolean
  , editor : String
  , "engine-strict": Boolean
  , force : Boolean
  , "fetch-retries": Number
  , "fetch-retry-factor": Number
  , "fetch-retry-mintimeout": Number
  , "fetch-retry-maxtimeout": Number
  , git: String
  , "git-tag-version": Boolean
  , global : Boolean
  , globalconfig : path
  , group : [Number, String]
  , "https-proxy" : [null, url]
  , "user-agent" : String
  , "heading": String
  , "if-present": Boolean
  , "ignore-scripts": Boolean
  , "init-module": path
  , "init-author-name" : String
  , "init-author-email" : String
  , "init-author-url" : ["", url]
  , "init-license": String
  , "init-version": semver
  , json: Boolean
  , key: [null, String]
  , link: Boolean
  // local-address must be listed as an IP for a local network interface
  // must be IPv4 due to node bug
  , "local-address" : getLocalAddresses()
  , loglevel : ["silent", "error", "warn", "http", "info", "verbose", "silly"]
  , logstream : Stream
  , long : Boolean
  , maxsockets : Number
  , message: String
  , "node-version" : [null, semver]
  , npat : Boolean
  , "onload-script" : [null, String]
  , optional: Boolean
  , parseable : Boolean
  , prefix: path
  , production: Boolean
  , "proprietary-attribs": Boolean
  , proxy : [null, false, url] // allow proxy to be disabled explicitly
  , "rebuild-bundle" : Boolean
  , registry : [null, url]
  , rollback : Boolean
  , save : Boolean
  , "save-bundle": Boolean
  , "save-dev" : Boolean
  , "save-exact" : Boolean
  , "save-optional" : Boolean
  , "save-prefix": String
  , scope : String
  , searchopts : String
  , searchexclude: [null, String]
  , searchsort: [ "name", "-name"
                , "description", "-description"
                , "author", "-author"
                , "date", "-date"
                , "keywords", "-keywords" ]
  , shell : String
  , shrinkwrap: Boolean
  , "sign-git-tag": Boolean
  , spin: ["always", Boolean]
  , "strict-ssl": Boolean
  , tag : String
  , tmp : path
  , unicode : Boolean
  , "unsafe-perm" : Boolean
  , usage : Boolean
  , user : [Number, String]
  , userconfig : path
  , umask: Umask
  , version : Boolean
  , "tag-version-prefix" : String
  , versions : Boolean
  , viewer: String
  , _exit : Boolean
  }

function getLocalAddresses () {
  var interfaces
  // #8094: some environments require elevated permissions to enumerate
  // interfaces, and synchronously throw EPERM when run without
  // elevated privileges
  try {
    interfaces = os.networkInterfaces()
  } catch (e) {
    interfaces = {}
  }

  return Object.keys(interfaces).map(function (nic) {
    return interfaces[nic].filter(function (addr) {
      return addr.family === 'IPv4'
    })
    .map(function (addr) {
      return addr.address
    })
  }).reduce(function (curr, next) {
    return curr.concat(next)
  }, []).concat(undefined)
}

exports.shorthands =
  { s : ["--loglevel", "silent"]
  , d : ["--loglevel", "info"]
  , dd : ["--loglevel", "verbose"]
  , ddd : ["--loglevel", "silly"]
  , noreg : ["--no-registry"]
  , N : ["--no-registry"]
  , reg : ["--registry"]
  , "no-reg" : ["--no-registry"]
  , silent : ["--loglevel", "silent"]
  , verbose : ["--loglevel", "verbose"]
  , quiet: ["--loglevel", "warn"]
  , q: ["--loglevel", "warn"]
  , h : ["--usage"]
  , H : ["--usage"]
  , "?" : ["--usage"]
  , help : ["--usage"]
  , v : ["--version"]
  , f : ["--force"]
  , gangster : ["--force"]
  , gangsta : ["--force"]
  , desc : ["--description"]
  , "no-desc" : ["--no-description"]
  , "local" : ["--no-global"]
  , l : ["--long"]
  , m : ["--message"]
  , p : ["--parseable"]
  , porcelain : ["--parseable"]
  , g : ["--global"]
  , S : ["--save"]
  , D : ["--save-dev"]
  , E : ["--save-exact"]
  , O : ["--save-optional"]
  , y : ["--yes"]
  , n : ["--no-yes"]
  , B : ["--save-bundle"]
  , C : ["--prefix"]
  }
