//Dependencies
var fs          = require('fs'),
    mongoose    = require('mongoose');
    

/**
 * Clears a collection and inserts the given data as new documents
 *
 * @param {Mixed}       The data to load. This parameter accepts either:
 *                          String: Path to a file or directory to load
 *                          Object: Object literal in the form described above
 * @param {Connection}  [db] Optionally, the mongoose connection to use.
 *                          Defaults to mongoose.connection.
 * @param {Function}    Callback
 */
var load = exports.load = function(data, db, callback) {
    if (typeof db === 'function') {
        callback = db;
        db = mongoose.connection;
    }

    if (typeof data == 'object') {

        loadObject(data, db, callback);

    } else if (typeof data == 'string') {

        //Get the absolute dir path if a relative path was given
        if (data.substr(0, 1) !== '/') {
            var parentPath = module.parent.filename.split('/');
            parentPath.pop();
            data = parentPath.join('/') + '/' + data;
        }

        //Determine if data is pointing to a file or directory
        fs.stat(data, function(err, stats) {
            if (err) throw err;

            if (stats.isDirectory()) {
                loadDir(data, db, callback);
            } else { //File
                loadFile(data, db, callback);
            }
        });

    } else { //Unsupported type

        callback(new Error('Data must be an object, array or string (file or dir path)'));

    }
}

    
/**
 * Clears a collection and inserts the given data as new documents
 *
 * @param {String}      The name of the model e.g. User, Post etc.
 * @param {Object}      The data to insert, as an array or object. E.g.:
 *                          { user1: {name: 'Alex'}, user2: {name: 'Bob'} }
 *                      or:
 *                          [ {name: 'Alex'}, {name:'Bob'} ]
 * @param {Connection}  The mongoose connection to use
 * @param {Function}    Callback
 */
function insertCollection(modelName, data, db, callback) {
    callback = callback || {};
    
    //Counters for managing callbacks
    var tasks = { total: 0, done: 0 };
    
    //Load model
    var Model = db.model(modelName);
    
    //Clear existing collection
    Model.collection.remove(function(err) {
        if (err) return callback(err);
        
        //Convert object to array
        var items = [];
        if (Array.isArray(data)) {
            items = data;
        } else {
            for (var i in data) {
                items.push(data[i]);
            }
        }
        
        //Check number of tasks to run
        if (items.length == 0) {
            return callback();
        } else {
            tasks.total = items.length;
        }
        
        //Insert each item individually so we get Mongoose validation etc.
        items.forEach(function(item) {                       
            var doc = new Model(item);
            doc.save(function(err) {
                if (err) return callback(err);
                
                //Check if task queue is complete
                tasks.done++;
                if (tasks.done == tasks.total) callback();
            });
        });
    });
}


/**
 * Loads fixtures from object data
 * 
 * @param {Object}      The data to load, keyed by the Mongoose model name e.g.:
 *                          { User: [{name: 'Alex'}, {name: 'Bob'}] }
 * @param {Connection}  The mongoose connection to use
 * @param {Function}    Callback
 */
function loadObject(data, db, callback) {
    callback = callback || function() {};
    
    //Counters for managing callbacks
    var tasks = { total: 0, done: 0 };
    
    //Go through each model's data
    for (var modelName in data) {
        (function() {
            tasks.total++;
            
            insertCollection(modelName, data[modelName], db, function(err) {
                if (err) throw(err);
                
                tasks.done++;
                if (tasks.done == tasks.total) callback();
            });
        })();
    }
}


/**
 * Loads fixtures from one file
 * 
 * TODO: Add callback option
 * 
 * @param {String}      The full path to the file to load
 * @param {Connection}  The mongoose connection to use
 * @param {Function}    Callback
 */
function loadFile(file, db, callback) { 
    callback = callback || function() {};
    
    if (file.substr(0, 1) !== '/') {
        var parentPath = module.parent.filename.split('/');
        parentPath.pop();
        file = parentPath.join('/') + '/' + file;
    }
    
    load(require(file), db, callback);
}


/**
 * Loads fixtures from all files in a directory
 * 
 * TODO: Add callback option
 * 
 * @param {String}      The directory path to load e.g. 'data/fixtures' or '../data'
 * @param {Connection}  The mongoose connection to use
 * @param {Function}    Callback
 */
function loadDir(dir, db, callback) {
    callback = callback || function() {};
    
    //Get the absolute dir path if a relative path was given
    if (dir.substr(0, 1) !== '/') {
        var parentPath = module.parent.filename.split('/');
        parentPath.pop();
        dir = parentPath.join('/') + '/' + dir;
    }
    
    //Counters for managing callbacks
    var tasks = { total: 0, done: 0 };
    
    //Load each file in directory
    fs.readdir(dir, function(err, files){
        if (err) return callback(err);
        
        tasks.total = files.length;
        
        files.forEach(function(file) {
            loadFile(dir + '/' + file, db, function(err) {
                if (err) return callback(err);
                
                tasks.done++;
                if (tasks.total == tasks.done) callback();
            });
        });
    });
};
@@ -1,18 +1,4 @@
-//Dependencies
 %0Avar
@@ -29,13 +29,13 @@
 fs')
-,%0A   
+;%0Avar
  mon
@@ -62,24 +62,60 @@
 mongoose');%0A
+var async       = require('async');%0A
     %0A%0A/**%0A *
@@ -3668,429 +3668,154 @@
     
-%0A    //Counters for managing callbacks%0A    var tasks = %7B total: 0, done: 0 %7D;%0A    %0A    //Go through each model's data%0A    for (var modelName in data) %7B%0A        (function() %7B%0A            tasks.total++;%0A            %0A            insertCollection(modelName, data%5BmodelName%5D, db, function(err) %7B%0A                if (err) throw(err);%0A                %0A                tasks.done++;%0A                if (tasks.done == tasks.total)
+var iterator = function(modelName, next)%7B%0A        insertCollection(modelName, data%5BmodelName%5D, db, next);%0A    %7D;%0A    async.forEach(data, iterator,
  cal
@@ -3823,48 +3823,11 @@
 back
-(
 );%0A
-            %7D);%0A        %7D)();%0A    %7D%0A
 %7D%0A%0A%0A
@@ -4961,90 +4961,8 @@
    %0A
-    //Counters for managing callbacks%0A    var tasks = %7B total: 0, done: 0 %7D;%0A    %0A
     
@@ -5093,67 +5093,23 @@
     
-tasks.total = files.length;%0A        %0A        files.forEach(
+var iterator = 
 func
@@ -5117,18 +5117,23 @@
 ion(file
+, next
 )
- 
 %7B%0A      
@@ -5173,202 +5173,73 @@
 db, 
-function(err) %7B%0A                if (err) return callback(err);%0A                %0A                tasks.done++;%0A                if (tasks.total == tasks.done) callback();%0A            %7D);%0A        %7D
+next);%0A        %7D;%0A        async.forEach(files, iterator, callback
 );%0A 

refactor using async module