var pull    = require('pull-stream')
var pt      = require('pull-traverse')
var semver  = require('semver')
var cat     = require('pull-cat')
var clean    = require('./clean')
var paramap  = require('pull-paramap')

module.exports = createResolve

function check(pkg, name, range) {
  if(!pkg) return false
  if(pkg.tree[name] && semver.satisfies(pkg.tree[name].version, range, true))
    return true
  return check(pkg.parent, name, range)
}

function fixModule (module) {
  if('string' === typeof module) {
  var parts = module.split('@')
    return {name: parts.shift(), version: parts.shift() || '*'}
  }
  return module
}

function niceError(err, parent, name, range) {
  if(!err) return
  err.message = 'package:'
  + parent.name + '@' + parent.version
  + ' could not resolve '
  + name + '@' + range
  + '\n' + err.message

  return err
}

var unresolved = {}

function createResolve (resolvePackage) {

  function resolveTree (module, opts, cb) {

    module = fixModule(module)

    var filter = opts.filter || function (pkg, root) {
      if(!pkg) return
      pkg.parent.tree[pkg.name] = pkg
    }

    if (! module) return cb(null, {})

    resolvePackage(module.name, module.version, opts, function (err, pkg) {
      if(err) return cb(err)
   
      var root = pkg

      if(opts.available) {
        root.parent = {tree: opts.available}
      }

      pull(cat([
        pull.values([pkg]),
        pt.depthFirst(pkg, function (pkg) {
          var deps = pkg.dependencies || {}
          pkg.tree = {}
          return pull(
            pull.values(Object.keys(deps)),
            //this could be parallel,
            //but it's not the bottle neck.
            paramap(function (name, cb) {
              //check if there is already a module that resolves this...

              //filter out versions that we already have.
              //if(opts.check !== false && check(pkg, name, deps[name]))
              //  return cb()
              unresolved[name + '@' + deps[name]] = true
              resolvePackage(name, deps[name], opts, function (err, _pkg) {
                delete unresolved[name + '@' + deps[name]]

                cb(niceError(err, pkg, name, deps[name]), _pkg)
              })
            }),
            pull.filter(),
            pull.through(function (_pkg) {
              _pkg.parent = pkg
              filter(_pkg, root)
            })
          )
        })
      ]),
      pull.drain(null, function (err) {
        cb(err, clean(pkg))
      }))
    })
  
  }

  //install non-conflicting modules as low in the tree as possible.
  function resolveTreeGreedy (module, opts, cb) {
    if(!cb) cb = opts, opts = null

    opts = opts || {}
    opts.filter = function (pkg, root) {
      if(!pkg) return
      if(!root.tree[pkg.name]) {
        root.tree[pkg.name] = pkg
        pkg.parent = root
      }
      else {
        pkg.parent.tree[pkg.name] = pkg
      }
      return pkg
    }

    resolveTree(module, opts, cb)
  }

  return function (module, opts, cb) {
    if(!cb)
      cb = opts, opts = {}

    function resolve(module, cb) {
      if(opts && opts.greedy)
        resolveTreeGreedy(module, opts, cb)
      else
        resolveTree(module, opts, cb)
    }

    if(Array.isArray(module) && module.length > 1) {

      var n = module.length, a = {}

      module.forEach(function (m) {
        resolve(m, next)
      })

      function next (err, tree) {
        if(err) return n = 0, cb(err)
        a[tree.name] = tree
        if(--n) return
        cb(null, a)
      }

    } else {
      if(Array.isArray(module))
        module = module.shift()

      resolve(module, cb)
    }
  }
}

//This Should Never Happen.
process.on('exit', function () {
  var keys = Object.keys(unresolved)
  if(keys.length)
  throw new Error('resolve did not callback: ' + keys.join(', '))
})


@@ -841,16 +841,135 @@
  err%0A%7D%0A%0A
+function merge (a, b) %7B%0A  var c = %7B%7D%0A  for(var k in a)%0A    c%5Bk%5D = a%5Bk%5D%0A  for(var k in b)%0A    c%5Bk%5D = b%5Bk%5D%0A  return c%0A%7D%0A%0A
 var unre
@@ -1258,24 +1258,174 @@
 (null, %7B%7D)%0A%0A
+    //this is the root package - if we are resolving from a package.json%0A    //we should just use that, instead of calling resolve pkg.%0A    var root%0A%0A
     resolveP
@@ -1524,30 +1524,55 @@
 %0A   
-%0A      var root = pkg%0A
+   tree(pkg)%0A    %7D)%0A%0A    function tree (root) %7B
 %0A   
@@ -1644,25 +1644,24 @@
 le%7D%0A      %7D%0A
-%0A
       pull(c
@@ -1686,19 +1686,20 @@
 values(%5B
-pkg
+root
 %5D),%0A    
@@ -1716,19 +1716,20 @@
 thFirst(
-pkg
+root
 , functi
@@ -1764,31 +1764,152 @@
 s = 
-pkg.dependencies
+(%0A            opts.dev && pkg === root%0A          ? merge(pkg.devDependencies, pkg.dependencies)%0A          : pkg.dependencies%0A          )
  %7C%7C %7B%7D%0A
+%0A
     
@@ -2889,19 +2889,20 @@
 , clean(
-pkg
+root
 ))%0A     
@@ -2911,20 +2911,16 @@
 ))%0A    %7D
-)%0A  
 %0A  %7D%0A%0A  

enable deps at root of resolve tree