"use strict";
const os_1 = require("os");
const Future = require("fibers/future");
const path = require("path");
const constants_1 = require("../../constants");
class ProjectBase {
    constructor($cordovaProjectCapabilities, $errors, $fs, $logger, $nativeScriptProjectCapabilities, $options, $projectConstants, $staticConfig) {
        this.$cordovaProjectCapabilities = $cordovaProjectCapabilities;
        this.$errors = $errors;
        this.$fs = $fs;
        this.$logger = $logger;
        this.$nativeScriptProjectCapabilities = $nativeScriptProjectCapabilities;
        this.$options = $options;
        this.$projectConstants = $projectConstants;
        this.$staticConfig = $staticConfig;
        this._shouldSaveProject = false;
        this._hasBuildConfigurations = false;
        this.configurationSpecificData = Object.create(null);
    }
    getShouldSaveProject() {
        return this._shouldSaveProject;
    }
    setShouldSaveProject(shouldSaveProject) {
        this._shouldSaveProject = shouldSaveProject;
    }
    get projectData() {
        this.readProjectData().wait();
        return this._projectData;
    }
    set projectData(projectData) {
        this._projectData = projectData;
    }
    getProjectDir() {
        return Future.fromResult(this.projectDir);
    }
    get capabilities() {
        let projectData = this.projectData;
        if (projectData) {
            if (projectData.Framework && projectData.Framework.toLowerCase() === constants_1.TARGET_FRAMEWORK_IDENTIFIERS.NativeScript.toLowerCase()) {
                return this.$nativeScriptProjectCapabilities;
            }
            else if (projectData.Framework && projectData.Framework.toLowerCase() === constants_1.TARGET_FRAMEWORK_IDENTIFIERS.Cordova.toLowerCase()) {
                return this.$cordovaProjectCapabilities;
            }
        }
        return null;
    }
    get hasBuildConfigurations() {
        return this._hasBuildConfigurations;
    }
    get projectInformation() {
        return {
            projectData: this.projectData,
            configurationSpecificData: this.configurationSpecificData,
            hasBuildConfigurations: this.hasBuildConfigurations,
            configurations: _.keys(this.configurationSpecificData)
        };
    }
    getAppIdentifierForPlatform(platform) {
        return (() => {
            if (!this._platformSpecificAppIdentifier) {
                this._platformSpecificAppIdentifier = this.projectData.AppIdentifier;
                if (platform &&
                    platform.toLowerCase() === this.$projectConstants.ANDROID_PLATFORM_NAME.toLowerCase() &&
                    this.projectData.Framework === constants_1.TARGET_FRAMEWORK_IDENTIFIERS.Cordova) {
                    let pathToAndroidResources = path.join(this.projectDir, this.$staticConfig.APP_RESOURCES_DIR_NAME, this.$projectConstants.ANDROID_PLATFORM_NAME);
                    let pathToAndroidManifest = path.join(pathToAndroidResources, ProjectBase.ANDROID_MANIFEST_NAME);
                    let appIdentifierInAndroidManifest = this.getAppIdentifierFromConfigFile(pathToAndroidManifest, /package\s*=\s*"(\S*)"/).wait();
                    if (appIdentifierInAndroidManifest && appIdentifierInAndroidManifest !== ProjectBase.APP_IDENTIFIER_PLACEHOLDER) {
                        this._platformSpecificAppIdentifier = appIdentifierInAndroidManifest;
                    }
                }
            }
            return this._platformSpecificAppIdentifier;
        }).future()();
    }
    validateAppIdentifier(platform) {
        return (() => {
            this.getAppIdentifierForPlatform(platform).wait();
        }).future()();
    }
    readProjectData() {
        return (() => {
            let projectDir = this.getProjectDir().wait();
            this.setShouldSaveProject(false);
            if (projectDir) {
                let projectFilePath = path.join(projectDir, this.$projectConstants.PROJECT_FILE);
                try {
                    this.projectData = this.getProjectData(projectFilePath);
                    this.validate();
                    let debugProjectFile = path.join(projectDir, this.$projectConstants.DEBUG_PROJECT_FILE_NAME);
                    if (this.$options.debug && !this.$fs.exists(debugProjectFile).wait()) {
                        this.$fs.writeJson(debugProjectFile, {}).wait();
                    }
                    let releaseProjectFile = path.join(projectDir, this.$projectConstants.RELEASE_PROJECT_FILE_NAME);
                    if (this.$options.release && !this.$fs.exists(releaseProjectFile).wait()) {
                        this.$fs.writeJson(releaseProjectFile, {}).wait();
                    }
                    _.each(this.$fs.enumerateFilesInDirectorySync(projectDir), (configProjectFile) => {
                        let configMatch = path.basename(configProjectFile).match(ProjectBase.CONFIGURATION_FROM_FILE_NAME_REGEX);
                        if (configMatch && configMatch.length > 1) {
                            let configurationName = configMatch[1];
                            let configProjectContent = this.$fs.readJson(configProjectFile).wait(), configurationLowerCase = configurationName.toLowerCase();
                            this.configurationSpecificData[configurationLowerCase] = _.merge(_.cloneDeep(this._projectData), configProjectContent);
                            this._hasBuildConfigurations = true;
                        }
                    });
                }
                catch (err) {
                    if (err.message === "FUTURE_PROJECT_VER") {
                        this.$errors.failWithoutHelp("This project is created by a newer version of AppBuilder. Upgrade AppBuilder CLI to work with it.");
                    }
                    this.$errors.failWithoutHelp("The project file %s is corrupted." + os_1.EOL +
                        "Consider restoring an earlier version from your source control or backup." + os_1.EOL +
                        "To create a new one with the default settings, delete this file and run $ appbuilder init hybrid." + os_1.EOL +
                        "Additional technical information: %s", projectFilePath, err.toString());
                }
                this.saveProjectIfNeeded();
            }
        }).future()();
    }
    getProjectData(projectFilePath) {
        let data = this.$fs.readJson(projectFilePath).wait();
        if (data.projectVersion && data.projectVersion.toString() !== "1") {
            this.$errors.fail("FUTURE_PROJECT_VER");
        }
        if (!_.has(data, "Framework")) {
            if (_.has(data, "projectType")) {
                data["Framework"] = data["projectType"];
                delete data["projectType"];
            }
            else {
                data["Framework"] = constants_1.TARGET_FRAMEWORK_IDENTIFIERS.Cordova;
            }
            this.setShouldSaveProject(true);
        }
        return data;
    }
    getAppIdentifierFromConfigFile(pathToConfigFile, regExp) {
        return (() => {
            if (this.$fs.exists(pathToConfigFile).wait()) {
                let fileContent = this.$fs.readText(pathToConfigFile).wait();
                let matches = fileContent.match(regExp);
                if (matches && matches[1]) {
                    return matches[1];
                }
            }
            return null;
        }).future()();
    }
}
ProjectBase.VALID_CONFIGURATION_CHARACTERS_REGEX = "[-_A-Za-z0-9]";
ProjectBase.CONFIGURATION_FROM_FILE_NAME_REGEX = new RegExp(`^[.](${ProjectBase.VALID_CONFIGURATION_CHARACTERS_REGEX}+?)[.]abproject$`, "i");
ProjectBase.ANDROID_MANIFEST_NAME = "AndroidManifest.xml";
ProjectBase.APP_IDENTIFIER_PLACEHOLDER = "$AppIdentifier$";
exports.ProjectBase = ProjectBase;
