"use strict";
var syncBatchLib = require("../../common/services/livesync/sync-batch");
var shell = require("shelljs");
var path = require("path");
var temp = require("temp");
var minimatch = require("minimatch");
var constants = require("../../common/constants");
var util = require("util");
var PlatformLiveSyncServiceBase = (function () {
    function PlatformLiveSyncServiceBase(_liveSyncData, $devicesService, $mobileHelper, $logger, $options, $deviceAppDataFactory, $fs, $injector, $projectFilesManager, $projectFilesProvider, $liveSyncProvider) {
        this.$devicesService = $devicesService;
        this.$mobileHelper = $mobileHelper;
        this.$logger = $logger;
        this.$options = $options;
        this.$deviceAppDataFactory = $deviceAppDataFactory;
        this.$fs = $fs;
        this.$injector = $injector;
        this.$projectFilesManager = $projectFilesManager;
        this.$projectFilesProvider = $projectFilesProvider;
        this.$liveSyncProvider = $liveSyncProvider;
        this.showFullLiveSyncInformation = false;
        this.batch = Object.create(null);
        this.livesyncData = Object.create(null);
        this.liveSyncData = _liveSyncData;
        this.fileHashes = Object.create(null);
    }
    PlatformLiveSyncServiceBase.prototype.partialSync = function (event, filePath, dispatcher, afterFileSyncAction) {
        if (filePath.indexOf(constants.APP_RESOURCES_FOLDER_NAME) !== -1) {
            this.$logger.warn(("Skipping livesync for changed file " + filePath + ". This change requires a full build to update your application. ").yellow.bold);
            return;
        }
        var fileHash = this.$fs.exists(filePath).wait() && this.$fs.getFsStats(filePath).wait().isFile() ? this.$fs.getFileShasum(filePath).wait() : "";
        if (fileHash === this.fileHashes[filePath]) {
            this.$logger.trace("Skipping livesync for " + filePath + " file with " + fileHash + " hash.");
            return;
        }
        this.$logger.trace("Adding " + filePath + " file with " + fileHash + " hash.");
        this.fileHashes[filePath] = fileHash;
        if (this.isFileExcluded(filePath, this.liveSyncData.excludedProjectDirsAndFiles)) {
            this.$logger.trace("Skipping livesync for changed file " + filePath + " as it is excluded in the patterns: " + this.liveSyncData.excludedProjectDirsAndFiles.join(", "));
            return;
        }
        var mappedFilePath = this.$projectFilesProvider.mapFilePath(filePath, this.liveSyncData.platform);
        this.$logger.trace("Syncing filePath " + filePath + ", mappedFilePath is " + mappedFilePath);
        if (!mappedFilePath) {
            this.$logger.warn("Unable to sync " + filePath + ".");
            return;
        }
        if (event === "added" || event === "changed" || event === "renamed") {
            this.batchSync(mappedFilePath, dispatcher, afterFileSyncAction);
        }
        else if (event === "deleted") {
            this.fileHashes = (_.omit(this.fileHashes, filePath));
            this.syncRemovedFile(mappedFilePath, afterFileSyncAction).wait();
        }
    };
    PlatformLiveSyncServiceBase.prototype.getCanExecuteAction = function (platform, appIdentifier) {
        var _this = this;
        var isTheSamePlatformAction = (function (device) { return device.deviceInfo.platform.toLowerCase() === platform.toLowerCase(); });
        if (this.$options.device) {
            return function (device) { return isTheSamePlatformAction(device) && device.deviceInfo.identifier === _this.$devicesService.getDeviceByDeviceOption().deviceInfo.identifier; };
        }
        return isTheSamePlatformAction;
    };
    PlatformLiveSyncServiceBase.prototype.tryInstallApplication = function (device, deviceAppData) {
        var _this = this;
        return (function () {
            device.applicationManager.checkForApplicationUpdates().wait();
            var appIdentifier = _this.liveSyncData.appIdentifier;
            if (!device.applicationManager.isApplicationInstalled(appIdentifier).wait()) {
                _this.$logger.warn("The application with id \"" + appIdentifier + "\" is not installed on device with identifier " + device.deviceInfo.identifier + ".");
                var packageFilePath = _this.$liveSyncProvider.buildForDevice(device).wait();
                device.applicationManager.installApplication(packageFilePath).wait();
                return true;
            }
            return false;
        }).future()();
    };
    PlatformLiveSyncServiceBase.prototype.refreshApplication = function (deviceAppData, localToDevicePaths) {
        var _this = this;
        return (function () {
            var deviceLiveSyncService = _this.resolveDeviceSpecificLiveSyncService(deviceAppData.device.deviceInfo.platform, deviceAppData.device);
            _this.$logger.info("Applying changes...");
            deviceLiveSyncService.refreshApplication(deviceAppData, localToDevicePaths, _this.liveSyncData.forceExecuteFullSync).wait();
        }).future()();
    };
    PlatformLiveSyncServiceBase.prototype.finishLivesync = function (deviceAppData) {
        var _this = this;
        return (function () {
            _this.$logger.info("Successfully synced application " + deviceAppData.appIdentifier + " on device " + deviceAppData.device.deviceInfo.identifier + ".");
        }).future()();
    };
    PlatformLiveSyncServiceBase.prototype.transferFiles = function (deviceAppData, localToDevicePaths, projectFilesPath, isFullSync) {
        var _this = this;
        return (function () {
            _this.$logger.info("Transferring project files...");
            _this.logFilesSyncInformation(localToDevicePaths, "Transferring %s.", _this.$logger.trace);
            var canTransferDirectory = isFullSync && (_this.$devicesService.isAndroidDevice(deviceAppData.device) || _this.$devicesService.isiOSSimulator(deviceAppData.device));
            if (canTransferDirectory) {
                var tempDir = temp.mkdirSync("tempDir");
                shell.cp("-Rf", path.join(projectFilesPath, "*"), tempDir);
                _this.$projectFilesManager.processPlatformSpecificFiles(tempDir, deviceAppData.platform).wait();
                deviceAppData.device.fileSystem.transferDirectory(deviceAppData, localToDevicePaths, tempDir).wait();
            }
            else {
                _this.$liveSyncProvider.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, isFullSync).wait();
            }
            _this.logFilesSyncInformation(localToDevicePaths, "Successfully transferred %s.", _this.$logger.info);
        }).future()();
    };
    PlatformLiveSyncServiceBase.prototype.resolveDeviceSpecificLiveSyncService = function (platform, device) {
        return this.$injector.resolve(this.$liveSyncProvider.deviceSpecificLiveSyncServices[platform.toLowerCase()], { _device: device });
    };
    PlatformLiveSyncServiceBase.prototype.isFileExcluded = function (filePath, excludedPatterns) {
        var isFileExcluded = false;
        _.each(excludedPatterns, function (pattern) {
            if (minimatch(filePath, pattern, { nocase: true })) {
                isFileExcluded = true;
                return false;
            }
        });
        return isFileExcluded;
    };
    PlatformLiveSyncServiceBase.prototype.batchSync = function (filePath, dispatcher, afterFileSyncAction) {
        var _this = this;
        var platformBatch = this.batch[this.liveSyncData.platform];
        if (!platformBatch || !platformBatch.syncPending) {
            var done = function () {
                return (function () {
                    dispatcher.dispatch(function () { return (function () {
                        try {
                            var _loop_1 = function(platformName) {
                                var batch = _this.batch[platformName];
                                batch.syncFiles((function (filesToSync) {
                                    _this.$liveSyncProvider.preparePlatformForSync(platformName).wait();
                                    var canExecute = _this.getCanExecuteAction(_this.liveSyncData.platform, _this.liveSyncData.appIdentifier);
                                    var deviceFileAction = function (deviceAppData, localToDevicePaths) { return _this.transferFiles(deviceAppData, localToDevicePaths, _this.liveSyncData.projectFilesPath, !filePath); };
                                    var action = _this.getSyncAction(filesToSync, deviceFileAction, afterFileSyncAction);
                                    _this.$devicesService.execute(action, canExecute);
                                }).future()).wait();
                            };
                            for (var platformName in _this.batch) {
                                _loop_1(platformName);
                            }
                        }
                        catch (err) {
                            _this.$logger.warn("Unable to sync files. Error is:", err.message);
                        }
                    }).future()(); });
                }).future()();
            };
            this.batch[this.liveSyncData.platform] = this.$injector.resolve(syncBatchLib.SyncBatch, { done: done });
            this.livesyncData[this.liveSyncData.platform] = this.liveSyncData;
        }
        this.batch[this.liveSyncData.platform].addFile(filePath);
    };
    PlatformLiveSyncServiceBase.prototype.syncRemovedFile = function (filePath, afterFileSyncAction) {
        var _this = this;
        return (function () {
            var deviceFilesAction = function (deviceAppData, localToDevicePaths) {
                var deviceLiveSyncService = _this.resolveDeviceSpecificLiveSyncService(_this.liveSyncData.platform, deviceAppData.device);
                return deviceLiveSyncService.removeFiles(_this.liveSyncData.appIdentifier, localToDevicePaths);
            };
            var canExecute = _this.getCanExecuteAction(_this.liveSyncData.platform, _this.liveSyncData.appIdentifier);
            var action = _this.getSyncAction([filePath], deviceFilesAction, afterFileSyncAction);
            _this.$devicesService.execute(action, canExecute).wait();
        }).future()();
    };
    PlatformLiveSyncServiceBase.prototype.getSyncAction = function (filesToSync, fileSyncAction, afterFileSyncAction) {
        var _this = this;
        var action = function (device) {
            return (function () {
                var deviceAppData = _this.$deviceAppDataFactory.create(_this.liveSyncData.appIdentifier, _this.$mobileHelper.normalizePlatformName(_this.liveSyncData.platform), device);
                var localToDevicePaths = _this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, _this.liveSyncData.projectFilesPath, filesToSync, _this.liveSyncData.excludedProjectDirsAndFiles);
                fileSyncAction(deviceAppData, localToDevicePaths).wait();
                if (!afterFileSyncAction) {
                    _this.refreshApplication(deviceAppData, localToDevicePaths).wait();
                }
                _this.finishLivesync(deviceAppData).wait();
                if (afterFileSyncAction) {
                    afterFileSyncAction(deviceAppData, localToDevicePaths).wait();
                }
            }).future()();
        };
        return action;
    };
    PlatformLiveSyncServiceBase.prototype.logFilesSyncInformation = function (localToDevicePaths, message, action) {
        var _this = this;
        if (this.showFullLiveSyncInformation) {
            _.each(localToDevicePaths, function (file) {
                action.call(_this.$logger, util.format(message, path.basename(file.getLocalPath()).yellow));
            });
        }
        else {
            action.call(this.$logger, util.format(message, "all files"));
        }
    };
    return PlatformLiveSyncServiceBase;
}());
exports.PlatformLiveSyncServiceBase = PlatformLiveSyncServiceBase;
