"use strict";
var helpers = require("../../common/helpers");
var constants = require("../../constants");
var minimatch = require("minimatch");
var net = require("net");
var Future = require("fibers/future");
var currentPageReloadId = 0;
var IOSLiveSyncService = (function () {
    function IOSLiveSyncService(_device, $iOSSocketRequestExecutor, $iOSNotification, $iOSEmulatorServices, $injector, $logger, $options, $iOSDebugService, $childProcess, $fs, $liveSyncProvider, $processService) {
        this.$iOSSocketRequestExecutor = $iOSSocketRequestExecutor;
        this.$iOSNotification = $iOSNotification;
        this.$iOSEmulatorServices = $iOSEmulatorServices;
        this.$injector = $injector;
        this.$logger = $logger;
        this.$options = $options;
        this.$iOSDebugService = $iOSDebugService;
        this.$childProcess = $childProcess;
        this.$fs = $fs;
        this.$liveSyncProvider = $liveSyncProvider;
        this.$processService = $processService;
        this.device = (_device);
    }
    Object.defineProperty(IOSLiveSyncService.prototype, "debugService", {
        get: function () {
            return this.$iOSDebugService;
        },
        enumerable: true,
        configurable: true
    });
    IOSLiveSyncService.prototype.afterInstallApplicationAction = function (deviceAppData, localToDevicePaths) {
        var _this = this;
        return (function () {
            return _this.$options.watch;
        }).future()();
    };
    IOSLiveSyncService.prototype.setupSocketIfNeeded = function () {
        var _this = this;
        return (function () {
            if (_this.socket) {
                return true;
            }
            if (_this.device.isEmulator) {
                _this.$iOSEmulatorServices.postDarwinNotification(_this.$iOSNotification.attachRequest).wait();
                try {
                    _this.socket = helpers.connectEventuallyUntilTimeout(function () { return net.connect(IOSLiveSyncService.BACKEND_PORT); }, 5000).wait();
                }
                catch (e) {
                    _this.$logger.warn(e);
                    return false;
                }
            }
            else {
                var timeout = 9000;
                _this.$iOSSocketRequestExecutor.executeAttachRequest(_this.device, timeout).wait();
                _this.socket = _this.device.connectToPort(IOSLiveSyncService.BACKEND_PORT);
            }
            _this.attachEventHandlers();
            return true;
        }).future()();
    };
    IOSLiveSyncService.prototype.removeFiles = function (appIdentifier, localToDevicePaths) {
        var _this = this;
        return (function () {
            _.each(localToDevicePaths, function (localToDevicePathData) { return _this.device.fileSystem.deleteFile(localToDevicePathData.getDevicePath(), appIdentifier); });
        }).future()();
    };
    IOSLiveSyncService.prototype.refreshApplication = function (deviceAppData, localToDevicePaths, forceExecuteFullSync) {
        var _this = this;
        return (function () {
            if (forceExecuteFullSync) {
                _this.restartApplication(deviceAppData).wait();
                return;
            }
            var scriptRelatedFiles = [];
            var scriptFiles = _.filter(localToDevicePaths, function (localToDevicePath) { return _.endsWith(localToDevicePath.getDevicePath(), ".js"); });
            constants.LIVESYNC_EXCLUDED_FILE_PATTERNS.forEach(function (pattern) { return scriptRelatedFiles = _.concat(scriptRelatedFiles, localToDevicePaths.filter(function (file) { return minimatch(file.getDevicePath(), pattern, { nocase: true }); })); });
            var otherFiles = _.difference(localToDevicePaths, _.concat(scriptFiles, scriptRelatedFiles));
            var shouldRestart = _.some(otherFiles, function (localToDevicePath) { return !_this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform); });
            if (shouldRestart) {
                _this.restartApplication(deviceAppData).wait();
                return;
            }
            if (!_this.$options.liveEdit && scriptFiles.length) {
                _this.restartApplication(deviceAppData).wait();
                return;
            }
            if (_this.setupSocketIfNeeded().wait()) {
                _this.liveEdit(scriptFiles);
                _this.reloadPage(deviceAppData, otherFiles).wait();
            }
        }).future()();
    };
    IOSLiveSyncService.prototype.restartApplication = function (deviceAppData) {
        var projectData = this.$injector.resolve("projectData");
        return this.device.applicationManager.restartApplication(deviceAppData.appIdentifier, projectData.projectName);
    };
    IOSLiveSyncService.prototype.reloadPage = function (deviceAppData, localToDevicePaths) {
        var _this = this;
        return (function () {
            if (localToDevicePaths.length) {
                var message = JSON.stringify({
                    method: "Page.reload",
                    params: {
                        ignoreCache: false
                    },
                    id: ++currentPageReloadId
                });
                _this.sendMessage(message).wait();
            }
        }).future()();
    };
    IOSLiveSyncService.prototype.liveEdit = function (localToDevicePaths) {
        var _this = this;
        return (function () {
            _.each(localToDevicePaths, function (localToDevicePath) {
                var content = _this.$fs.readText(localToDevicePath.getLocalPath()).wait();
                var message = JSON.stringify({
                    method: "Debugger.setScriptSource",
                    params: {
                        scriptUrl: localToDevicePath.getRelativeToProjectBasePath(),
                        scriptSource: content
                    },
                    id: ++currentPageReloadId
                });
                _this.sendMessage(message).wait();
            });
        }).future()();
    };
    IOSLiveSyncService.prototype.attachEventHandlers = function () {
        var _this = this;
        this.$processService.attachToProcessExitSignals(this, this.destroySocket);
        this.socket.on("close", function (hadError) {
            _this.$logger.trace("Socket closed, hadError is " + hadError + ".");
            _this.socket = null;
        });
        this.socket.on("error", function (error) {
            _this.$logger.trace("Socket error received: " + error);
        });
        this.socket.on("data", function (data) {
            _this.$logger.trace("Socket sent data: " + data.toString());
        });
    };
    IOSLiveSyncService.prototype.sendMessage = function (message) {
        var _this = this;
        return (function () {
            var socketWriteFuture = new Future();
            try {
                var length_1 = Buffer.byteLength(message, "utf16le");
                var payload = new Buffer(length_1 + 4);
                payload.writeInt32BE(length_1, 0);
                payload.write(message, 4, length_1, "utf16le");
                _this.socket.once("error", function (error) {
                    if (!socketWriteFuture.isResolved()) {
                        socketWriteFuture.throw(error);
                    }
                });
                _this.socket.write(payload, "utf16le", function () {
                    _this.socket.removeAllListeners("error");
                    if (!socketWriteFuture.isResolved()) {
                        socketWriteFuture.return();
                    }
                });
                socketWriteFuture.wait();
            }
            catch (error) {
                _this.$logger.trace("Error while sending message:", error);
                _this.destroySocket();
            }
        }).future()();
    };
    IOSLiveSyncService.prototype.destroySocket = function () {
        if (this.socket) {
            this.socket.destroy();
            this.socket = null;
        }
    };
    IOSLiveSyncService.BACKEND_PORT = 18181;
    return IOSLiveSyncService;
}());
$injector.register("iosLiveSyncServiceLocator", { factory: IOSLiveSyncService });
