text: var path = require('path')
  var count = require('count-top-entries')
  var wishlist = require('f-d-wishlist')
  
  function gotAllNestedFiles (dir, map, cb) { // cb tells the truth
    count(dir, function (err, data) {
      if (err) return cb(err)
      if (!map[dir] || (data.files !== map[dir].length)) {
        cb(null, false)
      } else if (!data.dirs) {
        cb(null, true)
      } else {
        wishlist(dir, { full: true }, function (err, wishlist) {
          if (err) return cb(err)
          wishlist.dirs.forEach(function (d) {
            gotAllNestedFiles(d, map, cb)
          })
        })
      }
    })
  }
  
  // designed to be used with ferross drag-drop module
  function group (files, opts, callback) { // opts: { size: boolean }
    if (typeof opts === 'function') return group(files, {}, opts)
    var allAbs = files.every(function (file) {
      return path.isAbsolute(file.path)
    })
    if (!allAbs) return callback(Error('cannot yet handle relative filepaths'))
    var trap = { dirs: [], files: [], paths: [], fdir: [], map: {}, temp: [] }
    var groups = []
    Array.prototype.push.apply(trap.paths, files.map(function (file) {
      return file.path
    }))
    // if single file input always early return as single file
    if (!trap.paths.length) {
      return callback(null, [])
    } else if (trap.paths.length === 1) {
      var singleton = { type: 'file', path: trap.paths[0] }
      if (opts.size) singleton.size = files[0].size
      return callback(null, [ singleton ])
    }
    // split paths into file objects
    trap.fdir = trap.paths.map(function (filepath) {
      return { path: filepath, dir: path.dirname(filepath) }
    })
    // map files to dirs
    trap.map = trap.fdir.reduce(function (acc, cur) {
      if (acc.hasOwnProperty(cur.dir)) acc[cur.dir].push(cur.path)
      else acc[cur.dir] = [ cur.path ]
      return acc
    }, {})
    // push keys of props that represent an entire dir to trap.dirs... via trap.t
    var dirs = Object.keys(trap.map)
    var pending = dirs.length
    dirs.forEach(function (dir) {
      gotAllNestedFiles(dir, trap.map, function (err, truth) {
        if (err) return callback(err, null)
        if (truth) trap.temp.push(dir)
        if (!--pending) finishUp()
      })
    })
    // finish
    function finishUp () {
      // push paths that are not covered by trap.temp to trap.files
      Array.prototype.push.apply(trap.files,
        trap.fdir.filter(function (file) {
          return !trap.temp.some(function (dir) {
            return dir === file.dir
          })
        }).map(function (file) {
          return file.path
        })
      )
      // collapse nested dirs in trap.temp to trap.dirs
      Array.prototype.push.apply(trap.dirs,
        trap.temp.filter(function (dir, i, arr) {
          return !arr.filter(function (d) {
            return d !== dir
          }).some(function (other) {
            return dir.startsWith(other)
          })
        })
      )
      // package neatly
      groups = trap.files.map(function (file) {
        return { type: 'file', path: file }
      }).concat(trap.dirs.map(function (dir) {
        return { type: 'directory', path: dir }
      }))
      // maybe add size
      if (opts.size) {
        groups = groups.map(function (item, i) {
          if (item.type === 'file') {
            item.size = files.find(function (file) {
              return file.path === item.path
            }).size
          } else {
            item.size = files.reduce(function (acc, cur) {
              if (cur.path.startsWith(item.path)) acc += cur.size
              return acc
            }, 0)
          }
          return item
        })
      }
      callback(null, groups)
    }
  }
  
  module.exports = group
  