"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.installGoBin = installGoBin;
exports.goBinPath = goBinPath;
exports.runGoTool = runGoTool;
exports.runBin = runBin;
exports.commandExists = commandExists;
exports.execTool = execTool;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const util_1 = tslib_1.__importDefault(require("util"));
const child_process_1 = require("child_process");
const coc_nvim_1 = require("coc.nvim");
const which_1 = tslib_1.__importDefault(require("which"));
const config_1 = require("./config");
const versions_1 = require("./versions");
const runExec = util_1.default.promisify(child_process_1.exec);
const isWin = process.platform === 'win32';
async function installGoBin(source, force = false, getVersion) {
    const name = goBinName(source);
    if (!force && await goBinExists(name)) {
        return true;
    }
    const statusItem = coc_nvim_1.window.createStatusBarItem(90, { progress: true });
    statusItem.text = `Installing '${name}'`;
    statusItem.show();
    const success = await goInstall(source) && await goBinExists(name);
    if (success) {
        const vname = getVersion ? `${name}@${await getVersion()}` : name;
        coc_nvim_1.window.showMessage(`Installed '${vname}'`);
    }
    else {
        coc_nvim_1.window.showMessage(`Failed to install '${name}'`, 'error');
    }
    statusItem.hide();
    return success;
}
async function goInstall(source) {
    return await goVersionOrLater('1.17.0')
        ? goRun(`install ${source}@latest`)
        : goRun(`get ${source}@latest`);
}
async function goVersionOrLater(version) {
    try {
        return (0, versions_1.compareVersions)(version, await getGoVersion()) < 0;
    }
    catch (err) {
        // mute
    }
    return false;
}
async function getGoVersion() {
    try {
        const [, out] = await runBin('go', ['version']);
        return out.trim().match(/^go version go(\S+) .*$/)[1];
    }
    catch (err) {
        // mute
    }
    return '';
}
async function goBinPath(source) {
    const name = goBinName(source);
    return path_1.default.join(await (0, config_1.configDir)('bin'), name + (isWin ? ".exe" : ""));
}
async function runGoTool(name, args = []) {
    return runBin(await goBinPath(name), args);
}
async function runBin(bin, args = []) {
    return new Promise((resolve) => {
        const p = (0, child_process_1.spawn)(bin, args);
        let out = "";
        p.stdout.on('data', (data) => out += data);
        p.on("close", code => resolve([code, out]));
    });
}
async function commandExists(command) {
    return Boolean(await (0, which_1.default)(command, { nothrow: true }));
}
////////////////////////////////////////////////////////////////////////////////
async function goBinExists(source) {
    const name = goBinName(source);
    const bin = await goBinPath(name);
    return fileExists(bin);
}
async function fileExists(path) {
    return new Promise((resolve) => fs_1.default.open(path, 'r', (err) => resolve(err === null)));
}
async function goRun(args) {
    const gopath = await (0, config_1.configDir)('tools');
    const gobin = await (0, config_1.configDir)('bin');
    const env = {
        GO111MODULE: 'on',
        GOBIN: gobin,
        GOPATH: gopath,
        GOROOT: '',
        GOTOOLDIR: '',
    };
    const cmd = isWin
        ? `go ${args}`
        : `env GOBIN="${gobin}" go ${args}`;
    const opts = {
        env: Object.assign({}, process.env, env),
        cwd: gopath,
        shell: isWin ? undefined : process.env.SHELL,
        windowsHide: true,
    };
    try {
        await runExec(cmd, opts);
    }
    catch (ex) {
        coc_nvim_1.window.showMessage(ex, 'error');
        return false;
    }
    return true;
}
async function execTool(source, args, input) {
    const [bin, name] = await Promise.all([
        goBinPath(source),
        goBinName(source),
    ]);
    if (!await commandExists(bin)) {
        await installGoBin(source);
    }
    return new Promise((resolve, reject) => {
        const p = (0, child_process_1.execFile)(bin, args, { cwd: coc_nvim_1.workspace.cwd }, async (err, stdout, stderr) => {
            if (err && err.code === "ENOENT") {
                return reject(`Error: Command ${name} not found! Run "CocCommand go.install.${name}" to install it and try again.`);
            }
            if (err) {
                return reject(stderr.toString());
            }
            return resolve(stdout.toString());
        });
        if (p.pid) {
            p.stdin.end(input);
        }
    });
}
////////////////////////////////////////////////////////////////////////////////
function goBinName(source) {
    return source.replace(/\/\.\.\.$/, '').split('/').pop();
}
//# sourceMappingURL=tools.js.map