"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectDataService = void 0;
const path = require("path");
const project_data_1 = require("../project-data");
const constants = require("../constants");
const constants_1 = require("../constants");
const helpers_1 = require("../common/helpers");
const decorators_1 = require("../common/decorators");
const _ = require("lodash");
const yok_1 = require("../common/yok");
const semver = require("semver");
class ProjectDataService {
    constructor($fs, $staticConfig, $logger, $devicePlatformsConstants, $androidResourcesMigrationService, $injector) {
        this.$fs = $fs;
        this.$staticConfig = $staticConfig;
        this.$logger = $logger;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$androidResourcesMigrationService = $androidResourcesMigrationService;
        this.$injector = $injector;
        this.projectDataCache = {};
        try {
            const projectData = this.$injector.resolve("projectData");
            projectData.initializeProjectData();
            this.defaultProjectDir = projectData.projectDir;
            this.projectDataCache[this.defaultProjectDir] = projectData;
        }
        catch (e) {
        }
    }
    get $pluginsService() {
        return this.$injector.resolve("pluginsService");
    }
    getNSValue(projectDir, propertyName) {
        return this.getValue(projectDir, this.getNativeScriptPropertyName(propertyName));
    }
    getNSValueFromContent(jsonData, propertyName) {
        try {
            return this.getPropertyValueFromJson(jsonData, this.getNativeScriptPropertyName(propertyName));
        }
        catch (e) {
            this.$logger.trace("Failed to get NS property value from JSON project data.");
        }
        return null;
    }
    setNSValue(projectDir, key, value) {
        this.setValue(projectDir, this.getNativeScriptPropertyName(key), value);
    }
    removeNSProperty(projectDir, propertyName) {
        this.removeProperty(projectDir, this.getNativeScriptPropertyName(propertyName));
    }
    removeDependency(projectDir, dependencyName) {
        const projectFileInfo = this.getProjectFileData(projectDir);
        delete projectFileInfo.projectData[ProjectDataService.DEPENDENCIES_KEY_NAME][dependencyName];
        this.$fs.writeJson(projectFileInfo.projectFilePath, projectFileInfo.projectData);
    }
    getProjectData(projectDir) {
        projectDir = projectDir || this.defaultProjectDir;
        this.projectDataCache[projectDir] =
            this.projectDataCache[projectDir] ||
                this.$injector.resolve(project_data_1.ProjectData);
        this.projectDataCache[projectDir].initializeProjectData(projectDir);
        return this.projectDataCache[projectDir];
    }
    getProjectDataFromContent(packageJsonContent, projectDir) {
        projectDir = projectDir || this.defaultProjectDir;
        this.projectDataCache[projectDir] =
            this.projectDataCache[projectDir] ||
                this.$injector.resolve(project_data_1.ProjectData);
        this.projectDataCache[projectDir].initializeProjectDataFromContent(packageJsonContent, projectDir);
        return this.projectDataCache[projectDir];
    }
    getAssetsStructure(opts) {
        return __awaiter(this, void 0, void 0, function* () {
            const iOSAssetStructure = yield this.getIOSAssetsStructure(opts);
            const androidAssetStructure = yield this.getAndroidAssetsStructure(opts);
            this.$logger.trace("iOS Assets structure:", JSON.stringify(iOSAssetStructure, null, 2));
            this.$logger.trace("Android Assets structure:", JSON.stringify(androidAssetStructure, null, 2));
            return {
                ios: iOSAssetStructure,
                android: androidAssetStructure,
            };
        });
    }
    getIOSAssetsStructure(opts) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectDir = opts.projectDir;
            const projectData = this.getProjectData(projectDir);
            const basePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants_1.AssetConstants.iOSAssetsDirName);
            const pathToIcons = path.join(basePath, constants_1.AssetConstants.iOSIconsDirName);
            const icons = yield this.getIOSAssetSubGroup(pathToIcons);
            const pathToSplashBackgrounds = path.join(basePath, constants_1.AssetConstants.iOSSplashBackgroundsDirName);
            const splashBackgrounds = yield this.getIOSAssetSubGroup(pathToSplashBackgrounds);
            const pathToSplashCenterImages = path.join(basePath, constants_1.AssetConstants.iOSSplashCenterImagesDirName);
            const splashCenterImages = yield this.getIOSAssetSubGroup(pathToSplashCenterImages);
            const pathToSplashImages = path.join(basePath, constants_1.AssetConstants.iOSSplashImagesDirName);
            const splashImages = yield this.getIOSAssetSubGroup(pathToSplashImages);
            return {
                icons,
                splashBackgrounds,
                splashCenterImages,
                splashImages,
            };
        });
    }
    removeNSConfigProperty(projectDir, propertyName) {
        this.$logger.trace(`Removing "${propertyName}" property from nsconfig.`);
        this.updateNsConfigValue(projectDir, null, [propertyName]);
        this.$logger.trace(`"${propertyName}" property successfully removed.`);
    }
    getAndroidAssetsStructure(opts) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectDir = opts.projectDir;
            const projectData = this.getProjectData(projectDir);
            const pathToAndroidDir = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android);
            const hasMigrated = this.$androidResourcesMigrationService.hasMigrated(projectData.appResourcesDirectoryPath);
            const basePath = hasMigrated
                ? path.join(pathToAndroidDir, constants_1.SRC_DIR, constants_1.MAIN_DIR, constants_1.RESOURCES_DIR)
                : pathToAndroidDir;
            const currentStructure = this.$fs.enumerateFilesInDirectorySync(basePath);
            const content = this.getImageDefinitions().android;
            return {
                icons: this.getAndroidAssetSubGroup(content.icons, currentStructure),
                splashBackgrounds: this.getAndroidAssetSubGroup(content.splashBackgrounds, currentStructure),
                splashCenterImages: this.getAndroidAssetSubGroup(content.splashCenterImages, currentStructure),
                splashImages: null,
            };
        });
    }
    getAppExecutableFiles(projectDir) {
        const projectData = this.getProjectData(projectDir);
        let supportedFileExtension = ".js";
        if (projectData.projectType === constants_1.ProjectTypes.NgFlavorName ||
            projectData.projectType === constants_1.ProjectTypes.TsFlavorName) {
            supportedFileExtension = ".ts";
        }
        const pathToProjectNodeModules = path.join(projectDir, constants_1.NODE_MODULES_FOLDER_NAME);
        const files = this.$fs.enumerateFilesInDirectorySync(projectData.appDirectoryPath, (filePath, fstat) => {
            if (filePath.indexOf(projectData.appResourcesDirectoryPath) !== -1) {
                return false;
            }
            if (fstat.isDirectory()) {
                if (filePath === pathToProjectNodeModules) {
                    return false;
                }
                return true;
            }
            return path.extname(filePath) === supportedFileExtension;
        });
        return files;
    }
    refreshProjectData(projectDir) {
        if (this.projectDataCache[projectDir]) {
            this.projectDataCache[projectDir].initializeProjectData(projectDir);
        }
    }
    updateNsConfigValue(projectDir, updateObject, propertiesToRemove) {
        const nsConfigPath = path.join(projectDir, constants.CONFIG_FILE_NAME_JS);
        const currentNsConfig = this.getNsConfig(nsConfigPath);
        let newNsConfig = currentNsConfig;
        if (updateObject) {
            newNsConfig = _.assign(newNsConfig || this.getNsConfigDefaultObject(), updateObject);
        }
        if (newNsConfig && propertiesToRemove && propertiesToRemove.length) {
            newNsConfig = _.omit(newNsConfig, propertiesToRemove);
        }
        if (newNsConfig) {
            this.$fs.writeJson(nsConfigPath, newNsConfig);
            this.refreshProjectData(projectDir);
        }
    }
    getNsConfig(nsConfigPath) {
        let result = null;
        if (this.$fs.exists(nsConfigPath)) {
            const nsConfigContent = this.$fs.readText(nsConfigPath);
            try {
                result = helpers_1.parseJson(nsConfigContent);
            }
            catch (e) {
                this.$logger.trace("The `nsconfig` content is not a valid JSON. Parse error: ", e);
            }
        }
        return result;
    }
    getImageDefinitions() {
        const pathToImageDefinitions = path.join(__dirname, "..", "..", constants_1.CLI_RESOURCES_DIR_NAME, constants_1.AssetConstants.assets, constants_1.AssetConstants.imageDefinitionsFileName);
        const imageDefinitions = this.$fs.readJson(pathToImageDefinitions);
        return imageDefinitions;
    }
    getIOSAssetSubGroup(dirPath) {
        return __awaiter(this, void 0, void 0, function* () {
            const pathToContentJson = path.join(dirPath, constants_1.AssetConstants.iOSResourcesFileName);
            const content = (this.$fs.exists(pathToContentJson) &&
                this.$fs.readJson(pathToContentJson)) || { images: [] };
            const finalContent = { images: [] };
            const imageDefinitions = this.getImageDefinitions().ios;
            _.each(content && content.images, (image) => {
                let foundMatchingDefinition = false;
                if (image.filename) {
                    image.path = path.join(dirPath, image.filename);
                }
                if (image.size) {
                    const [width, height] = image.size
                        .toString()
                        .split(constants_1.AssetConstants.sizeDelimiter);
                    if (width && height) {
                        image.width = +width;
                        image.height = +height;
                    }
                }
                _.each(imageDefinitions, (assetSubGroup) => {
                    const assetItem = _.find(assetSubGroup, (assetElement) => assetElement.filename === image.filename &&
                        path.basename(assetElement.directory) === path.basename(dirPath));
                    if (assetItem) {
                        foundMatchingDefinition = true;
                        if (!image.width || !image.height) {
                            image.width = assetItem.width;
                            image.height = assetItem.height;
                            image.size =
                                image.size ||
                                    `${assetItem.width}${constants_1.AssetConstants.sizeDelimiter}${assetItem.height}`;
                        }
                        image.resizeOperation =
                            image.resizeOperation || assetItem.resizeOperation;
                        image.overlayImageScale =
                            image.overlayImageScale || assetItem.overlayImageScale;
                        image.scale = image.scale || assetItem.scale;
                        image.rgba = assetItem.rgba;
                        finalContent.images.push(image);
                        return false;
                    }
                });
                if (!foundMatchingDefinition) {
                    if (image.height && image.width) {
                        this.$logger.trace("Missing data for image", image, " in CLI's resource file, but we will try to generate images based on the size from Contents.json");
                        finalContent.images.push(image);
                    }
                    else if (image.filename) {
                        this.$logger.warn(`Didn't find a matching image definition for file ${path.join(path.basename(dirPath), image.filename)}. This file will be skipped from resources generation.`);
                    }
                    else {
                        this.$logger.trace(`Unable to detect data for image generation of image`, image);
                    }
                }
            });
            return finalContent;
        });
    }
    getAndroidAssetSubGroup(assetItems, realPaths) {
        const assetSubGroup = {
            images: [],
        };
        const normalizedPaths = _.map(realPaths, (p) => path.normalize(p));
        _.each(assetItems, (assetItem) => {
            _.each(normalizedPaths, (currentNormalizedPath) => {
                const imagePath = path.join(assetItem.directory, assetItem.filename);
                if (currentNormalizedPath.indexOf(path.normalize(imagePath)) !== -1) {
                    assetItem.path = currentNormalizedPath;
                    assetItem.size = `${assetItem.width}${constants_1.AssetConstants.sizeDelimiter}${assetItem.height}`;
                    assetSubGroup.images.push(assetItem);
                    return false;
                }
            });
        });
        return assetSubGroup;
    }
    getValue(projectDir, propertyName) {
        const projectData = this.getProjectFileData(projectDir).projectData;
        if (projectData) {
            try {
                return this.getPropertyValueFromJson(projectData, propertyName);
            }
            catch (err) {
                this.$logger.trace(`Error while trying to get property ${propertyName} from ${projectDir}. Error is:`, err);
            }
        }
        return null;
    }
    getNativeScriptPropertyName(propertyName) {
        return `${this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE}${constants_1.NATIVESCRIPT_PROPS_INTERNAL_DELIMITER}${propertyName}`;
    }
    getPropertyValueFromJson(jsonData, dottedPropertyName) {
        const props = dottedPropertyName.split(constants_1.NATIVESCRIPT_PROPS_INTERNAL_DELIMITER);
        let result = jsonData[props.shift()];
        for (const prop of props) {
            result = result[prop];
        }
        return result;
    }
    setValue(projectDir, key, value) {
        const projectFileInfo = this.getProjectFileData(projectDir);
        const props = key.split(constants_1.NATIVESCRIPT_PROPS_INTERNAL_DELIMITER);
        const data = projectFileInfo.projectData;
        let currentData = data;
        _.each(props, (prop, index) => {
            if (index === props.length - 1) {
                currentData[prop] = value;
            }
            else {
                currentData[prop] = currentData[prop] || Object.create(null);
            }
            currentData = currentData[prop];
        });
        this.$fs.writeJson(projectFileInfo.projectFilePath, data);
    }
    removeProperty(projectDir, propertyName) {
        const projectFileInfo = this.getProjectFileData(projectDir);
        const data = projectFileInfo.projectData;
        let currentData = data;
        const props = propertyName.split(constants_1.NATIVESCRIPT_PROPS_INTERNAL_DELIMITER);
        const propertyToDelete = props.splice(props.length - 1, 1)[0];
        _.each(props, (prop) => {
            currentData = currentData[prop];
        });
        delete currentData[propertyToDelete];
        this.$fs.writeJson(projectFileInfo.projectFilePath, data);
    }
    getProjectFileData(projectDir) {
        const projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
        const projectFileContent = this.$fs.readText(projectFilePath);
        const projectData = projectFileContent
            ? JSON.parse(projectFileContent)
            : Object.create(null);
        return {
            projectData,
            projectFilePath,
        };
    }
    getNsConfigDefaultObject(data) {
        const config = {};
        Object.assign(config, data);
        return config;
    }
    getRuntimePackage(projectDir, platform) {
        platform = platform.toLowerCase();
        const packageJson = this.$fs.readJson(path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME));
        const runtimeName = platform === "android"
            ? constants.TNS_ANDROID_RUNTIME_NAME
            : constants.TNS_IOS_RUNTIME_NAME;
        if (packageJson &&
            packageJson.nativescript &&
            packageJson.nativescript[runtimeName] &&
            packageJson.nativescript[runtimeName].version) {
            return {
                name: runtimeName,
                version: packageJson.nativescript[runtimeName].version,
            };
        }
        return this.getInstalledRuntimePackage(projectDir, platform);
    }
    getInstalledRuntimePackage(projectDir, platform) {
        const runtimePackage = this.$pluginsService
            .getDependenciesFromPackageJson(projectDir)
            .devDependencies.find((d) => {
            if (platform === "ios") {
                return [
                    constants.SCOPED_IOS_RUNTIME_NAME,
                    constants.TNS_IOS_RUNTIME_NAME,
                ].includes(d.name);
            }
            else if (platform === "android") {
                return [
                    constants.SCOPED_ANDROID_RUNTIME_NAME,
                    constants.TNS_ANDROID_RUNTIME_NAME,
                ].includes(d.name);
            }
        });
        if (runtimePackage) {
            const isRange = semver.coerce(runtimePackage.version).version !==
                runtimePackage.version;
            if (runtimePackage.version.includes("tgz") || isRange) {
                try {
                    const runtimePackageJsonPath = require.resolve(`${runtimePackage.name}/package.json`, {
                        paths: [projectDir],
                    });
                    runtimePackage.version = this.$fs.readJson(runtimePackageJsonPath).version;
                }
                catch (err) {
                    if (isRange) {
                        runtimePackage.version = semver.coerce(runtimePackage.version).version;
                        runtimePackage._coerced = true;
                    }
                    else {
                        runtimePackage.version = null;
                    }
                }
            }
            return runtimePackage;
        }
        this.$logger.trace("Could not find an installed runtime, falling back to default runtimes");
        if (platform === "ios") {
            return {
                name: constants.SCOPED_IOS_RUNTIME_NAME,
                version: null,
            };
        }
        else if (platform === "android") {
            return {
                name: constants.SCOPED_ANDROID_RUNTIME_NAME,
                version: null,
            };
        }
    }
    getNsConfigDefaultContent(data) {
        const config = this.getNsConfigDefaultObject(data);
        return JSON.stringify(config);
    }
}
ProjectDataService.DEPENDENCIES_KEY_NAME = "dependencies";
__decorate([
    decorators_1.exported("projectDataService")
], ProjectDataService.prototype, "getProjectData", null);
__decorate([
    decorators_1.exported("projectDataService")
], ProjectDataService.prototype, "getProjectDataFromContent", null);
__decorate([
    decorators_1.exported("projectDataService")
], ProjectDataService.prototype, "getAssetsStructure", null);
__decorate([
    decorators_1.exported("projectDataService")
], ProjectDataService.prototype, "getIOSAssetsStructure", null);
__decorate([
    decorators_1.exported("projectDataService")
], ProjectDataService.prototype, "getAndroidAssetsStructure", null);
__decorate([
    decorators_1.memoize({
        hashFn(projectDir, platform) {
            return projectDir + ":" + platform;
        },
        shouldCache(result) {
            if (result._coerced) {
                return false;
            }
            return !!result.version;
        },
    })
], ProjectDataService.prototype, "getInstalledRuntimePackage", null);
__decorate([
    decorators_1.exported("projectDataService")
], ProjectDataService.prototype, "getNsConfigDefaultContent", null);
exports.ProjectDataService = ProjectDataService;
yok_1.injector.register("projectDataService", ProjectDataService);
