"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stripComments = stripComments;
exports.doesCurrentNpmCommandMatch = doesCurrentNpmCommandMatch;
exports.someWithRegExps = someWithRegExps;
exports.getCurrentNpmCommandArgv = getCurrentNpmCommandArgv;
exports.isInstallingNativeScriptGlobally = isInstallingNativeScriptGlobally;
exports.createRegExp = createRegExp;
exports.regExpEscape = regExpEscape;
exports.getShortPluginName = getShortPluginName;
exports.executeActionByChunks = executeActionByChunks;
exports.deferPromise = deferPromise;
exports.settlePromises = settlePromises;
exports.getPropertyName = getPropertyName;
exports.quoteString = quoteString;
exports.createGUID = createGUID;
exports.stringReplaceAll = stringReplaceAll;
exports.isRequestSuccessful = isRequestSuccessful;
exports.isResponseRedirect = isResponseRedirect;
exports.formatListOfNames = formatListOfNames;
exports.getRelativeToRootPath = getRelativeToRootPath;
exports.setIsInteractive = setIsInteractive;
exports.isInteractive = isInteractive;
exports.toBoolean = toBoolean;
exports.block = block;
exports.isNumberWithoutExponent = isNumberWithoutExponent;
exports.fromWindowsRelativePathToUnix = fromWindowsRelativePathToUnix;
exports.isNullOrWhitespace = isNullOrWhitespace;
exports.getCurrentEpochTime = getCurrentEpochTime;
exports.sleep = sleep;
exports.createTable = createTable;
exports.getMessageWithBorders = getMessageWithBorders;
exports.remove = remove;
exports.trimSymbol = trimSymbol;
exports.parseJson = parseJson;
exports.getFuturesResults = getFuturesResults;
exports.appendZeroesToVersion = appendZeroesToVersion;
exports.decorateMethod = decorateMethod;
exports.hook = hook;
exports.isPromise = isPromise;
exports.attachAwaitDetach = attachAwaitDetach;
exports.connectEventually = connectEventually;
exports.getHash = getHash;
exports.connectEventuallyUntilTimeout = connectEventuallyUntilTimeout;
exports.getProjectFilesConfig = getProjectFilesConfig;
exports.getPidFromiOSSimulatorLogs = getPidFromiOSSimulatorLogs;
exports.getValueFromNestedObject = getValueFromNestedObject;
exports.getWinRegPropertyValue = getWinRegPropertyValue;
exports.stringify = stringify;
exports.getFixedLengthDateString = getFixedLengthDateString;
exports.getFormattedDateComponent = getFormattedDateComponent;
exports.getFormattedMilliseconds = getFormattedMilliseconds;
exports.annotate = annotate;
exports.hasValidAndroidSigning = hasValidAndroidSigning;
const uuid_1 = require("uuid");
const os_1 = require("os");
const constants_1 = require("./constants");
const crypto = require("crypto");
const _ = require("lodash");
const Table = require("cli-table3");
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
function stripComments(content) {
    const newContent = content.replace(STRIP_COMMENTS, "");
    return newContent;
}
function doesCurrentNpmCommandMatch(patterns) {
    const currentNpmCommandArgv = getCurrentNpmCommandArgv();
    let result = false;
    if (currentNpmCommandArgv.length) {
        result = someWithRegExps(currentNpmCommandArgv, patterns);
    }
    return result;
}
function someWithRegExps(array, patterns) {
    return _.some(array, (item) => _.some(patterns, (pattern) => !!item.match(pattern)));
}
function getCurrentNpmCommandArgv() {
    let result = [];
    if (process.env && process.env.npm_config_argv) {
        try {
            const npmConfigArgv = JSON.parse(process.env.npm_config_argv);
            result = npmConfigArgv.original || [];
        }
        catch (error) {
        }
    }
    return result;
}
function isInstallingNativeScriptGlobally() {
    return (isInstallingNativeScriptGloballyWithNpm() ||
        isInstallingNativeScriptGloballyWithYarn());
}
function isInstallingNativeScriptGloballyWithNpm() {
    const isInstallCommand = doesCurrentNpmCommandMatch([/^install$/, /^i$/]);
    const isGlobalCommand = doesCurrentNpmCommandMatch([/^--global$/, /^-g$/]);
    const hasNativeScriptPackage = doesCurrentNpmCommandMatch([
        /^nativescript(@.*)?$/,
        /nativescript-(.*)\.tgz?$/,
    ]);
    return isInstallCommand && isGlobalCommand && hasNativeScriptPackage;
}
function isInstallingNativeScriptGloballyWithYarn() {
    const isInstallCommand = doesCurrentNpmCommandMatch([/^add$/]);
    const isGlobalCommand = doesCurrentNpmCommandMatch([/^global$/]);
    const hasNativeScriptPackage = doesCurrentNpmCommandMatch([
        /^nativescript(@.*)?$/,
        /nativescript-(.*)\.tgz?$/,
    ]);
    return isInstallCommand && isGlobalCommand && hasNativeScriptPackage;
}
function createRegExp(input, opts) {
    if (!input || !_.isString(input)) {
        throw new Error("Input must be a string.");
    }
    const escapedSource = regExpEscape(input);
    return new RegExp(escapedSource, opts);
}
function regExpEscape(input) {
    return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function getShortPluginName(pluginName) {
    return sanitizePluginName(pluginName).replace(/[\-]/g, "_");
}
function sanitizePluginName(pluginName) {
    return pluginName.split("/").pop();
}
async function executeActionByChunks(initialData, chunkSize, elementAction) {
    let arrayToChunk;
    let action;
    if (_.isArray(initialData)) {
        arrayToChunk = initialData;
        action = (element) => elementAction(element, initialData.indexOf(element));
    }
    else {
        arrayToChunk = _.keys(initialData);
        action = (key) => elementAction(initialData[key], key);
    }
    const chunks = _.chunk(arrayToChunk, chunkSize);
    for (const chunk of chunks) {
        await Promise.all(_.map(chunk, (element) => action(element)));
    }
}
function deferPromise() {
    let resolve;
    let reject;
    let isResolved = false;
    let isRejected = false;
    let promise;
    let result;
    promise = new Promise((innerResolve, innerReject) => {
        resolve = (value) => {
            isResolved = true;
            result = value;
            return innerResolve(value);
        };
        reject = (reason) => {
            isRejected = true;
            return innerReject(reason);
        };
    });
    return {
        promise,
        resolve,
        reject,
        isResolved: () => isResolved,
        isRejected: () => isRejected,
        isPending: () => !isResolved && !isRejected,
        getResult: () => result,
    };
}
function settlePromises(promises) {
    return new Promise((resolve, reject) => {
        let settledPromisesCount = 0;
        const results = [];
        const errors = [];
        const length = promises.length;
        if (!promises.length) {
            resolve(null);
        }
        _.forEach(promises, (currentPromise, index) => {
            currentPromise
                .then((result) => {
                results[index] = result;
            })
                .catch((err) => {
                errors.push(err);
            })
                .then(() => {
                settledPromisesCount++;
                if (settledPromisesCount === length) {
                    errors.length
                        ? reject(new Error(`Multiple errors were thrown:${os_1.EOL}${errors
                            .map((e) => e.message || e)
                            .join(os_1.EOL)}`))
                        : resolve(results);
                }
            })
                .catch();
        });
    });
}
function getPropertyName(func) {
    if (func) {
        const match = func
            .toString()
            .match(/(?:return\s+?.*\.(.+);)|(?:=>\s*?.*\.(.+)\b)/);
        if (match) {
            return (match[1] || match[2]).trim();
        }
    }
    return null;
}
function bashQuote(s) {
    if (s[0] === "'" && s[s.length - 1] === "'") {
        return s;
    }
    return "'" + s.replace(/'/g, "'\"'\"'") + "'";
}
function cmdQuote(s) {
    if (s[0] === '"' && s[s.length - 1] === '"') {
        return s;
    }
    return '"' + s.replace(/"/g, '\\"') + '"';
}
function quoteString(s) {
    if (!s) {
        return s;
    }
    return (0, os_1.platform)() === "win32" ? cmdQuote(s) : bashQuote(s);
}
function createGUID(useBraces) {
    let output;
    useBraces = useBraces === undefined ? true : useBraces;
    if (useBraces) {
        output = "{" + (0, uuid_1.v4)() + "}";
    }
    else {
        output = (0, uuid_1.v4)();
    }
    return output;
}
function stringReplaceAll(inputString, find, replace) {
    return inputString.split(find).join(replace);
}
function isRequestSuccessful(request) {
    return request.statusCode >= 200 && request.statusCode < 300;
}
function isResponseRedirect(response) {
    return _.includes([301, 302, 303, 307, 308], response.statusCode);
}
function formatListOfNames(names, conjunction) {
    conjunction = conjunction === undefined ? "or" : conjunction;
    if (names.length <= 1) {
        return names[0];
    }
    else {
        return (_.initial(names).join(", ") +
            " " +
            conjunction +
            " " +
            names[names.length - 1]);
    }
}
function getRelativeToRootPath(rootPath, filePath) {
    const relativeToRootPath = filePath.substr(rootPath.length);
    return relativeToRootPath;
}
let customIsInteractive;
function setIsInteractive(override) {
    customIsInteractive = override;
}
function isInteractive() {
    if (customIsInteractive) {
        return customIsInteractive();
    }
    const result = isRunningInTTY() && !isCIEnvironment();
    return result;
}
function isRunningInTTY() {
    return (process.stdout &&
        process.stdout.isTTY &&
        process.stdin &&
        process.stdin.isTTY);
}
function isCIEnvironment() {
    return !!(process.env && (process.env.CI || process.env.JENKINS_HOME));
}
function toBoolean(str) {
    return !!(str && str.toString && str.toString().toLowerCase() === "true");
}
function block(operation) {
    if (isInteractive()) {
        process.stdin.setRawMode(false);
    }
    operation();
    if (isInteractive()) {
        process.stdin.setRawMode(true);
    }
}
function isNumberWithoutExponent(n) {
    const parsedNum = parseFloat(n);
    return (!isNaN(parsedNum) &&
        isFinite(n) &&
        n.toString &&
        n.toString() === parsedNum.toString());
}
function fromWindowsRelativePathToUnix(windowsRelativePath) {
    return windowsRelativePath.replace(/\\/g, "/");
}
function isNullOrWhitespace(input) {
    if (!input && input !== false) {
        return true;
    }
    return _.isString(input) && input.replace(/\s/gi, "").length < 1;
}
function getCurrentEpochTime() {
    const dateTime = new Date();
    return dateTime.getTime();
}
async function sleep(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(async () => resolve(), ms);
    });
}
function createTable(headers, data) {
    const table = new Table({
        head: headers,
        chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
    });
    _.forEach(data, (row) => table.push(row));
    return table;
}
function getMessageWithBorders(message, spanLength = 3) {
    if (!message) {
        return "";
    }
    const longestRowLength = message.split("\n").sort((a, b) => {
        return b.length - a.length;
    })[0].length;
    let border = "*".repeat(longestRowLength + 2 * spanLength);
    if (border.length % 2 === 0) {
        border += "*";
    }
    border = border.replace(/\*\*/g, "* ");
    const formatRow = function (row) {
        return (_.padEnd("*", spanLength) +
            _.padEnd(row, border.length - 2 * spanLength) +
            _.padStart("*", spanLength) +
            os_1.EOL);
    };
    const emptyRow = formatRow("");
    const messageWithBorders = [];
    messageWithBorders.push(os_1.EOL, border + os_1.EOL, emptyRow, ...message.split("\n").map((row) => formatRow(row.trim())), emptyRow, border + os_1.EOL, os_1.EOL);
    return messageWithBorders.join("");
}
function remove(array, predicate, numberOfElements) {
    numberOfElements = numberOfElements || 1;
    const index = _.findIndex(array, predicate);
    if (index === -1) {
        return new Array();
    }
    return array.splice(index, numberOfElements);
}
function trimSymbol(str, symbol) {
    while (str.charAt(0) === symbol) {
        str = str.substr(1);
    }
    while (str.charAt(str.length - 1) === symbol) {
        str = str.substr(0, str.length - 1);
    }
    return str;
}
function parseJson(data) {
    return JSON.parse(data.replace(/^\uFEFF/, ""));
}
async function getFuturesResults(promises, predicate) {
    const results = await Promise.all(promises);
    return _(results).filter(predicate).flatten().value();
}
function appendZeroesToVersion(version, requiredVersionLength) {
    if (version) {
        const zeroesToAppend = requiredVersionLength - version.split(".").length;
        for (let index = 0; index < zeroesToAppend; index++) {
            version += ".0";
        }
    }
    return version;
}
function decorateMethod(before, after) {
    return (target, propertyKey, descriptor) => {
        const sink = descriptor.value;
        descriptor.value = async function (...args) {
            let newMethods = null;
            if (before) {
                newMethods = await before(sink, this, args);
            }
            let hasBeenReplaced = false;
            let result;
            if (newMethods && newMethods.length) {
                const replacementMethods = _.filter(newMethods, (f) => _.isFunction(f));
                if (replacementMethods.length > 0) {
                    hasBeenReplaced = true;
                    const chainedReplacementMethod = _.reduce(replacementMethods, (prev, next) => next.bind(next, args, prev), sink.bind(this));
                    result = chainedReplacementMethod();
                }
            }
            if (!hasBeenReplaced) {
                result = sink.apply(this, args);
            }
            if (after) {
                return await after(sink, this, result, args);
            }
            return result;
        };
    };
}
function hook(commandName) {
    function getHooksService(self) {
        let hooksService = self.$hooksService;
        if (!hooksService) {
            const injector = self.$injector;
            if (!injector) {
                throw Error("Type with hooks needs to have either $hooksService or $injector injected.");
            }
            hooksService = injector.resolve("hooksService");
        }
        return hooksService;
    }
    function prepareArguments(method, args, hooksService) {
        annotate(method);
        const argHash = {};
        for (let i = 0; i < method.$inject.args.length; ++i) {
            argHash[method.$inject.args[i]] = args[i];
        }
        argHash.$arguments = args;
        const result = {};
        result[hooksService.hookArgsName] = argHash;
        return result;
    }
    return decorateMethod(async (method, self, args) => {
        const hooksService = getHooksService(self);
        return hooksService.executeBeforeHooks(commandName, prepareArguments(method, args, hooksService));
    }, async (method, self, resultPromise, args) => {
        const result = await resultPromise;
        const hooksService = getHooksService(self);
        await hooksService.executeAfterHooks(commandName, prepareArguments(method, args, hooksService));
        return Promise.resolve(result);
    });
}
function isPromise(candidateFuture) {
    return !!(candidateFuture && typeof candidateFuture.then === "function");
}
async function attachAwaitDetach(eventName, eventEmitter, eventHandler, operation) {
    eventEmitter.on(eventName, eventHandler);
    try {
        await operation;
    }
    finally {
        eventEmitter.removeListener(eventName, eventHandler);
    }
}
async function connectEventually(factory, handler) {
    async function tryConnect() {
        const tryConnectAfterTimeout = setTimeout.bind(undefined, tryConnect, 1000);
        const socket = await factory();
        socket.on("connect", () => {
            socket.removeListener("error", tryConnectAfterTimeout);
            handler(socket);
        });
        socket.on("error", tryConnectAfterTimeout);
    }
    await tryConnect();
}
function getHash(str, options) {
    return crypto
        .createHash((options && options.algorithm) || "sha256")
        .update(str)
        .digest((options && options.encoding) || "hex");
}
async function connectEventuallyUntilTimeout(factory, timeout) {
    return new Promise(async (resolve, reject) => {
        let lastKnownError;
        let isResolved = false;
        const connectionTimer = setTimeout(function () {
            if (!isResolved) {
                isResolved = true;
                reject(lastKnownError || new Error(`Unable to connect for ${timeout}ms`));
            }
        }, timeout);
        async function tryConnect() {
            const tryConnectAfterTimeout = (error) => {
                if (isResolved) {
                    return;
                }
                lastKnownError = error;
                setTimeout(tryConnect, 1000);
            };
            try {
                const socket = await factory();
                socket.on("connect", () => {
                    socket.removeListener("error", tryConnectAfterTimeout);
                    isResolved = true;
                    clearTimeout(connectionTimer);
                    resolve(socket);
                });
                socket.on("error", tryConnectAfterTimeout);
            }
            catch (e) {
                lastKnownError = e;
                tryConnectAfterTimeout(e);
            }
        }
        await tryConnect();
    });
}
function getProjectFilesConfig(opts) {
    const projectFilesConfig = {
        configuration: opts.isReleaseBuild
            ? constants_1.Configurations.Release.toLowerCase()
            : constants_1.Configurations.Debug.toLowerCase(),
    };
    return projectFilesConfig;
}
function getPidFromiOSSimulatorLogs(applicationIdentifier, logLine) {
    if (logLine) {
        const pidRegExp = new RegExp(`${applicationIdentifier}:\\s?(\\d+)`);
        const pidMatch = logLine.match(pidRegExp);
        return pidMatch ? pidMatch[1] : null;
    }
    return null;
}
function getValueFromNestedObject(obj, key) {
    function _getValueRecursive(_obj, _key) {
        if (_.has(_obj, _key)) {
            return [_obj];
        }
        const res = [];
        _.forEach(_obj, (v, k) => {
            if (typeof v === "object" &&
                typeof k === "string" &&
                !_.startsWith(k, "$") &&
                !_.endsWith(k.toLowerCase(), "service") &&
                (v = _getValueRecursive(v, _key)).length) {
                res.push.apply(res, v);
            }
        });
        return res;
    }
    return _.head(_getValueRecursive(obj, key));
}
function getWinRegPropertyValue(key, propertyName) {
    return new Promise((resolve, reject) => {
        const Winreg = require("winreg");
        const regKey = new Winreg({
            hive: Winreg.HKLM,
            key: key,
        });
        regKey.get(propertyName, (err, value) => {
            if (err) {
                reject(err);
            }
            else {
                resolve(value);
            }
        });
    });
}
function stringify(value, replacer, space) {
    return JSON.stringify(value, replacer, space || 2);
}
function getFixedLengthDateString() {
    const currentDate = new Date();
    const year = currentDate.getFullYear();
    const month = getFormattedDateComponent(currentDate.getMonth() + 1);
    const day = getFormattedDateComponent(currentDate.getDate());
    const hour = getFormattedDateComponent(currentDate.getHours());
    const minutes = getFormattedDateComponent(currentDate.getMinutes());
    const seconds = getFormattedDateComponent(currentDate.getSeconds());
    const milliseconds = getFormattedMilliseconds(currentDate);
    return `${[year, month, day].join("-")} ${[hour, minutes, seconds].join(":")}.${milliseconds}`;
}
function getFormattedDateComponent(component) {
    const stringComponent = component.toString();
    return stringComponent.length === 1 ? `0${stringComponent}` : stringComponent;
}
function getFormattedMilliseconds(date) {
    let milliseconds = date.getMilliseconds().toString();
    while (milliseconds.length < 3) {
        milliseconds = `0${milliseconds}`;
    }
    return milliseconds;
}
const CLASS_NAME = /class\s+([A-Z].+?)(?:\s+.*?)?\{/;
const CONSTRUCTOR_ARGS = /constructor\s*([^\(]*)\(\s*([^\)]*)\)/m;
const FN_NAME_AND_ARGS = /^(?:function)?\s*([^\(]*)\(\s*([^\)]*)\)\s*(=>)?\s*[{_]/m;
const FN_ARG_SPLIT = /,/;
const FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
function annotate(fn) {
    let $inject, fnText, argDecl;
    if (typeof fn === "function") {
        if (!($inject = fn.$inject) || $inject.name !== fn.name) {
            $inject = { args: [], name: "" };
            fnText = fn.toString().replace(STRIP_COMMENTS, "");
            let nameMatch = fnText.match(CLASS_NAME);
            if (nameMatch) {
                argDecl = fnText.match(CONSTRUCTOR_ARGS);
            }
            else {
                nameMatch = argDecl = fnText.match(FN_NAME_AND_ARGS);
            }
            $inject.name = nameMatch && nameMatch[1];
            if (argDecl && fnText.length) {
                argDecl[2].split(FN_ARG_SPLIT).forEach((arg) => {
                    arg.replace(FN_ARG, (all, underscore, name) => $inject.args.push(name));
                });
            }
            fn.$inject = $inject;
        }
    }
    return $inject;
}
function hasValidAndroidSigning(signingData) {
    const isValid = signingData &&
        signingData.keyStorePath &&
        signingData.keyStorePassword &&
        signingData.keyStoreAlias &&
        signingData.keyStoreAliasPassword;
    return !!isValid;
}
