/*
 * Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

"use strict";

var Logger = require("./Logger"),
    Server = require("./Server"),
    os     = require("os");

/** @define {boolean} Whether debugger should be enabled at launch
 *
 * If true, the debugger is enabled automatically, and the init function is
 * *not* called. This gives the user time to connect with the remote
 * debugger and set breakpoints before running "launch" him/herself from the
 * console. In this case, launch is run by calling the global function
 * debugLaunch(), which only exists if DEBUG_ON_LAUNCH is set.
 *
 * NOTE: This is only useful for debugging the launch routine. If you
 * want to simply debug some command module, enable the debugger by
 * sending the base.enableDebugger command and then connect to it.
 *
 * NOTE: On Mac, if you leave the debugger stopped at a breakpoint and then
 * exit Brackets, the node process will become abandoned. (Because it is
 * stopped, it will never check its stdin to see if it's closed, so it
 * won't know that it's parent process has died.)
 */
var DEBUG_ON_LAUNCH = false;

/** @define {?string} Filename to dump all log data to.
 * If not null, all log data is appended to the specified file.
 */
var LOG_FILENAME_ON_LAUNCH = null;


function exit() {
    if (Server) {
        Server.stop();
    }
    process.exit(0);
}

/**
 * Top-level handler for any uncaught exception. Attempts to log
 * the exception and shut down gracefully. (Though that may of course
 * fail depending on the exception.)
 *
 * TODO: Switch to using Node Domains once they're stable enough. In v0.8.x
 * Domains are "Stability: 1 - Experimental". What we really want to
 * do is run every connection in a separate Domain. Then, if an individual
 * connection has an uncaught exception, we can just close it and still
 * continue to run normally.
 */
function uncaughtExceptionHandler() {
    var args = Array.prototype.slice.call(arguments, 0);
    args = args.map(function (arg) {
        return arg instanceof Error ? arg.stack : arg;
    });
    args.unshift("[Launcher] uncaught exception at top level, exiting.");
    Logger.error.apply(null, args);
    exit();
}

/**
 * Setup the Logger and launch the server. If DEBUG_ON_LAUNCH is false,
 * this is called immediately on module load. If DEBUG_ON_LAUNCH is true,
 * this method can be called by calling the global debugLaunch function
 * from the console.
 */
function launch() {
    process.on("uncaughtException", uncaughtExceptionHandler);
    if (LOG_FILENAME_ON_LAUNCH) {
        Logger.setLogFilename(LOG_FILENAME_ON_LAUNCH);
    }
    Logger.remapConsole();
    Server.on("end", function () {
        Server = null;
        Logger.info(
            "[Launcher] received Server \"end\" event, exiting process"
        );
        exit();
    });
    Server.start();
}

if (!DEBUG_ON_LAUNCH) {
    launch();
} else {
    var noopTimer = setInterval(function () {
        // no-op so that we don't exit the process
    }, 100000);

    // Inject a global so that user can call launch from the console
    // The debugger won't stop at breakpoints if they're reached as a
    // direct result of evaluation of the console. So, we call launch()
    // from a timer. This will allow hitting breakpoints set in launch()
    // and in the functions it calls.
    global.debugLaunch = function () {
        process.nextTick(function () {
            clearInterval(noopTimer);
            launch();
        });
    };
    process._debugProcess(process.pid);
}

// Set environment variable to use built-in Node.js API for temp directory
if (!process.env["TMPDIR"] && !process.env["TMP"] && !process.env["TEMP"]) {
    process.env["TMPDIR"] = process.env["TMP"] = process.env["TEMP"] = os.tmpdir();
}

exports.launch = launch;
exports.exit   = exit;
