"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};
const helpers_1 = require("../common/helpers");
class EmulatorPlatformService {
    constructor($mobileHelper, $childProcess, $devicesService, $options, $logger, $androidEmulatorServices) {
        this.$mobileHelper = $mobileHelper;
        this.$childProcess = $childProcess;
        this.$devicesService = $devicesService;
        this.$options = $options;
        this.$logger = $logger;
        this.$androidEmulatorServices = $androidEmulatorServices;
    }
    startEmulator(info, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!info.isRunning) {
                if (this.$mobileHelper.isAndroidPlatform(info.platform)) {
                    this.$options.avd = this.$options.device;
                    this.$options.device = null;
                    let platformsData = $injector.resolve("platformsData");
                    let platformData = platformsData.getPlatformData(info.platform, projectData);
                    let emulatorServices = platformData.emulatorServices;
                    emulatorServices.checkAvailability();
                    yield emulatorServices.checkDependencies();
                    yield emulatorServices.startEmulator();
                    this.$options.avd = null;
                    return;
                }
                if (this.$mobileHelper.isiOSPlatform(info.platform)) {
                    yield this.stopEmulator(info.platform);
                    let deferred = helpers_1.deferPromise();
                    yield this.$childProcess.exec(`open -a Simulator --args -CurrentDeviceUDID ${info.id}`);
                    let timeoutFunc = () => __awaiter(this, void 0, void 0, function* () {
                        info = yield this.getEmulatorInfo("ios", info.id);
                        if (info.isRunning) {
                            yield this.$devicesService.initialize({ platform: info.platform, deviceId: info.id });
                            let device = this.$devicesService.getDeviceByIdentifier(info.id);
                            yield device.applicationManager.checkForApplicationUpdates();
                            deferred.resolve();
                            return;
                        }
                        setTimeout(timeoutFunc, 2000);
                    });
                    yield timeoutFunc();
                    return deferred.promise;
                }
            }
        });
    }
    stopEmulator(platform) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.$mobileHelper.isiOSPlatform(platform)) {
                yield this.$childProcess.exec("pkill -9 -f Simulator");
            }
        });
    }
    getEmulatorInfo(platform, idOrName) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.$mobileHelper.isAndroidPlatform(platform)) {
                let androidEmulators = this.getAndroidEmulators();
                let found = androidEmulators.filter((info) => info.id === idOrName);
                if (found.length > 0) {
                    return found[0];
                }
                yield this.$devicesService.initialize({ platform: platform, deviceId: null, skipInferPlatform: true });
                let info = null;
                let action = (device) => __awaiter(this, void 0, void 0, function* () {
                    if (device.deviceInfo.identifier === idOrName) {
                        info = {
                            id: device.deviceInfo.identifier,
                            name: device.deviceInfo.displayName,
                            version: device.deviceInfo.version,
                            platform: "Android",
                            type: "emulator",
                            isRunning: true
                        };
                    }
                });
                yield this.$devicesService.execute(action, undefined, { allowNoDevices: true });
                return info;
            }
            if (this.$mobileHelper.isiOSPlatform(platform)) {
                let emulators = yield this.getiOSEmulators();
                let sdk = null;
                let versionStart = idOrName.indexOf("(");
                if (versionStart > 0) {
                    sdk = idOrName.substring(versionStart + 1, idOrName.indexOf(")", versionStart)).trim();
                    idOrName = idOrName.substring(0, versionStart - 1).trim();
                }
                let found = emulators.filter((info) => {
                    let sdkMatch = sdk ? info.version === sdk : true;
                    return sdkMatch && info.id === idOrName || info.name === idOrName;
                });
                return found.length > 0 ? found[0] : null;
            }
            return null;
        });
    }
    listAvailableEmulators(platform) {
        return __awaiter(this, void 0, void 0, function* () {
            let emulators = [];
            if (!platform || this.$mobileHelper.isiOSPlatform(platform)) {
                let iosEmulators = yield this.getiOSEmulators();
                if (iosEmulators) {
                    emulators = emulators.concat(iosEmulators);
                }
            }
            if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) {
                let androidEmulators = this.getAndroidEmulators();
                if (androidEmulators) {
                    emulators = emulators.concat(androidEmulators);
                }
            }
            this.outputEmulators("\nAvailable emulators", emulators);
            this.$logger.out("\nConnected devices & emulators");
            yield $injector.resolveCommand("device").execute(platform ? [platform] : []);
        });
    }
    getiOSEmulators() {
        return __awaiter(this, void 0, void 0, function* () {
            let output = yield this.$childProcess.exec("xcrun simctl list --json");
            let list = JSON.parse(output);
            let emulators = [];
            for (let osName in list["devices"]) {
                if (osName.indexOf("iOS") === -1) {
                    continue;
                }
                let os = list["devices"][osName];
                let version = this.parseiOSVersion(osName);
                for (let device of os) {
                    if (device["availability"] !== "(available)") {
                        continue;
                    }
                    let emulatorInfo = {
                        id: device["udid"],
                        name: device["name"],
                        isRunning: device["state"] === "Booted",
                        type: "simulator",
                        version: version,
                        platform: "iOS"
                    };
                    emulators.push(emulatorInfo);
                }
            }
            return emulators;
        });
    }
    getAndroidEmulators() {
        const androidVirtualDevices = this.$androidEmulatorServices.getAvds().map(avd => this.$androidEmulatorServices.getInfoFromAvd(avd));
        const emulators = _.map(androidVirtualDevices, avd => {
            return { name: avd.device, version: avd.target, id: avd.name, platform: "Android", type: "Emulator", isRunning: false };
        });
        return emulators;
    }
    parseiOSVersion(osName) {
        osName = osName.replace("com.apple.CoreSimulator.SimRuntime.iOS-", "");
        osName = osName.replace(/-/g, ".");
        osName = osName.replace("iOS", "");
        osName = osName.trim();
        return osName;
    }
    outputEmulators(title, emulators) {
        this.$logger.out(title);
        let table = helpers_1.createTable(["Device Name", "Platform", "Version", "Device Identifier"], []);
        for (let info of emulators) {
            table.push([info.name, info.platform, info.version, info.id]);
        }
        this.$logger.out(table.toString());
    }
}
exports.EmulatorPlatformService = EmulatorPlatformService;
$injector.register("emulatorPlatformService", EmulatorPlatformService);
