"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tmpFileManager = exports.ChildProcManager = exports.DelayedCaller = exports.childProcessStderrRedir = exports.childProcessStdoutRedir = exports.ConnectionLogger = exports.isStringListEqual = exports.getTmpDirSync = exports.resolvedPath = exports.pathToUri = exports.uriToPath = exports.fsUnlinkSync = exports.fsWriteFileSync = exports.fsWriteFile = exports.fsExists = exports.fsReadFileSync = exports.fsReadFile = void 0;
const node_1 = require("vscode-languageserver/node");
const vscode_uri_1 = require("vscode-uri");
const child = require("child_process");
const fs = require('fs');
const path = require('path');
const util = require('util');
const tmp = require('tmp');
const fsMkDir = util.promisify(fs.mkdir);
const _fsWriteFile = util.promisify(fs.writeFile);
exports.fsReadFile = util.promisify(fs.readFile);
exports.fsReadFileSync = fs.readFileSync;
exports.fsExists = util.promisify(fs.exists);
async function fsWriteFile(file, content) {
    try {
        return fsMkDir(path.dirname(file), { recursive: true })
            .then(() => { return _fsWriteFile(file, content); })
            .catch(error => { ConnectionLogger.error(error); });
    }
    catch (error) {
        ConnectionLogger.error(error);
    }
}
exports.fsWriteFile = fsWriteFile;
function fsWriteFileSync(file, content) {
    try {
        fs.mkdirSync(path.dirname(file), { recursive: true });
        fs.writeFileSync(file, content);
        return true;
    }
    catch (error) {
        ConnectionLogger.error(error);
        return false;
    }
}
exports.fsWriteFileSync = fsWriteFileSync;
function fsUnlinkSync(file) {
    try {
        fs.unlinkSync(file);
        return true;
    }
    catch (error) {
        ConnectionLogger.error(error);
        return false;
    }
}
exports.fsUnlinkSync = fsUnlinkSync;
function uriToPath(uri) {
    try {
        let fsPath = vscode_uri_1.URI.parse(uri).fsPath;
        try {
            return fs.realpathSync(fsPath);
        }
        catch (error) {
            ConnectionLogger.error(error);
            return fsPath;
        }
    }
    catch (error) {
        ConnectionLogger.error(error);
        return undefined;
    }
}
exports.uriToPath = uriToPath;
;
function pathToUri(path) {
    try {
        return (vscode_uri_1.URI.file(path).toString());
    }
    catch (error) {
        ConnectionLogger.error(error);
        return undefined;
    }
}
exports.pathToUri = pathToUri;
function resolvedPath(file) {
    try {
        return path.resolve(__dirname, file);
    }
    catch (error) {
        ConnectionLogger.error(error);
        return undefined;
    }
}
exports.resolvedPath = resolvedPath;
function getTmpDirSync() {
    try {
        return tmp.dirSync({ unsafeCleanup: true });
    }
    catch (error) {
        ConnectionLogger.error(error);
        return undefined;
    }
}
exports.getTmpDirSync = getTmpDirSync;
function isStringListEqual(stringList1, stringList2) {
    try {
        if ((stringList1 == undefined) && (stringList2 == undefined)) {
            return true;
        }
        else if (stringList1 == undefined) {
            return false;
        }
        else if (stringList2 == undefined) {
            return false;
        }
        else if (stringList1.length != stringList2.length) {
            return false;
        }
        let sortedStringList1 = stringList1.sort((one, two) => (one > two ? -1 : 1));
        let sortedStringList2 = stringList1.sort((one, two) => (one > two ? -1 : 1));
        for (let i = 0; i < stringList1.length; i++) {
            if (sortedStringList1[i] != sortedStringList2[i]) {
                return false;
            }
        }
        return true;
    }
    catch (error) {
        ConnectionLogger.error(error);
        return false;
    }
}
exports.isStringListEqual = isStringListEqual;
class ConnectionLogger {
    static sendNotification(type, message) {
        if (!ConnectionLogger._connection) {
            switch (type) {
                case node_1.MessageType.Error: {
                    console.error(message);
                    break;
                }
                default: {
                    console.log(message);
                    break;
                }
            }
        }
        else {
            try {
                ConnectionLogger._connection.sendNotification(node_1.LogMessageNotification.type, { type: type, message: message });
            }
            catch (error) {
                console.error(error);
            }
        }
    }
    static setConnection(connection) {
        ConnectionLogger._connection = connection;
    }
    static log(message, prefix = true) {
        ConnectionLogger.sendNotification(node_1.MessageType.Log, `${prefix ? "INFO: " : ""}${message}`);
    }
    static warn(message, prefix = true) {
        ConnectionLogger.sendNotification(node_1.MessageType.Warning, `${prefix ? "WARNING: " : ""}${message}`);
    }
    static error(message, prefix = true) {
        ConnectionLogger.sendNotification(node_1.MessageType.Error, `${prefix ? "ERROR: " : ""}${message}`);
    }
}
exports.ConnectionLogger = ConnectionLogger;
ConnectionLogger._connection = null;
function childProcessStdoutRedir(data) {
    try {
        let message = data.toString();
        if (message.startsWith("WARNING: ")) {
            ConnectionLogger.warn(message, false);
        }
        else {
            ConnectionLogger.log(message, false);
        }
    }
    catch (error) {
        ConnectionLogger.error(error, false);
        return;
    }
}
exports.childProcessStdoutRedir = childProcessStdoutRedir;
function childProcessStderrRedir(data) {
    try {
        let message = data.toString();
        ConnectionLogger.error(message, false);
    }
    catch (error) {
        ConnectionLogger.error(error, false);
        return;
    }
}
exports.childProcessStderrRedir = childProcessStderrRedir;
class DelayedCaller {
    constructor(timeOut = 1000) {
        this._callWaiting = new Map();
        this._timeOut = timeOut;
    }
    run(callName, successCallback, errorCallback) {
        if (this._callWaiting.has(callName)) {
            let [waitTimer, resolver] = this._callWaiting.get(callName);
            clearTimeout(waitTimer);
            resolver(false);
        }
        return new Promise(resolve => {
            this._callWaiting.set(callName, [setTimeout(resolve, this._timeOut, true), resolve]);
        }).then((success) => {
            if (!!success) {
                this._callWaiting.delete(callName);
            }
            return successCallback(!!success);
        }).catch(error => {
            return errorCallback(error);
        });
    }
}
exports.DelayedCaller = DelayedCaller;
class ChildProcManager {
    constructor() {
        this._alreadyRunning = new Map();
    }
    kill(key) {
        if (this._alreadyRunning.has(key)) {
            //ConnectionLogger.log(`DEBUG: Killing already running command to start a new one`);
            let [proc, statusRef] = this._alreadyRunning.get(key);
            statusRef[0] = false;
            proc.kill();
        }
    }
    run(key, command, callBack) {
        let statusRef = [true];
        this._alreadyRunning.set(key, [
            child.exec(command, (error, stdout, stderr) => {
                if (statusRef[0]) {
                    this._alreadyRunning.delete(key);
                }
                callBack(statusRef[0], error, stdout, stderr);
            }),
            statusRef
        ]);
    }
}
exports.ChildProcManager = ChildProcManager;
class TmpFileManager {
    constructor() {
        this._freeTmpFileNums = new Map();
        this._tmpDir = getTmpDirSync();
    }
    getTmpFilePath(...elems) {
        return path.join(this._tmpDir.name, ...elems);
    }
    cleanupTmpFiles() {
        this._tmpDir.removeCallback();
    }
    getFreeTmpFileNum(key) {
        if (!this._freeTmpFileNums.has(key)) {
            this._freeTmpFileNums.set(key, { total: 0, nums: [] });
        }
        if (this._freeTmpFileNums.get(key).nums.length <= 0) {
            this._freeTmpFileNums.get(key).nums.push(this._freeTmpFileNums.get(key).total++);
        }
        return this._freeTmpFileNums.get(key).nums.shift();
    }
    returnFreeTmpFileNum(key, tmpFileNum) {
        if (this._freeTmpFileNums.has(key)) {
            this._freeTmpFileNums.get(key).nums.push(tmpFileNum);
        }
        else {
            ConnectionLogger.error(`FreeTmpFileNums doesn't have the key ${key}`);
        }
    }
}
exports.tmpFileManager = new TmpFileManager();
