"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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
class LiveSyncCommandHelper {
    constructor($buildDataService, $projectData, $options, $deployController, $iosDeviceOperations, $mobileHelper, $devicesService, $injector, $buildController, $analyticsService, $bundleValidatorHelper, $errors, $iOSSimulatorLogProvider, $cleanupService, $runController) {
        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.$bundleValidatorHelper = $bundleValidatorHelper;
        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;
    }
    getDeviceInstances(platform) {
        return __awaiter(this, void 0, void 0, function* () {
            yield 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;
        });
    }
    createDeviceDescriptors(devices, platform, additionalOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            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, Object.assign({}, this.$options.argv, { outputPath, buildForDevice: !d.isEmulator }));
                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;
    }
    executeCommandLiveSync(platform, additionalOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            const devices = yield this.getDeviceInstances(platform);
            yield this.executeLiveSyncOperation(devices, platform, additionalOptions);
        });
    }
    executeLiveSyncOperation(devices, platform, additionalOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            const { liveSyncInfo, deviceDescriptors } = yield this.executeLiveSyncOperationCore(devices, platform, additionalOptions);
            if (this.$options.release) {
                yield this.runInRelease(platform, deviceDescriptors, liveSyncInfo);
                return;
            }
            yield this.$runController.run({
                liveSyncInfo,
                deviceDescriptors
            });
            const remainingDevicesToSync = devices.map(d => d.deviceInfo.identifier);
            this.$runController.on(constants_1.RunOnDeviceEvents.runOnDeviceStopped, (data) => {
                _.remove(remainingDevicesToSync, d => d === data.deviceIdentifier);
                if (remainingDevicesToSync.length === 0) {
                    process.exit(134);
                }
            });
        });
    }
    validatePlatform(platform) {
        return __awaiter(this, void 0, void 0, function* () {
            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 = yield platformProjectService.validate(this.$projectData, this.$options);
                result[availablePlatform.toLowerCase()] = validateOutput;
            }
            const minSupportedWebpackVersion = this.$options.hmr ? LiveSyncCommandHelper.MIN_SUPPORTED_WEBPACK_VERSION_WITH_HMR : null;
            this.$bundleValidatorHelper.validate(this.$projectData, minSupportedWebpackVersion);
            return result;
        });
    }
    executeLiveSyncOperationCore(devices, platform, additionalOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!devices || !devices.length) {
                if (platform) {
                    this.$errors.failWithoutHelp("Unable to find applicable devices to execute operation. Ensure connected devices are trusted and try again.");
                }
                else {
                    this.$errors.failWithoutHelp("Unable to find applicable devices to execute operation and unable to start emulator when platform is not specified.");
                }
            }
            const workingWithiOSDevices = !platform || this.$mobileHelper.isiOSPlatform(platform);
            const shouldKeepProcessAlive = this.$options.watch || !this.$options.justlaunch;
            if (shouldKeepProcessAlive) {
                this.$analyticsService.setShouldDispose(false);
                this.$cleanupService.setShouldDispose(false);
                if (workingWithiOSDevices) {
                    this.$iosDeviceOperations.setShouldDispose(false);
                    this.$iOSSimulatorLogProvider.setShouldDispose(false);
                }
            }
            const deviceDescriptors = yield this.createDeviceDescriptors(devices, platform, additionalOptions);
            const liveSyncInfo = this.getLiveSyncData(this.$projectData.projectDir);
            return { liveSyncInfo, deviceDescriptors };
        });
    }
    runInRelease(platform, deviceDescriptors, liveSyncInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.$devicesService.initialize({
                platform,
                deviceId: this.$options.device,
                emulator: this.$options.emulator,
                skipInferPlatform: !platform,
                sdk: this.$options.sdk
            });
            const buildData = this.$buildDataService.getBuildData(liveSyncInfo.projectDir, platform, Object.assign({}, this.$options.argv, { clean: true, watch: false }));
            yield this.$deployController.deploy({
                buildData,
                deviceDescriptors
            });
            for (const deviceDescriptor of deviceDescriptors) {
                const device = this.$devicesService.getDeviceByIdentifier(deviceDescriptor.identifier);
                yield device.applicationManager.startApplication({ appId: this.$projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()], projectName: this.$projectData.projectName });
            }
        });
    }
}
LiveSyncCommandHelper.MIN_SUPPORTED_WEBPACK_VERSION_WITH_HMR = "0.17.0";
exports.LiveSyncCommandHelper = LiveSyncCommandHelper;
$injector.register("liveSyncCommandHelper", LiveSyncCommandHelper);
