"use strict";
var constants = require("../constants");
var path = require('path');
var Future = require('fibers/future');
var os = require('os');
var fiberBootstrap = require("../common/fiber-bootstrap");
var TestExecutionService = (function () {
    function TestExecutionService($injector, $projectData, $platformService, $platformsData, $liveSyncProvider, $devicePlatformsConstants, $resources, $httpClient, $config, $logger, $fs, $options, $pluginsService, $errors, $androidDebugService, $iOSDebugService, $devicesService, $childProcess) {
        this.$injector = $injector;
        this.$projectData = $projectData;
        this.$platformService = $platformService;
        this.$platformsData = $platformsData;
        this.$liveSyncProvider = $liveSyncProvider;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$resources = $resources;
        this.$httpClient = $httpClient;
        this.$config = $config;
        this.$logger = $logger;
        this.$fs = $fs;
        this.$options = $options;
        this.$pluginsService = $pluginsService;
        this.$errors = $errors;
        this.$androidDebugService = $androidDebugService;
        this.$iOSDebugService = $iOSDebugService;
        this.$devicesService = $devicesService;
        this.$childProcess = $childProcess;
        this.allowedParameters = [];
    }
    TestExecutionService.prototype.startTestRunner = function (platform) {
        var _this = this;
        return (function () {
            _this.platform = platform;
            _this.$options.justlaunch = true;
            var blockingOperationFuture = new Future();
            process.on('message', function (launcherConfig) {
                fiberBootstrap.run(function () {
                    try {
                        var platformData = _this.$platformsData.getPlatformData(platform.toLowerCase());
                        var projectDir = _this.$projectData.projectDir;
                        _this.$devicesService.initialize({ platform: platform, deviceId: _this.$options.device }).wait();
                        var projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
                        var configOptions = JSON.parse(launcherConfig);
                        _this.$options.debugBrk = configOptions.debugBrk;
                        _this.$options.debugTransport = configOptions.debugTransport;
                        var configJs = _this.generateConfig(_this.$options.port.toString(), configOptions);
                        _this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs).wait();
                        var socketIoJsUrl = "http://localhost:" + _this.$options.port + "/socket.io/socket.io.js";
                        var socketIoJs = _this.$httpClient.httpRequest(socketIoJsUrl).wait().body;
                        _this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs).wait();
                        if (!_this.$platformService.preparePlatform(platform).wait()) {
                            _this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
                        }
                        _this.detourEntryPoint(projectFilesPath).wait();
                        _this.liveSyncProject(platform);
                        if (_this.$options.debugBrk) {
                            _this.$logger.info('Starting debugger...');
                            var debugService = _this.$injector.resolve(platform + "DebugService");
                            debugService.debugStart().wait();
                        }
                        blockingOperationFuture.return();
                    }
                    catch (err) {
                        blockingOperationFuture.throw(err);
                    }
                });
            });
            process.send("ready");
            blockingOperationFuture.wait();
        }).future()();
    };
    TestExecutionService.prototype.startKarmaServer = function (platform) {
        var _this = this;
        var karmaFuture = new Future();
        platform = platform.toLowerCase();
        this.platform = platform;
        if (this.$options.debugBrk && this.$options.watch) {
            this.$errors.failWithoutHelp("You cannot use --watch and --debug-brk simultaneously. Remove one of the flags and try again.");
        }
        if (!this.$platformService.preparePlatform(platform).wait()) {
            this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
        }
        var projectDir = this.$projectData.projectDir;
        this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait();
        var karmaConfig = this.getKarmaConfiguration(platform), karmaRunner = this.$childProcess.fork(path.join(__dirname, "karma-execution.js"));
        karmaRunner.send({ karmaConfig: karmaConfig });
        karmaRunner.on("message", function (karmaData) {
            fiberBootstrap.run(function () {
                _this.$logger.trace("## Unit-testing: Parent process received message", karmaData);
                var port;
                if (karmaData.url) {
                    port = karmaData.url.port;
                    var socketIoJsUrl = "http://" + karmaData.url.host + "/socket.io/socket.io.js";
                    var socketIoJs = _this.$httpClient.httpRequest(socketIoJsUrl).wait().body;
                    _this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs).wait();
                }
                if (karmaData.launcherConfig) {
                    var configOptions = JSON.parse(karmaData.launcherConfig);
                    var configJs = _this.generateConfig(port, configOptions);
                    _this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs).wait();
                }
                if (_this.$options.debugBrk) {
                    _this.getDebugService(platform).debug().wait();
                }
                else {
                    _this.liveSyncProject(platform).wait();
                }
            });
        });
        karmaRunner.on("exit", function (exitCode) {
            if (exitCode !== 0) {
                var testError = new Error("Test run failed.");
                testError.suppressCommandHelp = true;
                karmaFuture.throw(testError);
            }
            else {
                karmaFuture.return();
            }
        });
        return karmaFuture;
    };
    TestExecutionService.prototype.detourEntryPoint = function (projectFilesPath) {
        var _this = this;
        return (function () {
            var packageJsonPath = path.join(projectFilesPath, 'package.json');
            var packageJson = _this.$fs.readJson(packageJsonPath).wait();
            packageJson.main = TestExecutionService.MAIN_APP_NAME;
            _this.$fs.writeJson(packageJsonPath, packageJson).wait();
        }).future()();
    };
    TestExecutionService.prototype.generateConfig = function (port, options) {
        var nics = os.networkInterfaces();
        var ips = Object.keys(nics)
            .map(function (nicName) { return nics[nicName].filter(function (binding) { return binding.family === 'IPv4'; })[0]; })
            .filter(function (binding) { return binding; })
            .map(function (binding) { return binding.address; });
        var config = {
            port: port,
            ips: ips,
            options: options,
        };
        return 'module.exports = ' + JSON.stringify(config);
    };
    TestExecutionService.prototype.getDebugService = function (platform) {
        var lowerCasedPlatform = platform.toLowerCase();
        if (lowerCasedPlatform === this.$devicePlatformsConstants.iOS.toLowerCase()) {
            return this.$iOSDebugService;
        }
        else if (lowerCasedPlatform === this.$devicePlatformsConstants.Android.toLowerCase()) {
            return this.$androidDebugService;
        }
        throw new Error("Invalid platform " + platform + ". Valid platforms are " + this.$devicePlatformsConstants.iOS + " and " + this.$devicePlatformsConstants.Android);
    };
    TestExecutionService.prototype.getKarmaConfiguration = function (platform) {
        var karmaConfig = {
            browsers: [platform],
            configFile: path.join(this.$projectData.projectDir, 'karma.conf.js'),
            _NS: {
                log: this.$logger.getLevel(),
                path: this.$options.path,
                tns: process.argv[1],
                node: process.execPath,
                options: {
                    debugTransport: this.$options.debugTransport,
                    debugBrk: this.$options.debugBrk,
                    watch: !!this.$options.watch
                }
            },
        };
        if (this.$config.DEBUG || this.$logger.getLevel() === 'TRACE') {
            karmaConfig.logLevel = 'DEBUG';
        }
        if (!this.$options.watch) {
            karmaConfig.singleRun = true;
        }
        if (this.$options.debugBrk) {
            karmaConfig.browserNoActivityTimeout = 1000000000;
        }
        karmaConfig.projectDir = this.$projectData.projectDir;
        this.$logger.debug(JSON.stringify(karmaConfig, null, 4));
        return karmaConfig;
    };
    TestExecutionService.prototype.liveSyncProject = function (platform) {
        var _this = this;
        return (function () {
            var platformData = _this.$platformsData.getPlatformData(platform.toLowerCase()), projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
            var liveSyncData = {
                platform: platform,
                appIdentifier: _this.$projectData.projectId,
                projectFilesPath: projectFilesPath,
                forceExecuteFullSync: true,
                syncWorkingDirectory: path.join(_this.$projectData.projectDir, constants.APP_FOLDER_NAME),
                excludedProjectDirsAndFiles: _this.$options.release ? constants.LIVESYNC_EXCLUDED_FILE_PATTERNS : []
            };
            var liveSyncService = _this.$injector.resolve(_this.$liveSyncProvider.platformSpecificLiveSyncServices[platform.toLowerCase()], { _liveSyncData: liveSyncData });
            liveSyncService.fullSync().wait();
        }).future()();
    };
    TestExecutionService.MAIN_APP_NAME = "./tns_modules/" + constants.TEST_RUNNER_NAME + "/app.js";
    TestExecutionService.CONFIG_FILE_NAME = "node_modules/" + constants.TEST_RUNNER_NAME + "/config.js";
    TestExecutionService.SOCKETIO_JS_FILE_NAME = "node_modules/" + constants.TEST_RUNNER_NAME + "/socket.io.js";
    return TestExecutionService;
}());
$injector.register('testExecutionService', TestExecutionService);
