"use strict";
var net = require("net");
var path = require("path");
var semver = require("semver");
var byline = require("byline");
var inspectorBackendPort = 18181;
var inspectorAppName = "NativeScript Inspector.app";
var inspectorZipName = "NativeScript Inspector.zip";
var inspectorNpmPackageName = "tns-ios-inspector";
var inspectorUiDir = "WebInspectorUI/";
var TIMEOUT_SECONDS = 90;
var IOSDebugService = (function () {
    function IOSDebugService($config, $platformService, $iOSEmulatorServices, $devicesService, $platformsData, $projectData, $childProcess, $logger, $fs, $errors, $injector, $npmInstallationManager, $options, $projectDataService, $utils, $iOSNotification, $iOSSocketRequestExecutor, $socketProxyFactory) {
        this.$config = $config;
        this.$platformService = $platformService;
        this.$iOSEmulatorServices = $iOSEmulatorServices;
        this.$devicesService = $devicesService;
        this.$platformsData = $platformsData;
        this.$projectData = $projectData;
        this.$childProcess = $childProcess;
        this.$logger = $logger;
        this.$fs = $fs;
        this.$errors = $errors;
        this.$injector = $injector;
        this.$npmInstallationManager = $npmInstallationManager;
        this.$options = $options;
        this.$projectDataService = $projectDataService;
        this.$utils = $utils;
        this.$iOSNotification = $iOSNotification;
        this.$iOSSocketRequestExecutor = $iOSSocketRequestExecutor;
        this.$socketProxyFactory = $socketProxyFactory;
    }
    Object.defineProperty(IOSDebugService.prototype, "platform", {
        get: function () {
            return "ios";
        },
        enumerable: true,
        configurable: true
    });
    IOSDebugService.prototype.debug = function () {
        if (this.$options.debugBrk && this.$options.start) {
            this.$errors.failWithoutHelp("Expected exactly one of the --debug-brk or --start options.");
        }
        if (this.$devicesService.isOnlyiOSSimultorRunning() || this.$devicesService.deviceCount === 0) {
            this.$options.emulator = true;
        }
        if (this.$options.emulator) {
            if (this.$options.debugBrk) {
                return this.emulatorDebugBrk(true);
            }
            else if (this.$options.start) {
                return this.emulatorStart();
            }
            else {
                return this.emulatorDebugBrk();
            }
        }
        else {
            if (this.$options.debugBrk) {
                return this.deviceDebugBrk(true);
            }
            else if (this.$options.start) {
                return this.deviceStart();
            }
            else {
                return this.deviceDebugBrk(false);
            }
        }
    };
    IOSDebugService.prototype.debugStart = function () {
        var _this = this;
        return (function () {
            _this.$devicesService.initialize({ platform: _this.platform, deviceId: _this.$options.device }).wait();
            _this.$devicesService.execute(function (device) { return device.isEmulator ? _this.emulatorDebugBrk() : _this.debugBrkCore(device); }).wait();
        }).future()();
    };
    IOSDebugService.prototype.debugStop = function () {
        var _this = this;
        return (function () {
            if (_this._lldbProcess) {
                _this._lldbProcess.stdin.write("process detach\n");
                _this._lldbProcess.kill();
                _this._lldbProcess = undefined;
            }
        }).future()();
    };
    IOSDebugService.prototype.emulatorDebugBrk = function (shouldBreak) {
        var _this = this;
        return (function () {
            var platformData = _this.$platformsData.getPlatformData(_this.platform);
            if (_this.$options.rebuild) {
                _this.$platformService.prepareAndExecute(_this.platform, function () { return _this.$platformService.buildPlatform(_this.platform); }).wait();
            }
            var emulatorPackage = _this.$platformService.getLatestApplicationPackageForEmulator(platformData).wait();
            var args = shouldBreak ? "--nativescript-debug-brk" : "--nativescript-debug-start";
            var child_process = _this.$iOSEmulatorServices.runApplicationOnEmulator(emulatorPackage.packageName, {
                waitForDebugger: true, captureStdin: true,
                args: args, appId: _this.$projectData.projectId,
                skipInstall: _this.$config.debugLivesync
            }).wait();
            var lineStream = byline(child_process.stdout);
            lineStream.on('data', function (line) {
                var lineText = line.toString();
                if (lineText && _.startsWith(lineText, _this.$projectData.projectId)) {
                    var pid = _.trimStart(lineText, _this.$projectData.projectId + ": ");
                    _this._lldbProcess = _this.$childProcess.spawn("lldb", ["-p", pid]);
                    _this._lldbProcess.stdin.write("process continue\n");
                }
                else {
                    process.stdout.write(line + "\n");
                }
            });
            _this.wireDebuggerClient(function () { return net.connect(inspectorBackendPort); }).wait();
        }).future()();
    };
    IOSDebugService.prototype.emulatorStart = function () {
        var _this = this;
        return (function () {
            _this.wireDebuggerClient(function () { return net.connect(inspectorBackendPort); }).wait();
            var attachRequestMessage = _this.$iOSNotification.attachRequest;
            var iOSEmulator = _this.$iOSEmulatorServices;
            iOSEmulator.postDarwinNotification(attachRequestMessage).wait();
        }).future()();
    };
    IOSDebugService.prototype.deviceDebugBrk = function (shouldBreak) {
        var _this = this;
        return (function () {
            _this.$devicesService.initialize({ platform: _this.platform, deviceId: _this.$options.device }).wait();
            _this.$devicesService.execute(function (device) { return (function () {
                if (device.isEmulator) {
                    return _this.emulatorDebugBrk(shouldBreak).wait();
                }
                var action;
                if (_this.$config.debugLivesync) {
                    action = _this.$platformService.startOnDevice(_this.platform);
                }
                else {
                    action = _this.$platformService.deployOnDevice(_this.platform);
                }
                _this.debugBrkCore(device, shouldBreak).wait();
                action.wait();
            }).future()(); }).wait();
        }).future()();
    };
    IOSDebugService.prototype.debugBrkCore = function (device, shouldBreak) {
        var _this = this;
        return (function () {
            var timeout = _this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS);
            var readyForAttachTimeout = _this.getReadyForAttachTimeout(timeout);
            _this.$iOSSocketRequestExecutor.executeLaunchRequest(device, timeout, readyForAttachTimeout, shouldBreak).wait();
            _this.wireDebuggerClient(function () { return device.connectToPort(inspectorBackendPort); }).wait();
        }).future()();
    };
    IOSDebugService.prototype.deviceStart = function () {
        var _this = this;
        return (function () {
            _this.$devicesService.initialize({ platform: _this.platform, deviceId: _this.$options.device }).wait();
            _this.$devicesService.execute(function (device) { return device.isEmulator ? _this.emulatorStart() : _this.deviceStartCore(device); }).wait();
        }).future()();
    };
    IOSDebugService.prototype.deviceStartCore = function (device) {
        var _this = this;
        return (function () {
            var timeout = _this.getReadyForAttachTimeout();
            _this.$iOSSocketRequestExecutor.executeAttachRequest(device, timeout).wait();
            _this.wireDebuggerClient(function () { return device.connectToPort(inspectorBackendPort); }).wait();
        }).future()();
    };
    IOSDebugService.prototype.wireDebuggerClient = function (factory) {
        var _this = this;
        return (function () {
            var socketProxy = _this.$socketProxyFactory.createSocketProxy(factory).wait();
            _this.executeOpenDebuggerClient(socketProxy).wait();
        }).future()();
    };
    IOSDebugService.prototype.executeOpenDebuggerClient = function (fileDescriptor) {
        var _this = this;
        if (this.$options.client) {
            return this.openDebuggingClient(fileDescriptor);
        }
        else {
            return (function () {
                _this.$logger.info("Suppressing debugging client.");
            }).future()();
        }
    };
    IOSDebugService.prototype.openDebuggingClient = function (fileDescriptor) {
        var _this = this;
        return (function () {
            var frameworkVersion = _this.getProjectFrameworkVersion().wait();
            var inspectorPath = _this.getInspectorPath(frameworkVersion).wait();
            var inspectorSourceLocation;
            var cmd = null;
            if (semver.lt(frameworkVersion, "1.2.0")) {
                cmd = "open -a Safari \"" + inspectorSourceLocation + "\"";
            }
            else {
                var inspectorApplicationDir = void 0;
                if (semver.lt(frameworkVersion, "1.6.0")) {
                    inspectorApplicationDir = inspectorPath;
                    inspectorSourceLocation = path.join(inspectorPath, "Safari/Main.html");
                }
                else {
                    inspectorApplicationDir = path.join(inspectorPath, "..");
                    inspectorSourceLocation = path.join(inspectorPath, "Main.html");
                }
                var inspectorApplicationPath = path.join(inspectorApplicationDir, inspectorAppName);
                if (!_this.$fs.exists(inspectorApplicationPath).wait()) {
                    _this.$fs.unzip(path.join(inspectorApplicationDir, inspectorZipName), inspectorApplicationDir).wait();
                }
                cmd = "open -a '" + inspectorApplicationPath + "' --args '" + inspectorSourceLocation + "' '" + _this.$projectData.projectName + "' '" + fileDescriptor + "'";
            }
            _this.$childProcess.exec(cmd).wait();
        }).future()();
    };
    IOSDebugService.prototype.getProjectFrameworkVersion = function () {
        var _this = this;
        return (function () {
            _this.$projectDataService.initialize(_this.$projectData.projectDir);
            var platformData = _this.$platformsData.getPlatformData(_this.platform);
            return _this.$projectDataService.getValue(platformData.frameworkPackageName).wait().version;
        }).future()();
    };
    IOSDebugService.prototype.getInspectorPath = function (frameworkVersion) {
        if (semver.lt(frameworkVersion, "1.6.0")) {
            return this.getInspectorPathFromTnsIosPackage(frameworkVersion);
        }
        else {
            return this.getInspectorPathFromInspectorPackage();
        }
    };
    IOSDebugService.prototype.getInspectorPathFromInspectorPackage = function () {
        var _this = this;
        return (function () {
            var inspectorPackage = _this.$npmInstallationManager.install(inspectorNpmPackageName).wait();
            var inspectorPath = path.join(inspectorPackage, inspectorUiDir);
            return inspectorPath;
        }).future()();
    };
    IOSDebugService.prototype.getInspectorPathFromTnsIosPackage = function (frameworkVersion) {
        var _this = this;
        return (function () {
            var tnsIosPackage = "";
            if (_this.$options.frameworkPath) {
                if (_this.$fs.getFsStats(_this.$options.frameworkPath).wait().isFile()) {
                    _this.$errors.failWithoutHelp("frameworkPath option must be path to directory which contains tns-ios framework");
                }
                tnsIosPackage = path.resolve(_this.$options.frameworkPath);
            }
            else {
                var platformData = _this.$platformsData.getPlatformData(_this.platform);
                tnsIosPackage = _this.$npmInstallationManager.install(platformData.frameworkPackageName, { version: frameworkVersion }).wait();
            }
            var inspectorPath = path.join(tnsIosPackage, inspectorUiDir);
            return inspectorPath;
        }).future()();
    };
    IOSDebugService.prototype.getReadyForAttachTimeout = function (timeoutInMilliseconds) {
        var timeout = timeoutInMilliseconds || this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS);
        var readyForAttachTimeout = timeout / 10;
        var defaultReadyForAttachTimeout = 5000;
        return readyForAttachTimeout > defaultReadyForAttachTimeout ? readyForAttachTimeout : defaultReadyForAttachTimeout;
    };
    return IOSDebugService;
}());
$injector.register("iOSDebugService", IOSDebugService);
