"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;
};
const constants = require("../../constants");
const helpers = require("../../common/helpers");
const path = require("path");
const semver = require("semver");
const fiberBootstrap = require("../../common/fiber-bootstrap");
let gaze = require("gaze");
class LiveSyncService {
    constructor($devicePlatformsConstants, $errors, $platformsData, $platformService, $projectData, $projectDataService, $prompter, $injector, $liveSyncProvider, $mobileHelper, $devicesService, $options, $logger, $dispatcher, $hooksService, $processService) {
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$errors = $errors;
        this.$platformsData = $platformsData;
        this.$platformService = $platformService;
        this.$projectData = $projectData;
        this.$projectDataService = $projectDataService;
        this.$prompter = $prompter;
        this.$injector = $injector;
        this.$liveSyncProvider = $liveSyncProvider;
        this.$mobileHelper = $mobileHelper;
        this.$devicesService = $devicesService;
        this.$options = $options;
        this.$logger = $logger;
        this.$dispatcher = $dispatcher;
        this.$hooksService = $hooksService;
        this.$processService = $processService;
        this.forceExecuteFullSync = false;
        this._isInitialized = false;
    }
    ensureAndroidFrameworkVersion(platformData) {
        return (() => {
            this.$projectDataService.initialize(this.$projectData.projectDir);
            let frameworkVersion = this.$projectDataService.getValue(platformData.frameworkPackageName).version;
            if (platformData.normalizedPlatformName.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) {
                if (semver.lt(frameworkVersion, "1.2.1")) {
                    let shouldUpdate = this.$prompter.confirm("You need Android Runtime 1.2.1 or later for LiveSync to work properly. Do you want to update your runtime now?").wait();
                    if (shouldUpdate) {
                        this.$platformService.updatePlatforms([this.$devicePlatformsConstants.Android.toLowerCase()]).wait();
                    }
                    else {
                        return;
                    }
                }
            }
        }).future()();
    }
    get isInitialized() {
        return this._isInitialized;
    }
    liveSync(platform, applicationReloadAction) {
        return (() => {
            let liveSyncData = [];
            if (platform) {
                this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait();
                liveSyncData.push(this.prepareLiveSyncData(platform));
            }
            else if (this.$options.device) {
                this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait();
                platform = this.$devicesService.getDeviceByIdentifier(this.$options.device).deviceInfo.platform;
                liveSyncData.push(this.prepareLiveSyncData(platform));
            }
            else {
                this.$devicesService.initialize({ skipInferPlatform: true }).wait();
                this.$devicesService.stopDeviceDetectionInterval().wait();
                for (let installedPlatform of this.$platformService.getInstalledPlatforms()) {
                    if (this.$devicesService.getDevicesForPlatform(installedPlatform).length === 0) {
                        this.$devicesService.startEmulator(installedPlatform).wait();
                    }
                    liveSyncData.push(this.prepareLiveSyncData(installedPlatform));
                }
            }
            if (liveSyncData.length === 0) {
                this.$errors.fail("There are no platforms installed in this project. Please specify platform or install one by using `tns platform add` command!");
            }
            this._isInitialized = true;
            this.liveSyncCore(liveSyncData, applicationReloadAction).wait();
        }).future()();
    }
    prepareLiveSyncData(platform) {
        platform = platform || this.$devicesService.platform;
        this.$platformService.preparePlatform(platform.toLowerCase()).wait();
        let platformData = this.$platformsData.getPlatformData(platform.toLowerCase());
        if (this.$mobileHelper.isAndroidPlatform(platform)) {
            this.ensureAndroidFrameworkVersion(platformData).wait();
        }
        let liveSyncData = {
            platform: platform,
            appIdentifier: this.$projectData.projectId,
            projectFilesPath: path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME),
            syncWorkingDirectory: path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME),
            excludedProjectDirsAndFiles: this.$options.release ? constants.LIVESYNC_EXCLUDED_FILE_PATTERNS : [],
            forceExecuteFullSync: this.forceExecuteFullSync
        };
        return liveSyncData;
    }
    resolvePlatformLiveSyncBaseService(platform, liveSyncData) {
        return this.$injector.resolve(this.$liveSyncProvider.platformSpecificLiveSyncServices[platform.toLowerCase()], { _liveSyncData: liveSyncData });
    }
    liveSyncCore(liveSyncData, applicationReloadAction) {
        return (() => {
            let watchForChangeActions = [];
            _.each(liveSyncData, (dataItem) => {
                let service = this.resolvePlatformLiveSyncBaseService(dataItem.platform, dataItem);
                watchForChangeActions.push((event, filePath, dispatcher) => {
                    service.partialSync(event, filePath, dispatcher, applicationReloadAction);
                });
                service.fullSync(applicationReloadAction).wait();
            });
            if (this.$options.watch) {
                this.$hooksService.executeBeforeHooks('watch').wait();
                this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions);
            }
        }).future()();
    }
    partialSync(syncWorkingDirectory, onChangedActions) {
        let that = this;
        let gazeWatcher = gaze("**/*", { cwd: syncWorkingDirectory, follow: true }, function (err, watcher) {
            this.on('all', (event, filePath) => {
                fiberBootstrap.run(() => {
                    that.$dispatcher.dispatch(() => (() => {
                        try {
                            for (let i = 0; i < onChangedActions.length; i++) {
                                onChangedActions[i](event, filePath, that.$dispatcher);
                            }
                        }
                        catch (err) {
                            that.$logger.info(`Unable to sync file ${filePath}. Error is:${err.message}`.red.bold);
                            that.$logger.info("Try saving it again or restart the livesync operation.");
                        }
                    }).future()());
                });
            });
        });
        this.$processService.attachToProcessExitSignals(this, () => gazeWatcher.close());
        this.$dispatcher.run();
    }
}
__decorate([
    helpers.hook('livesync')
], LiveSyncService.prototype, "liveSyncCore", null);
$injector.register("usbLiveSyncService", LiveSyncService);
