"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LiveSyncCommandHelper = void 0;
const _ = require("lodash");
const yok_1 = require("../common/yok");
const constants_1 = require("../constants");
class LiveSyncCommandHelper {
    constructor($androidBundleValidatorHelper, $buildDataService, $projectData, $options, $deployController, $iosDeviceOperations, $mobileHelper, $devicesService, $injector, $buildController, $analyticsService, $errors, $iOSSimulatorLogProvider, $cleanupService, $runController) {
        this.$androidBundleValidatorHelper = $androidBundleValidatorHelper;
        this.$buildDataService = $buildDataService;
        this.$projectData = $projectData;
        this.$options = $options;
        this.$deployController = $deployController;
        this.$iosDeviceOperations = $iosDeviceOperations;
        this.$mobileHelper = $mobileHelper;
        this.$devicesService = $devicesService;
        this.$injector = $injector;
        this.$buildController = $buildController;
        this.$analyticsService = $analyticsService;
        this.$errors = $errors;
        this.$iOSSimulatorLogProvider = $iOSSimulatorLogProvider;
        this.$cleanupService = $cleanupService;
        this.$runController = $runController;
    }
    get $platformsDataService() {
        return this.$injector.resolve("platformsDataService");
    }
    getLiveSyncData(projectDir) {
        const liveSyncInfo = {
            projectDir,
            skipWatcher: !this.$options.watch || this.$options.justlaunch,
            clean: this.$options.clean,
            release: this.$options.release,
            env: this.$options.env,
            timeout: this.$options.timeout,
            useHotModuleReload: this.$options.hmr,
            force: this.$options.force,
            emulator: this.$options.emulator,
        };
        return liveSyncInfo;
    }
    async getDeviceInstances(platform) {
        await this.$devicesService.initialize({
            platform,
            deviceId: this.$options.device,
            emulator: this.$options.emulator,
            skipInferPlatform: !platform,
            sdk: this.$options.sdk,
        });
        const devices = this.$devicesService
            .getDeviceInstances()
            .filter((d) => !platform ||
            d.deviceInfo.platform.toLowerCase() === platform.toLowerCase());
        return devices;
    }
    async createDeviceDescriptors(devices, platform, additionalOptions) {
        const deviceDescriptors = devices.map((d) => {
            const outputPath = additionalOptions &&
                additionalOptions.getOutputDirectory &&
                additionalOptions.getOutputDirectory({
                    platform: d.deviceInfo.platform,
                    emulator: d.isEmulator,
                    projectDir: this.$projectData.projectDir,
                });
            const buildData = this.$buildDataService.getBuildData(this.$projectData.projectDir, d.deviceInfo.platform, {
                ...this.$options.argv,
                outputPath,
                buildForDevice: !d.isEmulator,
                watch: !this.$options.release && this.$options.watch,
                nativePrepare: {
                    forceRebuildNativeApp: additionalOptions.forceRebuildNativeApp,
                },
                _device: d,
            });
            this.$androidBundleValidatorHelper.validateDeviceApiLevel(d, buildData);
            const buildAction = additionalOptions && additionalOptions.buildPlatform
                ? additionalOptions.buildPlatform.bind(additionalOptions.buildPlatform, d.deviceInfo.platform, buildData, this.$projectData)
                : this.$buildController.build.bind(this.$buildController, buildData);
            const info = {
                identifier: d.deviceInfo.identifier,
                buildAction,
                debuggingEnabled: additionalOptions &&
                    additionalOptions.deviceDebugMap &&
                    additionalOptions.deviceDebugMap[d.deviceInfo.identifier],
                debugOptions: this.$options,
                skipNativePrepare: additionalOptions && additionalOptions.skipNativePrepare,
                buildData,
            };
            return info;
        });
        return deviceDescriptors;
    }
    getPlatformsForOperation(platform) {
        const availablePlatforms = platform
            ? [platform]
            : _.values(this.$mobileHelper.platformNames.map((p) => p.toLowerCase()));
        return availablePlatforms;
    }
    async executeCommandLiveSync(platform, additionalOptions) {
        const devices = await this.getDeviceInstances(platform);
        await this.executeLiveSyncOperation(devices, platform, additionalOptions);
    }
    async executeLiveSyncOperation(devices, platform, additionalOptions) {
        const { liveSyncInfo, deviceDescriptors } = await this.executeLiveSyncOperationCore(devices, platform, additionalOptions);
        if (this.$options.release) {
            await this.runInRelease(platform, deviceDescriptors);
            return;
        }
        if (additionalOptions.restartLiveSync) {
            await this.$runController.stop({
                projectDir: this.$projectData.projectDir,
                deviceIdentifiers: deviceDescriptors.map((device) => device.identifier),
                stopOptions: {
                    shouldAwaitAllActions: true,
                    keepProcessAlive: true,
                },
            });
            const devices = await this.getDeviceInstances(platform);
            await this.executeLiveSyncOperation(devices, platform, {
                ...additionalOptions,
                restartLiveSync: false,
            });
            return;
        }
        else {
            await this.$runController.run({
                liveSyncInfo,
                deviceDescriptors,
            });
        }
        this.$runController.on(constants_1.RunOnDeviceEvents.runOnDeviceStopped, async (data) => {
            const devices = await this.getDeviceInstances(platform);
            const remainingDevicesToSync = devices.map((d) => d.deviceInfo.identifier);
            _.remove(remainingDevicesToSync, (d) => d === data.deviceIdentifier);
            if (remainingDevicesToSync.length === 0 && !data.keepProcessAlive) {
                process.exit(134);
            }
        });
    }
    async validatePlatform(platform) {
        const result = {};
        const availablePlatforms = this.getPlatformsForOperation(platform);
        for (const availablePlatform of availablePlatforms) {
            const platformData = this.$platformsDataService.getPlatformData(availablePlatform, this.$projectData);
            const platformProjectService = platformData.platformProjectService;
            const validateOutput = await platformProjectService.validate(this.$projectData, this.$options);
            result[availablePlatform.toLowerCase()] = validateOutput;
        }
        return result;
    }
    async executeLiveSyncOperationCore(devices, platform, additionalOptions) {
        if (!devices || !devices.length) {
            if (platform) {
                this.$errors.fail("Unable to find applicable devices to execute operation. Ensure connected devices are trusted and try again.");
            }
            else {
                this.$errors.fail("Unable to find applicable devices to execute operation and unable to start emulator when platform is not specified.");
            }
        }
        const workingWithAppleDevices = !platform || this.$mobileHelper.isApplePlatform(platform);
        const shouldKeepProcessAlive = this.$options.watch || !this.$options.justlaunch;
        if (shouldKeepProcessAlive) {
            this.$analyticsService.setShouldDispose(false);
            this.$cleanupService.setShouldDispose(false);
            if (workingWithAppleDevices) {
                this.$iosDeviceOperations.setShouldDispose(false);
                this.$iOSSimulatorLogProvider.setShouldDispose(false);
            }
        }
        const deviceDescriptors = await this.createDeviceDescriptors(devices, platform, additionalOptions);
        const liveSyncInfo = this.getLiveSyncData(this.$projectData.projectDir);
        return { liveSyncInfo, deviceDescriptors };
    }
    async runInRelease(platform, deviceDescriptors) {
        await this.$devicesService.initialize({
            platform,
            deviceId: this.$options.device,
            emulator: this.$options.emulator,
            skipInferPlatform: !platform,
            sdk: this.$options.sdk,
        });
        await this.$deployController.deploy({ deviceDescriptors });
        for (const deviceDescriptor of deviceDescriptors) {
            const device = this.$devicesService.getDeviceByIdentifier(deviceDescriptor.identifier);
            await device.applicationManager.startApplication({
                appId: this.$projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()],
                projectName: this.$projectData.projectName,
                projectDir: this.$projectData.projectDir,
            });
        }
    }
    async stop() {
        const devices = await this.getDeviceInstances();
        return this.$runController.stop({
            projectDir: this.$projectData.projectDir,
            deviceIdentifiers: devices.map((d) => d.deviceInfo.identifier),
            stopOptions: {
                shouldAwaitAllActions: true,
                keepProcessAlive: true,
            },
        });
    }
}
exports.LiveSyncCommandHelper = LiveSyncCommandHelper;
yok_1.injector.register("liveSyncCommandHelper", LiveSyncCommandHelper);
