/* --------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for license information.
 * ------------------------------------------------------------------------------------------ */
'use strict';
var url = require('url');
var path = require('path');
var child_process_1 = require('child_process');
function uriToFilePath(uri) {
    var parsed = url.parse(uri);
    if (parsed.protocol !== 'file:' || !parsed.path) {
        return null;
    }
    var segments = parsed.path.split('/');
    for (var i = 0, len = segments.length; i < len; i++) {
        segments[i] = decodeURIComponent(segments[i]);
    }
    if (process.platform === 'win32' && segments.length > 1) {
        var first = segments[0];
        var second = segments[1];
        // Do we have a drive letter and we started with a / which is the
        // case if the first segement is empty (see split above)
        if (first.length === 0 && second.length > 1 && second[1] === ':') {
            // Remove first slash
            segments.shift();
        }
    }
    return path.normalize(segments.join('/'));
}
exports.uriToFilePath = uriToFilePath;
function isWindows() {
    return process.platform === 'win32';
}
function resolveModule(workspaceRoot, moduleName) {
    var nodePathKey = 'NODE_PATH';
    return new Promise(function (resolve, reject) {
        var result = Object.create(null);
        var nodePath = [];
        if (workspaceRoot) {
            nodePath.push(path.join(workspaceRoot, 'node_modules'));
        }
        child_process_1.exec('npm config get prefix', function (error, stdout, stderr) {
            if (!error) {
                var globalPath = stdout.toString().replace(/[\s\r\n]+$/, '');
                if (globalPath.length > 0) {
                    if (isWindows()) {
                        nodePath.push(path.join(globalPath, 'node_modules'));
                    }
                    else {
                        nodePath.push(path.join(globalPath, 'lib', 'node_modules'));
                    }
                }
            }
            var separator = isWindows() ? ';' : ':';
            var env = process.env;
            var newEnv = Object.create(null);
            Object.keys(env).forEach(function (key) { return newEnv[key] = env[key]; });
            if (newEnv[nodePathKey]) {
                newEnv[nodePathKey] = nodePath.join(separator) + separator + newEnv[nodePathKey];
            }
            else {
                newEnv[nodePathKey] = nodePath.join(separator);
            }
            try {
                var cp_1 = child_process_1.fork(path.join(__dirname, 'resolve.js'), [], { env: newEnv, execArgv: [] });
                cp_1.on('message', function (message) {
                    if (message.command === 'resolve') {
                        var toRequire = moduleName;
                        if (message.success) {
                            toRequire = message.result;
                        }
                        cp_1.send({ command: 'exit' });
                        try {
                            resolve(require(toRequire));
                        }
                        catch (error) {
                            reject(error);
                        }
                    }
                });
                var message = {
                    command: 'resolve',
                    args: moduleName
                };
                cp_1.send(message);
            }
            catch (error) {
                reject(error);
            }
        });
    });
}
exports.resolveModule = resolveModule;
function resolve(moduleName, nodePath, cwd, tracer) {
    var nodePathKey = 'NODE_PATH';
    var app = [
        "var p = process;",
        "p.on('message',function(m){",
        "if(m.c==='e'){",
        "p.exit(0);",
        "}",
        "else if(m.c==='rs'){",
        "try{",
        "var r=require.resolve(m.a);",
        "p.send({c:'r',s:true,r:r});",
        "}",
        "catch(err){",
        "p.send({c:'r',s:false});",
        "}",
        "}",
        "});"
    ].join('');
    return new Promise(function (resolve, reject) {
        var env = process.env;
        var newEnv = Object.create(null);
        Object.keys(env).forEach(function (key) { return newEnv[key] = env[key]; });
        if (nodePath) {
            if (newEnv[nodePathKey]) {
                newEnv[nodePathKey] = nodePath + path.delimiter + newEnv[nodePathKey];
            }
            else {
                newEnv[nodePathKey] = nodePath;
            }
            if (tracer) {
                tracer("NODE_PATH value is: " + newEnv[nodePathKey]);
            }
        }
        newEnv['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = '1';
        try {
            var cp_2 = child_process_1.fork('', [], {
                cwd: cwd,
                env: newEnv,
                execArgv: ['-e', app]
            });
            cp_2.on('error', function (error) {
                reject(error);
            });
            cp_2.on('message', function (message) {
                if (message.c === 'r') {
                    cp_2.send({ c: 'e' });
                    if (message.s) {
                        resolve(message.r);
                    }
                    else {
                        reject(new Error("Failed to resolve module: " + moduleName));
                    }
                }
            });
            var message = {
                c: 'rs',
                a: moduleName
            };
            cp_2.send(message);
        }
        catch (error) {
            reject(error);
        }
    });
}
exports.resolve = resolve;
function resolveGlobalNodePath(tracer) {
    var npmCommand = isWindows() ? 'npm.cmd' : 'npm';
    var stdout = child_process_1.spawnSync(npmCommand, ['config', 'get', 'prefix'], {
        encoding: 'utf8'
    }).stdout;
    if (!stdout) {
        if (tracer) {
            tracer("'npm config get prefix' didn't return a value.");
        }
        return undefined;
    }
    var prefix = stdout.trim();
    if (tracer) {
        tracer("'npm config get prefix' value is: " + prefix);
    }
    if (prefix.length > 0) {
        if (isWindows()) {
            return path.join(prefix, 'node_modules');
        }
        else {
            return path.join(prefix, 'lib', 'node_modules');
        }
    }
    return undefined;
}
exports.resolveGlobalNodePath = resolveGlobalNodePath;
function resolveModulePath(workspaceRoot, moduleName, nodePath, tracer) {
    if (nodePath) {
        if (!path.isAbsolute(nodePath)) {
            nodePath = path.join(workspaceRoot, nodePath);
        }
        return resolve(moduleName, nodePath, nodePath, tracer).then(function (value) {
            if (value.indexOf(path.normalize(nodePath)) === 0) {
                return value;
            }
            else {
                return Promise.reject(new Error("Failed to load " + moduleName + " from node path location."));
            }
        }).then(null, function (error) {
            return resolve(moduleName, resolveGlobalNodePath(tracer), workspaceRoot, tracer);
        });
    }
    else {
        return resolve(moduleName, resolveGlobalNodePath(tracer), workspaceRoot, tracer);
    }
}
exports.resolveModulePath = resolveModulePath;
/**
 * Resolves the given module relative to the given workspace root. In contrast to
 * `resolveModule` this method considers the parent chain as well.
 */
function resolveModule2(workspaceRoot, moduleName, nodePath, tracer) {
    return resolveModulePath(workspaceRoot, moduleName, nodePath, tracer).then(function (path) {
        if (tracer) {
            tracer("Module " + moduleName + " got resolved to " + path);
        }
        return require(path);
    });
}
exports.resolveModule2 = resolveModule2;
