/**
 * Copyright 2013 the PM2 project authors. All rights reserved.
 * Use of this source code is governed by a license that
 * can be found in the LICENSE file.
 */
var shelljs       = require('shelljs');
var path          = require('path');
var fs            = require('fs');
var async         = require('async');
var p             = path;
var spawn         = require('child_process').spawn;
var chalk         = require('chalk');
var Configuration = require('../../Configuration.js');
var cst           = require('../../../constants.js');
var Common        = require('../../Common');
var Utility       = require('../../Utility.js');

var MODULE_CONF_PREFIX = 'module-db';

var Modularizer = module.exports = {};

function startModule(CLI, opts, cb) {
  /** SCRIPT
   * Open file and make the script detection smart
   */

  if (!opts.cmd) throw new Error('module package.json not defined');
  if (!opts.development_mode) opts.development_mode = false;

  try {
    var package_json = require(opts.cmd);
  } catch(e) {
    Common.printError(e);
    return cb();
  }

  /**
   * Script file detection
   * 1- *apps* field (default pm2 json configuration)
   * 2- *bin* field
   * 3- *main* field
   */
  if (!package_json.apps) {
    package_json.apps = {};

    if (package_json.bin) {
      var bin = Object.keys(package_json.bin)[0];

      package_json.apps.script = package_json.bin[bin];
    }
    else if (package_json.main) {
      package_json.apps.script = package_json.main;
    }
  }

  Common.extend(opts, {
    cwd               : opts.proc_path,
    watch             : opts.development_mode,
    force_name        : package_json.name,
    started_as_module : true
  });

  // Start the module
  CLI.start(package_json, opts, function(err, data) {
    if (err) return cb(err);
    return cb(null, data);
  });
};

Modularizer.launchModules = function(CLI, cb) {
  var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
  var modules = Configuration.getSync(MODULE_CONF_PREFIX);

  if (!modules) return cb();

  async.eachLimit(Object.keys(modules), 1, function(module, next) {
    var pmod = p.join(module_folder, module, cst.DEFAULT_MODULE_JSON);

    Common.printOut(cst.PREFIX_MSG_MOD + 'Starting module ' + module);

    var opts = {};

    if (modules[module] != true) {
      Common.extend(opts, modules[module]);
    }

    Common.extend(opts, {
      cmd : pmod,
      development_mode : false,
      proc_path : p.join(module_folder, module)
    });

    startModule(CLI, opts, function(err, dt) {
      if (err) console.error(err);
      return next();
    });

  }, function() {
    return cb ? cb(null) : false;
  });
}

Modularizer.installModule = function(CLI, module_name, opts, cb) {
  var proc_path = '',
      cmd  = '',
      conf = {},
      development_mode = false;

  var cli = {
    bin : 'npm',
    cmd : 'install'
  }

  Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.bin.toUpperCase() + ']') + ' to install ' + module_name + ' ...');

  var install_instance = spawn(cst.IS_WINDOWS ? cli.bin + '.cmd' : cli.bin, [cli.cmd, module_name, '--loglevel=error'], {
    stdio : 'inherit',
    env: process.env,
		shell : true,
    cwd : cst.PM2_ROOT_PATH
  });

  install_instance.on('close', finalize);

  install_instance.on('error', function (err) {
    console.error(err.stack || err);
  });

  function finalize(code) {
    if (code != 0) {
      return cb(new Error("Installation failed"));
    }

    Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');

    var canonic_module_name = Utility.getCanonicModuleName(module_name);

    proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', canonic_module_name);

    cmd = p.join(proc_path, cst.DEFAULT_MODULE_JSON);

    /**
     * Append default configuration to module configuration
     */
    try {
      var conf = JSON.parse(fs.readFileSync(path.join(proc_path, 'package.json')).toString()).config;
      if (conf) {
        Object.keys(conf).forEach(function(key) {
          Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);
        });
      }
    } catch(e) {
      Common.printError(e);
    }

    opts = Common.extend(opts, {
      cmd : cmd,
      development_mode : development_mode,
      proc_path : proc_path
    });

    Configuration.set(MODULE_CONF_PREFIX + ':' + canonic_module_name, {
      uid : opts.uid,
      gid : opts.gid,
      version : 0
    }, function(err, data) {

      startModule(CLI, opts, function(err, dt) {
        if (err) return cb(err);

        if (process.env.PM2_PROGRAMMATIC === 'true')
          return cb(null, dt);

        CLI.conf(canonic_module_name, function() {
          Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
          Common.printOut(cst.PREFIX_MSG_MOD + 'Edit configuration via: `pm2 conf`');
          return cb(null, dt);
        });
      });
    });
  }
}
