"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 constants = require("../../constants");
var helpers = require("../../common/helpers");
var path = require("path");
var semver = require("semver");
var fiberBootstrap = require("../../common/fiber-bootstrap");
var gaze = require("gaze");
var LiveSyncService = (function () {
    function LiveSyncService($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;
    }
    LiveSyncService.prototype.ensureAndroidFrameworkVersion = function (platformData) {
        var _this = this;
        return (function () {
            _this.$projectDataService.initialize(_this.$projectData.projectDir);
            var frameworkVersion = _this.$projectDataService.getValue(platformData.frameworkPackageName).wait().version;
            if (platformData.normalizedPlatformName.toLowerCase() === _this.$devicePlatformsConstants.Android.toLowerCase()) {
                if (semver.lt(frameworkVersion, "1.2.1")) {
                    var 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()();
    };
    Object.defineProperty(LiveSyncService.prototype, "isInitialized", {
        get: function () {
            return this._isInitialized;
        },
        enumerable: true,
        configurable: true
    });
    LiveSyncService.prototype.liveSync = function (platform, applicationReloadAction) {
        var _this = this;
        return (function () {
            var 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 (var _i = 0, _a = _this.$platformService.getInstalledPlatforms().wait(); _i < _a.length; _i++) {
                    var installedPlatform = _a[_i];
                    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()();
    };
    LiveSyncService.prototype.prepareLiveSyncData = function (platform) {
        platform = platform || this.$devicesService.platform;
        if (!this.$platformService.preparePlatform(platform.toLowerCase()).wait()) {
            this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
        }
        var platformData = this.$platformsData.getPlatformData(platform.toLowerCase());
        if (this.$mobileHelper.isAndroidPlatform(platform)) {
            this.ensureAndroidFrameworkVersion(platformData).wait();
        }
        var 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;
    };
    LiveSyncService.prototype.resolvePlatformLiveSyncBaseService = function (platform, liveSyncData) {
        return this.$injector.resolve(this.$liveSyncProvider.platformSpecificLiveSyncServices[platform.toLowerCase()], { _liveSyncData: liveSyncData });
    };
    LiveSyncService.prototype.liveSyncCore = function (liveSyncData, applicationReloadAction) {
        var _this = this;
        return (function () {
            var watchForChangeActions = [];
            _.each(liveSyncData, function (dataItem) {
                var service = _this.resolvePlatformLiveSyncBaseService(dataItem.platform, dataItem);
                watchForChangeActions.push(function (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()();
    };
    LiveSyncService.prototype.partialSync = function (syncWorkingDirectory, onChangedActions) {
        var that = this;
        var gazeWatcher = gaze("**/*", { cwd: syncWorkingDirectory }, function (err, watcher) {
            this.on('all', function (event, filePath) {
                fiberBootstrap.run(function () {
                    that.$dispatcher.dispatch(function () { return (function () {
                        try {
                            for (var 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, function () { return gazeWatcher.close(); });
        this.$dispatcher.run();
    };
    __decorate([
        helpers.hook('livesync')
    ], LiveSyncService.prototype, "liveSyncCore", null);
    return LiveSyncService;
}());
$injector.register("usbLiveSyncService", LiveSyncService);
