/*
 * grunt-bower-postinst
 * https://github.com/krampstudio/grunt-bower-postinst
 *
 * Copyright (c) 2013 Bertrand Chevrier
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

    grunt.registerMultiTask('bower_postinst', 'Post Installation Tasks for Bower Components.', function bowerPostInstTask () {
        
        var bower = require('bower');
        var spawn = require('child_process').spawn;
        var util = require('util');
        var isWin = process.platform === 'win32';
        var task = grunt.task.current;
        var done = task.async();
        var _ = grunt.util._;
        var stacks = {}; 
        
        var options = task.options({
                directory : bower.config.directory,
                actions : {
                    'git submodule' : ['update'],
                    'npm' : ['install'],
                    'grunt' : [],
                    'jake' : [],
                    'make' : ['install']
                },
                components : []
            });
            
        /**
         * Format cli args : manage spaces in paths
         * @param {Array} args - the arguments
         * @returns {Array} the formatted arguments
         */
        var unspaceArgs = function(args) {
            if (isWin) {
                args = args.map(function(item) {
                    if (item.indexOf(' ') >= 0) {
                        return '"' + item + '"';
                    }
                    else {
                        return item;
                    }
                });
            } else {
                // Unix: escape spaces in paths
                args = args.map(function(item) {
                    return item.replace(' ', '\\ ');
                });
            }
            return args;
        };
        
        Object.keys(options.components).forEach(function(component){
            var compDir =  options.directory + "/" + component,
                actions = options.components[component];
            if(grunt.file.exists(compDir) && grunt.file.isDir(compDir)){
    
                //discover available actions
                var detect = {
                    'git submodule'     : grunt.file.exists(compDir + "/.gitmodules"),
                    'npm'               : grunt.file.exists(compDir + "/package.json"),
                    'grunt'             : grunt.file.exists(compDir + "/Gruntfile.js"),
                    'jake'              : grunt.file.exists(compDir + "/Jakefile"),
                    'make'              : grunt.file.exists(compDir + "/Makefile")
                };
                
                grunt.log.debug("Entering " + compDir  + " -> " + util.inspect(detect));
                
                //build the postinst commands stack to run, merging the user defined and the default actions
                var stack = [];
                actions.forEach(function(action){
                    if(_.isString(action) && _.isArray(options.actions[action])){
                        action = _.object([action], options.actions[action]);
                    }
                    if(_.isPlainObject(action)){
                        Object.keys(action).forEach(function(key){
                            //check if the config file for the action has been detected
                            if(detect[key] !== undefined && detect[key] === false){
                                grunt.fail.warn("Asked to run '" + key + "' but no valid configuration is detected in '" + component + "'");
                            }
                            stack.push(_.flatten([key].concat(action[key])));
                        });
                    }
                });
                
                //transform the stack to an array of spawn function
                stack = stack.map(function(item){
                    return function(callback){
                        if(_.isArray(item) && item.length > 0){
                            var action = unspaceArgs(item);
                            var cmd = (isWin) ? 'cmd' : action[0];
                            var args = (isWin) ? ['/c'].concat(action) : action.slice(1);
                            
                            grunt.log.write('Running on ' + compDir + ' : ' + cmd + ' ' + args.join(' ') + '\n');
                            
                            var child = spawn(cmd, args, {
                                    windowsVerbatimArguments: isWin,
                                    cwd : compDir
                                });
                            child.stdout.on('data', function(data){
                                grunt.log.debug(data);
                            });
                            child.stderr.on('data', function(data){
                                grunt.log.debug(data);
                            });
                            child.on('exit', function(code){
                                if(code === 0){
                                    callback(null, action[0] + ' on ' + compDir);
                                    grunt.log.write('Finnished : ' + action[0] + ' on ' + compDir + '\n');
                                } else {
                                    callback(new Error("Process exit with code " + code));
                                } 
                            });
                        }
                    };
                });
                
                stacks[component] = stack;
                
            } else {
                grunt.log.warn('Component "' + component + '" not found in ' + compDir );
            }
        });
        
        //once the stack is completed, format the stack to run series by component
        var series = _.values(stacks).map(function(stack){
            return function(){
                grunt.util.async.series(stack, function(err, result){
                     if(err){
                        grunt.fail.warn(err);
                    } else {
                        grunt.log.write(result);
                    }
                });
            };
        });
        
        //then run all series in parrallel
        grunt.util.async.parallel(series, function(err, result){
            if(err){
                grunt.fail.warn(err);
            } else {
                grunt.log.write(result);
            }
            done(true);
        });
    });
};

@@ -2086,24 +2086,158 @@
 pDir))%7B%0A    
+    %0A                //will contains the stack of actions to excute for the component%0A                var stack = %5B%5D;%0A                
 %0A           
@@ -2865,32 +2865,49 @@
                %0A
+                %0A
                 
@@ -3003,40 +3003,8 @@
 ons%0A
-                var stack = %5B%5D;%0A
     
@@ -3347,16 +3347,45 @@
 n(key)%7B%0A
+                            %0A
         
@@ -3942,16 +3942,20 @@
 of spawn
+able
  functio
@@ -4406,16 +4406,18 @@
 og.write
+ln
 ('Runnin
@@ -4469,23 +4469,16 @@
 oin(' ')
- + '%5Cn'
 );%0A     
@@ -5217,114 +5217,28 @@
 pDir
-);%0A                                    grunt.log.write('Finnished : ' + action%5B0%5D + ' on ' + compDir + '%5Cn
++ ' ran successfully
 ');%0A
@@ -5836,15 +5836,18 @@
 s = 
-_.value
+Object.key
 s(st
@@ -5865,21 +5865,25 @@
 unction(
-stack
+component
 )%7B%0A     
@@ -5953,16 +5953,28 @@
 es(stack
+s%5Bcomponent%5D
 , functi
@@ -6123,32 +6123,340 @@
     
-grunt.log.write(result);
+if(result && result.length %3E 0)%7B%0A                            grunt.log.ok(component + ' postinst executed ' + result.length + (result.length %3E 1 ? ' actions' : ' action') + ' successfully');%0A                        %7D else%7B%0A                            grunt.log.warn('No actions executed for ' + component);%0A                        %7D
 %0A   
@@ -6623,32 +6623,24 @@
 function(err
-, result
 )%7B%0A         
@@ -6740,20 +6740,35 @@
 log.
-write(result
+ok('bower posinst finished'
 );%0A 

update output