text: // jshint node: true
  "use strict";
  
  var _ = require("lodash"),
    path = require("path"),
    fs = require("fs"),
    rm_rf = require("rimraf"),
    exists = require("exists-file"),
    R = require("ramda"),
    mkdir = require("mkdirp"),
    Logger = require("./Logger"),
    utils = require("./utils"),
    PROJECT_CONFIG = "webgme-setup.json";
  const { spawn } = require("child_process");
  
  var BaseManager = function (logger) {
    this._logger = logger || new Logger();
  };
  
  BaseManager.prototype.start = async function (options, callback) {
    var root = utils.getRootPath();
  
    if (!root) {
      var err = `No webgme app found!`;
      this._logger.error(err);
      return callback(err);
    }
  
    if (options.hard) {
      this._logger.write("Removing installed modules");
      rm_rf(
        path.join(root, "node_modules"),
        this._start.bind(this, root, callback)
      );
    } else {
      try {
        await this._start(root);
        callback();
      } catch (err) {
        callback(err);
        throw err;
      }
    }
  };
  
  BaseManager.prototype._start = async function (root) {
    this._logger.write("Installing dependencies...");
    await this.spawn("npm", ["install"]);
  
    const webgmePath = path.join(root, "node_modules", "webgme-engine");
    if (!exists(webgmePath)) {
      this._logger.write("Installing webgme...");
      await this.spawn("npm", ["install", "webgme"]);
    }
  
    await this.spawn("npm", ["start"]);
  };
  
  BaseManager.prototype.spawn = async function (cmd, args) {
    const job = spawn(cmd, args);
    job.stdout.pipe(process.stdout);
    job.stderr.pipe(process.stderr);
    await new Promise((resolve, reject) =>
      job.on("close", (code) => (code ? reject(code) : resolve()))
    );
  };
  
  BaseManager.prototype.init = function (args, callback) {
    // Create new project
    var project, name, err;
  
    project = path.resolve(args.name || process.cwd());
    name = path.basename(project).toLowerCase();
    this._logger.info("Creating new project at " + project);
  
    if (!args.name) {
      // Creating in current directory
      // Check if the project contains webgme-setup.json file
      var setupJsonPath = path.join(process.cwd(), PROJECT_CONFIG);
      if (exists(setupJsonPath)) {
        err = "Cannot create project here. Project already exists.";
        this._logger.error(err);
        return callback(err);
      }
    } else {
      // Check that the target directory doesn't exist
      if (exists(project)) {
        err = "Cannot create " + args.name + ". File exists.";
        this._logger.error(err);
        return callback(err);
      }
    }
  
    mkdir(
      project,
      function (err) {
        if (err) {
          this._logger.error(`Creating project failed: ${err}`);
          return rm_rf(project, (e) => {
            if (e) {
              this._logger.error(`Cleanup failed: ${e}`);
            }
            return callback(err);
          });
        }
  
        // Create the package.json
        this._createPkgJson(project, name);
  
        // Create the base directories
        BaseManager._createBasicFileStructure(project);
  
        // Create the webgme files
        this._createWebGMEFiles(project);
  
        // Create the project info file
        var webgmeInfo = {
          components: {},
          dependencies: {},
        };
        fs.writeFileSync(
          path.join(project, PROJECT_CONFIG),
          JSON.stringify(webgmeInfo, null, 2)
        );
        utils.updateWebGMEConfig(project);
  
        this._logger.write(
          "Created project at " +
            project +
            ".\n\nStart your WebGME " +
            "app with 'webgme start' from the project root.\n" +
            "It is recommended to run 'npm init' from the within project " +
            "to finish configuration."
        );
        callback();
      }.bind(this)
    );
  };
  
  BaseManager.prototype._createPkgJson = function (project, name) {
    var pkgJsonTemplatePath = path.join(
        __dirname,
        "res",
        "package.template.json"
      ),
      pkgJsonTemplate = _.template(fs.readFileSync(pkgJsonTemplatePath)),
      toolJsonPath = path.join(__dirname, "..", "package.json"),
      toolJson = require(toolJsonPath),
      pkgContent = {
        webgmeVersion: toolJson.dependencies["webgme-engine"],
        name: name,
      },
      outputPkgJson = path.join(project, "package.json"),
      originalPkgJson = {},
      newPkgJson = JSON.parse(pkgJsonTemplate(pkgContent)),
      pkgJson;
  
    // Load existing package.json, if exists
    if (exists(outputPkgJson)) {
      originalPkgJson = require(outputPkgJson);
      // We will remove the require cache for the package.json since
      // we are changing it now
      delete require.cache[require.resolve(outputPkgJson)];
    }
    pkgJson = _.extend({}, originalPkgJson, newPkgJson);
  
    // Copy the dev and regular dependencies separately (_.extend is shallow)
    pkgJson.dependencies = _.extend(
      originalPkgJson.dependencies || {},
      newPkgJson.dependencies
    );
    pkgJson.devDependencies = _.extend(
      originalPkgJson.devDependencies || {},
      newPkgJson.devDependencies
    );
  
    this._logger.info("Writing package.json to " + outputPkgJson);
  
    fs.writeFileSync(outputPkgJson, JSON.stringify(pkgJson, null, 2));
  };
  
  BaseManager._createBasicFileStructure = function (project) {
    var vizDir = path.join("src", "visualizers"),
      dirs = ["src", "test", path.join("src", "common"), vizDir];
  
    dirs.forEach((dir) => {
      var absDir = path.join(project, dir);
      fs.mkdirSync(absDir);
    });
  
    fs.writeFileSync(path.join(project, vizDir, "Visualizers.json"), "[]");
  };
  
  BaseManager.prototype._createWebGMEFiles = function (project) {
    // Create webgme config info
    var configDir = path.join(project, "config");
  
    // Config files
    fs.mkdirSync(configDir);
    fs.readdirSync(path.join(__dirname, "res", "config"))
      .map(R.pipe(R.nthArg(0), path.join.bind(path, "config"))) // Add 'config/' for each
      .forEach(BaseManager._copyFileToProject.bind(null, project, ""));
  
    // Create app.js
    BaseManager._copyFileToProject(project, "", "app.js");
  
    // Create test fixtures
    BaseManager._copyFileToProject(project, "test", "globals.js");
  
    // Create .gitignore
    BaseManager._createGitIgnore(project);
  
    // Create src/common README.md
    BaseManager._createCommonReadme(project);
  
    // Create README.md
    BaseManager._createReadme(project);
  };
  
  BaseManager._createReadme = function (project) {
    var boilerplatePath = path.join(__dirname, "res", "README.md.ejs"),
      readme = _.template(fs.readFileSync(boilerplatePath, "utf8")),
      dstPath = path.join(project, "README.md"),
      content = {
        name: path.basename(project),
      };
  
    if (!exists(dstPath)) {
      fs.writeFileSync(dstPath, readme(content));
    }
  };
  
  BaseManager._createCommonReadme = function (project) {
    var boilerplatePath = path.join(__dirname, "res", "README.common.md.ejs"),
      readme = _.template(fs.readFileSync(boilerplatePath, "utf8")),
      dstPath = path.join(project, "src", "common", "README.md"),
      content = {
        name: path.basename(project),
      };
  
    if (!exists(dstPath)) {
      fs.writeFileSync(dstPath, readme(content));
    }
  };
  
  /**
   * Copy the file to the project if the file doesn't already exist
   *
   * @param {String} project
   * @param {String} subPath
   * @param {String} filename
   * @return {undefined}
   */
  BaseManager._createGitIgnore = function (project) {
    var boilerplatePath = path.join(__dirname, "res", "gitignore"),
      dstPath = path.join(project, ".gitignore");
  
    if (!exists(dstPath)) {
      fs.createReadStream(boilerplatePath).pipe(fs.createWriteStream(dstPath));
    }
  };
  
  BaseManager._copyFileToProject = function (project, subPath, filename) {
    var boilerplatePath, dstPath;
  
    subPath = subPath || "";
    boilerplatePath = path.join(__dirname, "res", filename);
    dstPath = path.join(project, subPath, filename);
  
    fs.createReadStream(boilerplatePath).pipe(fs.createWriteStream(dstPath));
  };
  
  module.exports = BaseManager;
  