"use strict";
const constants = require("../constants");
const osenv = require("osenv");
const path = require("path");
const shelljs = require("shelljs");
class ProjectService {
    constructor($npm, $errors, $fs, $logger, $projectDataService, $projectHelper, $projectNameService, $projectTemplatesService, $options) {
        this.$npm = $npm;
        this.$errors = $errors;
        this.$fs = $fs;
        this.$logger = $logger;
        this.$projectDataService = $projectDataService;
        this.$projectHelper = $projectHelper;
        this.$projectNameService = $projectNameService;
        this.$projectTemplatesService = $projectTemplatesService;
        this.$options = $options;
    }
    createProject(projectName, selectedTemplate) {
        return (() => {
            if (!projectName) {
                this.$errors.fail("You must specify <App name> when creating a new project.");
            }
            projectName = this.$projectNameService.ensureValidName(projectName, { force: this.$options.force }).wait();
            let projectId = this.$options.appid || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX);
            let projectDir = path.join(path.resolve(this.$options.path || "."), projectName);
            this.$fs.createDirectory(projectDir).wait();
            if (this.$fs.exists(projectDir).wait() && !this.$fs.isEmptyDir(projectDir).wait()) {
                this.$errors.fail("Path already exists and is not empty %s", projectDir);
            }
            this.createPackageJson(projectDir, projectId).wait();
            let customAppPath = this.getCustomAppPath();
            if (customAppPath) {
                customAppPath = path.resolve(customAppPath);
                if (!this.$fs.exists(customAppPath).wait()) {
                    this.$errors.failWithoutHelp(`The specified path "${customAppPath}" doesn't exist. Check that you specified the path correctly and try again.`);
                }
                let customAppContents = this.$fs.enumerateFilesInDirectorySync(customAppPath);
                if (customAppContents.length === 0) {
                    this.$errors.failWithoutHelp(`The specified path "${customAppPath}" is empty directory.`);
                }
            }
            this.$logger.trace("Creating a new NativeScript project with name %s and id %s at location %s", projectName, projectId, projectDir);
            let projectAppDirectory = path.join(projectDir, constants.APP_FOLDER_NAME);
            let appPath = null;
            if (customAppPath) {
                this.$logger.trace("Using custom app from %s", customAppPath);
                let relativePathFromSourceToTarget = path.relative(customAppPath, projectAppDirectory);
                if (relativePathFromSourceToTarget !== projectAppDirectory) {
                    let doesRelativePathGoUpAtLeastOneDir = relativePathFromSourceToTarget.split(path.sep)[0] === "..";
                    if (!doesRelativePathGoUpAtLeastOneDir) {
                        this.$errors.fail("Project dir %s must not be created at/inside the template used to create the project %s.", projectDir, customAppPath);
                    }
                }
                this.$logger.trace("Copying custom app into %s", projectAppDirectory);
                appPath = customAppPath;
            }
            else {
                let templatePath = this.$projectTemplatesService.prepareTemplate(selectedTemplate, projectDir).wait();
                this.$logger.trace(`Copying application from '${templatePath}' into '${projectAppDirectory}'.`);
                let templatePackageJson = this.$fs.readJson(path.join(templatePath, "package.json")).wait();
                selectedTemplate = templatePackageJson.name;
                appPath = templatePath;
            }
            try {
                this.createProjectCore(projectDir, appPath, projectId).wait();
                this.mergeProjectAndTemplateProperties(projectDir, appPath).wait();
                this.$npm.install(projectDir, projectDir, { "ignore-scripts": this.$options.ignoreScripts }).wait();
                selectedTemplate = selectedTemplate || "";
                let templateName = (constants.RESERVED_TEMPLATE_NAMES[selectedTemplate.toLowerCase()] || selectedTemplate) || constants.RESERVED_TEMPLATE_NAMES["default"];
                this.$npm.uninstall(selectedTemplate, { save: true }, projectDir).wait();
                this.$logger.trace(`Using NativeScript verified template: ${templateName} with version undefined.`);
            }
            catch (err) {
                this.$fs.deleteDirectory(projectDir).wait();
                throw err;
            }
            this.$logger.printMarkdown("Project `%s` was successfully created.", projectName);
        }).future()();
    }
    mergeProjectAndTemplateProperties(projectDir, templatePath) {
        return (() => {
            let templatePackageJsonPath = path.join(templatePath, constants.PACKAGE_JSON_FILE_NAME);
            if (this.$fs.exists(templatePackageJsonPath).wait()) {
                let projectPackageJsonPath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME);
                let projectPackageJsonData = this.$fs.readJson(projectPackageJsonPath).wait();
                this.$logger.trace("Initial project package.json data: ", projectPackageJsonData);
                let templatePackageJsonData = this.$fs.readJson(templatePackageJsonPath).wait();
                if (projectPackageJsonData.dependencies || templatePackageJsonData.dependencies) {
                    projectPackageJsonData.dependencies = this.mergeDependencies(projectPackageJsonData.dependencies, templatePackageJsonData.dependencies);
                }
                if (projectPackageJsonData.devDependencies || templatePackageJsonData.devDependencies) {
                    projectPackageJsonData.devDependencies = this.mergeDependencies(projectPackageJsonData.devDependencies, templatePackageJsonData.devDependencies);
                }
                this.$logger.trace("New project package.json data: ", projectPackageJsonData);
                this.$fs.writeJson(projectPackageJsonPath, projectPackageJsonData).wait();
            }
            else {
                this.$logger.trace(`Template ${templatePath} does not have ${constants.PACKAGE_JSON_FILE_NAME} file.`);
            }
        }).future()();
    }
    mergeDependencies(projectDependencies, templateDependencies) {
        this.$logger.trace("Merging dependencies, projectDependencies are: ", projectDependencies, " templateDependencies are: ", templateDependencies);
        projectDependencies = projectDependencies || {};
        _.extend(projectDependencies, templateDependencies || {});
        let sortedDeps = {};
        let dependenciesNames = _.keys(projectDependencies).sort();
        _.each(dependenciesNames, (key) => {
            sortedDeps[key] = projectDependencies[key];
        });
        this.$logger.trace("Sorted merged dependencies are: ", sortedDeps);
        return sortedDeps;
    }
    createProjectCore(projectDir, appSourcePath, projectId) {
        return (() => {
            this.$fs.ensureDirectoryExists(projectDir).wait();
            let appDestinationPath = path.join(projectDir, constants.APP_FOLDER_NAME);
            this.$fs.createDirectory(appDestinationPath).wait();
            shelljs.cp('-R', path.join(appSourcePath, "*"), appDestinationPath);
            shelljs.cp('-R', path.join(appSourcePath, ".*"), appDestinationPath);
            this.$fs.createDirectory(path.join(projectDir, "platforms")).wait();
            let tnsModulesVersion = this.$options.tnsModulesVersion;
            let packageName = constants.TNS_CORE_MODULES_NAME;
            if (tnsModulesVersion) {
                packageName = `${packageName}@${tnsModulesVersion}`;
            }
            this.$npm.install(packageName, projectDir, { save: true, "save-exact": true }).wait();
        }).future()();
    }
    createPackageJson(projectDir, projectId) {
        return (() => {
            this.$projectDataService.initialize(projectDir);
            this.$projectDataService.setValue("id", projectId).wait();
        }).future()();
    }
    getCustomAppPath() {
        let customAppPath = this.$options.copyFrom || this.$options.linkTo;
        if (customAppPath) {
            if (customAppPath.indexOf("http://") === 0) {
                this.$errors.fail("Only local paths for custom app are supported.");
            }
            if (customAppPath.substr(0, 1) === '~') {
                customAppPath = path.join(osenv.home(), customAppPath.substr(1));
            }
        }
        return customAppPath;
    }
}
exports.ProjectService = ProjectService;
$injector.register("projectService", ProjectService);
