"use strict";
var path = require("path");
var shelljs = require("shelljs");
var semver = require("semver");
var constants = require("../constants");
var PluginsService = (function () {
    function PluginsService($npm, $fs, $childProcess, $options, $logger, $errors, $injector) {
        this.$npm = $npm;
        this.$fs = $fs;
        this.$childProcess = $childProcess;
        this.$options = $options;
        this.$logger = $logger;
        this.$errors = $errors;
        this.$injector = $injector;
    }
    Object.defineProperty(PluginsService.prototype, "$projectData", {
        get: function () {
            return this.$injector.resolve("projectData");
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(PluginsService.prototype, "$platformsData", {
        get: function () {
            return this.$injector.resolve("platformsData");
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(PluginsService.prototype, "$pluginVariablesService", {
        get: function () {
            return this.$injector.resolve("pluginVariablesService");
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(PluginsService.prototype, "$projectDataService", {
        get: function () {
            return this.$injector.resolve("projectDataService");
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(PluginsService.prototype, "$projectFilesManager", {
        get: function () {
            return this.$injector.resolve("projectFilesManager");
        },
        enumerable: true,
        configurable: true
    });
    PluginsService.prototype.add = function (plugin) {
        var _this = this;
        return (function () {
            _this.ensure().wait();
            var dependencyData = _this.$npm.cache(plugin, undefined, PluginsService.NPM_CONFIG).wait();
            if (dependencyData.nativescript) {
                var pluginData_1 = _this.convertToPluginData(dependencyData);
                var action = function (pluginDestinationPath, platform, platformData) {
                    return (function () {
                        _this.isPluginDataValidForPlatform(pluginData_1, platform).wait();
                    }).future()();
                };
                _this.executeForAllInstalledPlatforms(action).wait();
                try {
                    _this.$pluginVariablesService.savePluginVariablesInProjectFile(pluginData_1).wait();
                    _this.executeNpmCommand(PluginsService.INSTALL_COMMAND_NAME, plugin).wait();
                }
                catch (err) {
                    _this.$projectDataService.initialize(_this.$projectData.projectDir);
                    _this.$projectDataService.removeProperty(_this.$pluginVariablesService.getPluginVariablePropertyName(pluginData_1.name)).wait();
                    _this.$projectDataService.removeDependency(pluginData_1.name).wait();
                    throw err;
                }
                _this.$logger.out("Successfully installed plugin " + dependencyData.name + ".");
            }
            else {
                _this.$errors.failWithoutHelp(plugin + " is not a valid NativeScript plugin. Verify that the plugin package.json file contains a nativescript key and try again.");
            }
        }).future()();
    };
    PluginsService.prototype.remove = function (pluginName) {
        var _this = this;
        return (function () {
            var removePluginNativeCodeAction = function (modulesDestinationPath, platform, platformData) {
                return (function () {
                    var pluginData = _this.convertToPluginData(_this.getNodeModuleData(pluginName).wait());
                    platformData.platformProjectService.removePluginNativeCode(pluginData).wait();
                }).future()();
            };
            _this.$pluginVariablesService.removePluginVariablesFromProjectFile(pluginName.toLowerCase()).wait();
            _this.executeForAllInstalledPlatforms(removePluginNativeCodeAction).wait();
            _this.executeNpmCommand(PluginsService.UNINSTALL_COMMAND_NAME, pluginName).wait();
            var showMessage = true;
            var action = function (modulesDestinationPath, platform, platformData) {
                return (function () {
                    shelljs.rm("-rf", path.join(modulesDestinationPath, pluginName));
                    _this.$logger.out("Successfully removed plugin " + pluginName + " for " + platform + ".");
                    showMessage = false;
                }).future()();
            };
            _this.executeForAllInstalledPlatforms(action).wait();
            if (showMessage) {
                _this.$logger.out("Succsessfully removed plugin " + pluginName);
            }
        }).future()();
    };
    PluginsService.prototype.getAvailable = function (filter) {
        var silent = true;
        return this.$npm.search(filter, silent);
    };
    PluginsService.prototype.prepare = function (dependencyData, platform) {
        var _this = this;
        return (function () {
            platform = platform.toLowerCase();
            var platformData = _this.$platformsData.getPlatformData(platform);
            var pluginData = _this.convertToPluginData(dependencyData);
            var appFolderExists = _this.$fs.exists(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait();
            if (appFolderExists) {
                _this.preparePluginScripts(pluginData, platform);
                _this.preparePluginNativeCode(pluginData, platform);
                _this.$logger.out("Successfully prepared plugin " + pluginData.name + " for " + platform + ".");
            }
        }).future()();
    };
    PluginsService.prototype.preparePluginScripts = function (pluginData, platform) {
        var platformData = this.$platformsData.getPlatformData(platform);
        var pluginScriptsDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
        var scriptsDestinationExists = this.$fs.exists(pluginScriptsDestinationPath).wait();
        if (!scriptsDestinationExists) {
            return;
        }
        if (!this.isPluginDataValidForPlatform(pluginData, platform).wait()) {
            shelljs.rm("-rf", path.join(pluginScriptsDestinationPath, pluginData.name, "platforms"));
            return;
        }
        this.$projectFilesManager.processPlatformSpecificFiles(pluginScriptsDestinationPath, platform).wait();
    };
    PluginsService.prototype.preparePluginNativeCode = function (pluginData, platform) {
        var platformData = this.$platformsData.getPlatformData(platform);
        var pluginScriptsDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
        pluginData.pluginPlatformsFolderPath = function (_platform) { return path.join(pluginData.fullPath, "platforms", _platform); };
        platformData.platformProjectService.preparePluginNativeCode(pluginData).wait();
        shelljs.rm("-rf", path.join(pluginScriptsDestinationPath, pluginData.name, "platforms"));
    };
    PluginsService.prototype.ensureAllDependenciesAreInstalled = function () {
        var _this = this;
        return (function () {
            var installedDependencies = _this.$fs.exists(_this.nodeModulesPath).wait() ? _this.$fs.readDirectory(_this.nodeModulesPath).wait() : [];
            _(installedDependencies)
                .filter(function (dependencyName) { return _.startsWith(dependencyName, "@"); })
                .each(function (scopedDependencyDir) {
                var contents = _this.$fs.readDirectory(path.join(_this.nodeModulesPath, scopedDependencyDir)).wait();
                installedDependencies = installedDependencies.concat.apply(installedDependencies, contents.map(function (dependencyName) { return (scopedDependencyDir + "/" + dependencyName); }));
            });
            var packageJsonContent = _this.$fs.readJson(_this.getPackageJsonFilePath()).wait();
            var allDependencies = _.keys(packageJsonContent.dependencies).concat(_.keys(packageJsonContent.devDependencies));
            var notInstalledDependencies = _.difference(allDependencies, installedDependencies);
            if (_this.$options.force || notInstalledDependencies.length) {
                _this.$logger.trace("Npm install will be called from CLI. Force option is: ", _this.$options.force, " Not installed dependencies are: ", notInstalledDependencies);
                _this.$npm.install(_this.$projectData.projectDir, _this.$projectData.projectDir, { "ignore-scripts": _this.$options.ignoreScripts }).wait();
            }
        }).future()();
    };
    PluginsService.prototype.getAllInstalledPlugins = function () {
        var _this = this;
        return (function () {
            var nodeModules = _this.getAllInstalledModules().wait().map(function (nodeModuleData) { return _this.convertToPluginData(nodeModuleData); });
            return _.filter(nodeModules, function (nodeModuleData) { return nodeModuleData && nodeModuleData.isPlugin; });
        }).future()();
    };
    PluginsService.prototype.getDependenciesFromPackageJson = function () {
        var _this = this;
        return (function () {
            var packageJson = _this.$fs.readJson(_this.getPackageJsonFilePath()).wait();
            var dependencies = _this.getBasicPluginInformation(packageJson.dependencies);
            var devDependencies = _this.getBasicPluginInformation(packageJson.devDependencies);
            return {
                dependencies: dependencies,
                devDependencies: devDependencies
            };
        }).future()();
    };
    PluginsService.prototype.getBasicPluginInformation = function (dependencies) {
        return _.map(dependencies, function (version, key) { return ({
            name: key,
            version: version
        }); });
    };
    Object.defineProperty(PluginsService.prototype, "nodeModulesPath", {
        get: function () {
            return path.join(this.$projectData.projectDir, "node_modules");
        },
        enumerable: true,
        configurable: true
    });
    PluginsService.prototype.getPackageJsonFilePath = function () {
        return path.join(this.$projectData.projectDir, "package.json");
    };
    PluginsService.prototype.getPackageJsonFilePathForModule = function (moduleName) {
        return path.join(this.nodeModulesPath, moduleName, "package.json");
    };
    PluginsService.prototype.getDependencies = function () {
        var packageJsonFilePath = this.getPackageJsonFilePath();
        return _.keys(require(packageJsonFilePath).dependencies);
    };
    PluginsService.prototype.getNodeModuleData = function (module) {
        var _this = this;
        return (function () {
            if (!_this.$fs.exists(module).wait() || path.basename(module) !== "package.json") {
                module = _this.getPackageJsonFilePathForModule(module);
            }
            var data = _this.$fs.readJson(module).wait();
            return {
                name: data.name,
                version: data.version,
                fullPath: path.dirname(module),
                isPlugin: data.nativescript !== undefined,
                moduleInfo: data.nativescript
            };
        }).future()();
    };
    PluginsService.prototype.convertToPluginData = function (cacheData) {
        var pluginData = {};
        pluginData.name = cacheData.name;
        pluginData.version = cacheData.version;
        pluginData.fullPath = cacheData.directory || path.dirname(this.getPackageJsonFilePathForModule(cacheData.name));
        pluginData.isPlugin = !!cacheData.nativescript || !!cacheData.moduleInfo;
        pluginData.pluginPlatformsFolderPath = function (platform) { return path.join(pluginData.fullPath, "platforms", platform); };
        var data = cacheData.nativescript || cacheData.moduleInfo;
        if (pluginData.isPlugin) {
            pluginData.platformsData = data.platforms;
            pluginData.pluginVariables = data.variables;
        }
        return pluginData;
    };
    PluginsService.prototype.ensure = function () {
        var _this = this;
        return (function () {
            _this.ensureAllDependenciesAreInstalled().wait();
            _this.$fs.ensureDirectoryExists(_this.nodeModulesPath).wait();
        }).future()();
    };
    PluginsService.prototype.getAllInstalledModules = function () {
        var _this = this;
        return (function () {
            _this.ensure().wait();
            var nodeModules = _this.getDependencies();
            return _.map(nodeModules, function (nodeModuleName) { return _this.getNodeModuleData(nodeModuleName).wait(); });
        }).future()();
    };
    PluginsService.prototype.executeNpmCommand = function (npmCommandName, npmCommandArguments) {
        var _this = this;
        return (function () {
            var result = "";
            if (npmCommandName === PluginsService.INSTALL_COMMAND_NAME) {
                result = _this.$npm.install(npmCommandArguments, _this.$projectData.projectDir, PluginsService.NPM_CONFIG).wait();
            }
            else if (npmCommandName === PluginsService.UNINSTALL_COMMAND_NAME) {
                result = _this.$npm.uninstall(npmCommandArguments, PluginsService.NPM_CONFIG, _this.$projectData.projectDir).wait();
                if (!result || !result.length) {
                    return npmCommandArguments.toLowerCase();
                }
            }
            return _this.parseNpmCommandResult(result);
        }).future()();
    };
    PluginsService.prototype.parseNpmCommandResult = function (npmCommandResult) {
        return npmCommandResult[0][0].split("@")[0];
    };
    PluginsService.prototype.executeForAllInstalledPlatforms = function (action) {
        var _this = this;
        return (function () {
            var availablePlatforms = _.keys(_this.$platformsData.availablePlatforms);
            _.each(availablePlatforms, function (platform) {
                var isPlatformInstalled = _this.$fs.exists(path.join(_this.$projectData.platformsDir, platform.toLowerCase())).wait();
                if (isPlatformInstalled) {
                    var platformData = _this.$platformsData.getPlatformData(platform.toLowerCase());
                    var pluginDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
                    action(pluginDestinationPath, platform.toLowerCase(), platformData).wait();
                }
            });
        }).future()();
    };
    PluginsService.prototype.getInstalledFrameworkVersion = function (platform) {
        var _this = this;
        return (function () {
            var platformData = _this.$platformsData.getPlatformData(platform);
            _this.$projectDataService.initialize(_this.$projectData.projectDir);
            var frameworkData = _this.$projectDataService.getValue(platformData.frameworkPackageName).wait();
            return frameworkData.version;
        }).future()();
    };
    PluginsService.prototype.isPluginDataValidForPlatform = function (pluginData, platform) {
        var _this = this;
        return (function () {
            var isValid = true;
            var installedFrameworkVersion = _this.getInstalledFrameworkVersion(platform).wait();
            var pluginPlatformsData = pluginData.platformsData;
            if (pluginPlatformsData) {
                var pluginVersion = pluginPlatformsData[platform];
                if (!pluginVersion) {
                    _this.$logger.warn(pluginData.name + " is not supported for " + platform + ".");
                    isValid = false;
                }
                else if (semver.gt(pluginVersion, installedFrameworkVersion)) {
                    _this.$logger.warn(pluginData.name + " " + pluginVersion + " for " + platform + " is not compatible with the currently installed framework version " + installedFrameworkVersion + ".");
                    isValid = false;
                }
            }
            return isValid;
        }).future()();
    };
    PluginsService.INSTALL_COMMAND_NAME = "install";
    PluginsService.UNINSTALL_COMMAND_NAME = "uninstall";
    PluginsService.NPM_CONFIG = {
        save: true
    };
    return PluginsService;
}());
exports.PluginsService = PluginsService;
$injector.register("pluginsService", PluginsService);
