module.exports = rimraf
rimraf.sync = rimrafSync

var path = require("path")
  , fs

try {
  // optional dependency
  fs = require("../../graceful-fs/graceful-fs.js")
} catch (er) {
  fs = require("fs")
}

var lstat = "lstat"
if (process.platform === "win32") {
  // not reliable on windows prior to 0.7.9
  var v = process.version.replace(/^v/, '').split(/\.|-/).map(Number)
  if (v[0] === 0 && (v[1] < 7 || v[1] == 7 && v[2] < 9)) {
    lstat = "stat"
  }
}
if (!fs[lstat]) lstat = "stat"
var lstatSync = lstat + "Sync"

// for EMFILE handling
var timeout = 0
exports.EMFILE_MAX = 1000
exports.BUSYTRIES_MAX = 3

function rimraf (p, cb) {

  if (!cb) throw new Error("No callback passed to rimraf()")

  var busyTries = 0

  rimraf_(p, function CB (er) {
    if (er) {
      if (er.code === "EBUSY" && busyTries < exports.BUSYTRIES_MAX) {
        busyTries ++
        var time = busyTries * 100
        // try again, with the same exact callback as this one.
        return setTimeout(function () {
          rimraf_(p, CB)
        }, time)
      }

      // this one won't happen if graceful-fs is used.
      if (er.code === "EMFILE" && timeout < exports.EMFILE_MAX) {
        return setTimeout(function () {
          rimraf_(p, CB)
        }, timeout ++)
      }

      // already gone
      if (er.code === "ENOENT") er = null
    }

    timeout = 0
    cb(er)
  })
}

function rimraf_ (p, cb) {
  fs[lstat](p, function (er, s) {
    if (er) {
      // already gone
      if (er.code === "ENOENT") return cb()
      // some other kind of error, permissions, etc.
      return cb(er)
    }

    return rm_(p, s, false, cb)
  })
}


var myGid = function myGid () {
  var g = process.getuid && process.getgid()
  myGid = function myGid () { return g }
  return g
}

var myUid = function myUid () {
  var u = process.getuid && process.getuid()
  myUid = function myUid () { return u }
  return u
}


function writable (s) {
  var mode = s.mode || 0777
    , uid = myUid()
    , gid = myGid()
  return (mode & 0002)
      || (gid === s.gid && (mode & 0020))
      || (uid === s.uid && (mode & 0200))
}

function rm_ (p, s, didWritableCheck, cb) {
  if (!didWritableCheck && !writable(s)) {
    // make file writable
    // user/group/world, doesn't matter at this point
    // since it's about to get nuked.
    return fs.chmod(p, s.mode | 0222, function (er) {
      if (er) return cb(er)
      rm_(p, s, true, cb)
    })
  }

  if (!s.isDirectory()) {
    return fs.unlink(p, cb)
  }

  // directory
  fs.readdir(p, function (er, files) {
    if (er) return cb(er)
    asyncForEach(files.map(function (f) {
      return path.join(p, f)
    }), function (file, cb) {
      rimraf(file, cb)
    }, function (er) {
      if (er) return cb(er)
      fs.rmdir(p, cb)
    })
  })
}

function asyncForEach (list, fn, cb) {
  if (!list.length) cb()
  var c = list.length
    , errState = null
  list.forEach(function (item, i, list) {
    fn(item, function (er) {
      if (errState) return
      if (er) return cb(errState = er)
      if (-- c === 0) return cb()
    })
  })
}

// this looks simpler, but it will fail with big directory trees,
// or on slow stupid awful cygwin filesystems
function rimrafSync (p) {
  try {
    var s = fs[lstatSync](p)
  } catch (er) {
    if (er.code === "ENOENT") return
    throw er
  }

  if (!writable(s)) {
    fs.chmodSync(p, s.mode | 0222)
  }

  if (!s.isDirectory()) return fs.unlinkSync(p)

  fs.readdirSync(p).forEach(function (f) {
    rimrafSync(path.join(p, f))
  })
  fs.rmdirSync(p)
}
