"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AndroidGenymotionService = void 0;
const constants_1 = require("../../../constants");
const helpers_1 = require("../../../helpers");
const os_1 = require("os");
const path = require("path");
const _ = require("lodash");
const osenv = require("osenv");
const decorators_1 = require("../../../decorators");
const constants_2 = require("../../../../constants");
const yok_1 = require("../../../yok");
class AndroidGenymotionService {
    constructor($adb, $childProcess, $devicePlatformsConstants, $emulatorHelper, $fs, $logger, $virtualBoxService) {
        this.$adb = $adb;
        this.$childProcess = $childProcess;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$emulatorHelper = $emulatorHelper;
        this.$fs = $fs;
        this.$logger = $logger;
        this.$virtualBoxService = $virtualBoxService;
    }
    getEmulatorImages(adbDevicesOutput) {
        return __awaiter(this, void 0, void 0, function* () {
            const availableEmulatorsOutput = yield this.getEmulatorImagesCore();
            const runningEmulatorIds = yield this.getRunningEmulatorIds(adbDevicesOutput);
            const runningEmulators = yield (0, helpers_1.settlePromises)(_.map(runningEmulatorIds, (emulatorId) => this.getRunningEmulatorData(emulatorId, availableEmulatorsOutput.devices)));
            const devices = availableEmulatorsOutput.devices.map((emulator) => this.$emulatorHelper.getEmulatorByImageIdentifier(emulator.imageIdentifier, runningEmulators) || emulator);
            return {
                devices,
                errors: availableEmulatorsOutput.errors,
            };
        });
    }
    getRunningEmulatorIds(adbDevicesOutput) {
        return __awaiter(this, void 0, void 0, function* () {
            const results = yield Promise.all(_(adbDevicesOutput)
                .filter((r) => !r.match(constants_1.AndroidVirtualDevice.RUNNING_AVD_EMULATOR_REGEX))
                .map((row) => __awaiter(this, void 0, void 0, function* () {
                const match = row.match(/^(.+?)\s+device$/);
                if (match && match[1]) {
                    const emulatorId = match[1];
                    const result = (yield this.isGenymotionEmulator(emulatorId))
                        ? emulatorId
                        : undefined;
                    return Promise.resolve(result);
                }
                return Promise.resolve(undefined);
            }))
                .value());
            return _(results)
                .filter((r) => !!r)
                .map((r) => r.toString())
                .value();
        });
    }
    get pathToEmulatorExecutable() {
        const searchPaths = this.playerSearchPaths[process.platform];
        const searchPath = _.find(searchPaths, (sPath) => this.$fs.exists(sPath));
        return searchPath || "player";
    }
    startEmulatorArgs(imageIdentifier) {
        return ["--vm-name", imageIdentifier];
    }
    getEmulatorImagesCore() {
        return __awaiter(this, void 0, void 0, function* () {
            const output = yield this.$virtualBoxService.listVms();
            if (output.error) {
                return { devices: [], errors: output.error ? [output.error] : [] };
            }
            const devices = yield this.parseListVmsOutput(output.vms);
            return { devices, errors: [] };
        });
    }
    getRunningEmulatorName(emulatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            const output = yield this.$adb.getPropertyValue(emulatorId, "ro.product.model");
            this.$logger.trace(output);
            return _.first(output.split(os_1.EOL)).trim();
        });
    }
    getRunningEmulatorImageIdentifier(emulatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            const adbDevices = yield this.$adb.getDevicesSafe();
            const emulatorImages = (yield this.getEmulatorImages(adbDevices)).devices;
            const emulator = yield this.getRunningEmulatorData(emulatorId, emulatorImages);
            return emulator ? emulator.imageIdentifier : null;
        });
    }
    getRunningEmulatorData(runningEmulatorId, availableEmulators) {
        return __awaiter(this, void 0, void 0, function* () {
            const emulatorName = yield this.getRunningEmulatorName(runningEmulatorId);
            const runningEmulator = this.$emulatorHelper.getEmulatorByIdOrName(emulatorName, availableEmulators);
            if (!runningEmulator) {
                return null;
            }
            this.$emulatorHelper.setRunningAndroidEmulatorProperties(runningEmulatorId, runningEmulator);
            return runningEmulator;
        });
    }
    get playerSearchPaths() {
        return {
            darwin: [
                "/Applications/Genymotion.app/Contents/MacOS/player.app/Contents/MacOS/player",
                "/Applications/Genymotion.app/Contents/MacOS/player",
            ],
            linux: [path.join(osenv.home(), "genymotion", "player")],
            win32: [
                `${process.env["PROGRAMFILES"]}\\Genymobile\\Genymotion\\player.exe`,
                `${process.env["PROGRAMFILES(X86)"]}\\Genymobile\\Genymotion\\player.exe`,
            ],
        };
    }
    parseListVmsOutput(vms) {
        return __awaiter(this, void 0, void 0, function* () {
            const configurationError = yield this.getConfigurationError();
            const devices = [];
            for (const vm of vms) {
                try {
                    const output = yield this.$virtualBoxService.enumerateGuestProperties(vm.id);
                    if (output &&
                        output.properties &&
                        output.properties.indexOf("genymotion") !== -1) {
                        devices.push(this.convertToDeviceInfo(output.properties, vm.id, vm.name, output.error, configurationError));
                    }
                }
                catch (err) {
                    this.$logger.trace(`Error while parsing vm ${vm.id}`);
                }
            }
            return devices;
        });
    }
    convertToDeviceInfo(output, id, name, error, configurationError) {
        return {
            identifier: null,
            imageIdentifier: id,
            displayName: name,
            model: name,
            version: this.getSdkVersion(output),
            vendor: constants_1.AndroidVirtualDevice.GENYMOTION_VENDOR_NAME,
            status: constants_1.NOT_RUNNING_EMULATOR_STATUS,
            errorHelp: [configurationError, error].filter((item) => !!item).join(os_1.EOL) || null,
            isTablet: false,
            type: constants_1.DeviceTypes.Emulator,
            connectionTypes: [constants_2.DeviceConnectionType.Local],
            platform: this.$devicePlatformsConstants.Android,
        };
    }
    getSdkVersion(output) {
        const androidApiLevelRow = output
            .split("\n")
            .filter((row) => !!row)
            .find((row) => row.indexOf("Name: android_version") !== -1);
        return androidApiLevelRow.split(", ")[1].split("value: ")[1];
    }
    isGenymotionEmulator(emulatorId) {
        return __awaiter(this, void 0, void 0, function* () {
            const manufacturer = yield this.$adb.getPropertyValue(emulatorId, "ro.product.manufacturer");
            if (manufacturer && manufacturer.match(/^Genymotion/i)) {
                return true;
            }
            const buildProduct = yield this.$adb.getPropertyValue(emulatorId, "ro.build.product");
            if (buildProduct && _.includes(buildProduct.toLowerCase(), "vbox")) {
                return true;
            }
            return false;
        });
    }
    getConfigurationPlatformSpecficErrorMessage() {
        const searchPaths = this.playerSearchPaths[process.platform];
        return `Unable to find the Genymotion player in the following location${searchPaths.length > 1 ? "s" : ""}:
${searchPaths.join(os_1.EOL)}
In case you have installed Genymotion in a different location, please add the path to player executable to your PATH environment variable.`;
    }
    getConfigurationError() {
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield this.$childProcess.trySpawnFromCloseEvent(this.pathToEmulatorExecutable, [], {}, { throwError: false });
            if (result &&
                result.stderr &&
                result.stderr.indexOf(constants_1.AndroidVirtualDevice.GENYMOTION_DEFAULT_STDERR_STRING) === -1) {
                this.$logger.trace("Configuration error for Genymotion", result);
                return this.getConfigurationPlatformSpecficErrorMessage();
            }
            return null;
        });
    }
}
__decorate([
    (0, decorators_1.cache)()
], AndroidGenymotionService.prototype, "getConfigurationPlatformSpecficErrorMessage", null);
__decorate([
    (0, decorators_1.cache)()
], AndroidGenymotionService.prototype, "getConfigurationError", null);
exports.AndroidGenymotionService = AndroidGenymotionService;
yok_1.injector.register("androidGenymotionService", AndroidGenymotionService);
