var fs = require('fs');
var ff = require('ff');

var path = require('path');
var gitClient = require('../util/gitClient');
var stringify = require('../util/stringify');
var spawn = require('child_process').spawn;

var BaseCommand = require('../util/BaseCommand').BaseCommand;

var DEFAULT_PROTOCOL = "https";
var DEFAULT_DEPS = [
  {
    name: "devkit",
    ssh: "git@github.com:",
    https: "https://github.com/",
    repo: "gameclosure/gcapi-priv",
    tag: "2.0.0-beta.5"
  }
];

var InstallCommand = Class(BaseCommand, function (supr) {

  this.name = 'install';
  this.description = 'installs (or updates) devkit dependencies for an app or a specific dependency if one is provided';

  this.init = function () {
    supr(this, 'init', arguments);

    this.opts
      .describe('ssh', 'switches git protocol to ssh, default: false (https)');

  }

  this.exec = function (commands, args) {

    var argv = this.opts.argv;
    var protocol = argv.ssh ? 'ssh' : 'https';
    var module = args.shift();

    // ensure modules directory exists
    if (!fs.existsSync('modules')) {
      fs.mkdirSync('modules');
    }

    if (!fs.statSync('modules').isDirectory()) {
      throw new Error('"modules" must be a directory');
    }

    var directory = path.resolve(process.cwd(), 'modules');

    if (module) {
      // single module provided, install it
      this.install(directory, module);
    } else {
      // no module provided, read and install all modules from package.json
      if (!fs.existsSync('package.json')) {
        // create package.json with default dep for new games
        fs.writeFileSync('package.json', stringify({"devkitDependencies": toJSON(protocol, DEFAULT_DEPS)}));
      }

      try {
        var deps = JSON.parse(fs.readFileSync('package.json')).devkitDependencies;
      } catch (e) {
        throw new Error('Unable to read "package.json"');
      }

      Object.keys(deps).forEach(function (name) {
        this.install(directory, name, deps[name]);
      }, this);
    }
  }

  function toJSON(protocol, deps) {
    var out = {};
    deps.forEach(function (dep) {
      out[dep.name] = dep[protocol] + dep.repo + (dep.tag ? '#' + dep.tag : '');
    });
    return out;
  }

  this.install = function (directory, moduleName, url, tag) {
    var modulePath = path.join(directory, moduleName);
    var git = gitClient.get(path.join(directory, moduleName));
    if (url) {

      // find tag in url
      var i = url.indexOf('#');
      if (!tag && i >= 0) {
        var tag = url.substring(i + 1);
        url = url.substring(0, i);
      }

      this.logger.log('"' + moduleName + '" installing into', modulePath);
      var f = ff(this, function () {
        if (!fs.existsSync(modulePath)) {
          var modulesGit = gitClient.get(directory, {customFds: [0, 1, 2]});
          modulesGit('clone', url, moduleName, f());
        } else {
          git('fetch', f());
        }
      }, function () {
        if (tag) {
          git('checkout', tag, f());
        }
      }, function () {
        git('submodule', 'sync', '--recursive', f());
      }, function () {
        git('submodule', 'update', '--init', '--recursive', f());
      }, function () {
        var npm = spawn('npm', ['install'], {customFds: [0, 1, 2], cwd: modulePath});
        npm.on('close', f());
      }, function () {
        try {
          var name = require(path.join(directory, moduleName, 'package.json')).name;
        } catch (e) {
          // no package.json file in this module
        }

        // directory name should always match the name provided in package.json
        if (name && name != moduleName) {
          this.logger.warn('This module has changed names from "' +
            moduleName + '" to "' + name + '". Please update your dependency.');
        }
      }).error(function (err) {
        logger.error(err);
      });
    }
  }
});

module.exports = InstallCommand;

@@ -1893,42 +1893,190 @@
     
-Object.keys(deps).forEach(function
+// serially install all dependencies%0A      var index = 0;%0A      var names = Object.keys(deps);%0A      var next = bind(this, function () %7B%0A        var name = names%5Bindex++%5D;%0A        if
  (na
@@ -2077,24 +2077,26 @@
 if (name) %7B%0A
+  
         this
@@ -2131,16 +2131,28 @@
 ps%5Bname%5D
+, null, next
 );%0A     
@@ -2156,15 +2156,33 @@
     
+ 
-%7D, this
+ %7D%0A      %7D);%0A      next(
 );%0A 
@@ -2447,16 +2447,20 @@
 url, tag
+, cb
 ) %7B%0A    
@@ -3214,174 +3214,8 @@
 ) %7B%0A
-        git('submodule', 'sync', '--recursive', f());%0A      %7D, function () %7B%0A        git('submodule', 'update', '--init', '--recursive', f());%0A      %7D, function () %7B%0A
     
@@ -3870,24 +3870,24 @@
 r(err);%0A
-
       %7D)
 ;%0A    %7D%0A
@@ -3874,24 +3874,31 @@
 r);%0A      %7D)
+.cb(cb)
 ;%0A    %7D%0A  %7D%0A

move install scripts and submodule updates into npm scripts