"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const os_1 = require("os");
const path = require("path");
const constants_1 = require("../constants");
const helpers_1 = require("../common/helpers");
class CocoaPodsService {
    constructor($cocoaPodsPlatformManager, $fs, $childProcess, $errors, $xcprojService, $logger, $config, $xcconfigService) {
        this.$cocoaPodsPlatformManager = $cocoaPodsPlatformManager;
        this.$fs = $fs;
        this.$childProcess = $childProcess;
        this.$errors = $errors;
        this.$xcprojService = $xcprojService;
        this.$logger = $logger;
        this.$config = $config;
        this.$xcconfigService = $xcconfigService;
    }
    getPodfileHeader(targetName) {
        return `use_frameworks!${os_1.EOL}${os_1.EOL}target "${targetName}" do${os_1.EOL}`;
    }
    getPodfileFooter() {
        return `${os_1.EOL}end`;
    }
    getProjectPodfilePath(projectRoot) {
        return path.join(projectRoot, constants_1.PODFILE_NAME);
    }
    executePodInstall(projectRoot, xcodeProjPath) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                yield this.$childProcess.exec("which pod");
                yield this.$childProcess.exec("which xcodeproj");
            }
            catch (e) {
                this.$errors.failWithoutHelp("CocoaPods or ruby gem 'xcodeproj' is not installed. Run `sudo gem install cocoapods` and try again.");
            }
            yield this.$xcprojService.verifyXcproj({ shouldFail: true });
            this.$logger.info("Installing pods...");
            const podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod";
            const podInstallResult = yield this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: projectRoot, stdio: ['pipe', process.stdout, process.stdout] }, { throwError: false });
            if (podInstallResult.exitCode !== 0) {
                this.$errors.failWithoutHelp(`'${podTool} install' command failed.${podInstallResult.stderr ? " Error is: " + podInstallResult.stderr : ""}`);
            }
            if ((yield this.$xcprojService.getXcprojInfo()).shouldUseXcproj) {
                yield this.$childProcess.spawnFromEvent("xcproj", ["--project", xcodeProjPath, "touch"], "close");
            }
            return podInstallResult;
        });
    }
    mergePodXcconfigFile(projectData, platformData) {
        return __awaiter(this, void 0, void 0, function* () {
            const podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${projectData.projectName}`);
            const podFolder = path.join(platformData.projectRoot, podFilesRootDirName);
            if (this.$fs.exists(podFolder)) {
                const pluginsXcconfigFilePaths = this.$xcconfigService.getPluginsXcconfigFilePaths(platformData.projectRoot);
                for (const configuration in pluginsXcconfigFilePaths) {
                    const pluginsXcconfigFilePath = pluginsXcconfigFilePaths[configuration];
                    const podXcconfigFilePath = path.join(podFolder, `Pods-${projectData.projectName}.${configuration}.xcconfig`);
                    yield this.$xcconfigService.mergeFiles(podXcconfigFilePath, pluginsXcconfigFilePath);
                }
            }
        });
    }
    applyPodfileFromAppResources(projectData, platformData) {
        return __awaiter(this, void 0, void 0, function* () {
            const { projectRoot, normalizedPlatformName } = platformData;
            const mainPodfilePath = path.join(projectData.appResourcesDirectoryPath, normalizedPlatformName, constants_1.PODFILE_NAME);
            const projectPodfilePath = this.getProjectPodfilePath(projectRoot);
            if (this.$fs.exists(projectPodfilePath) || this.$fs.exists(mainPodfilePath)) {
                yield this.applyPodfileToProject(constants_1.NS_BASE_PODFILE, mainPodfilePath, projectData, projectRoot);
            }
        });
    }
    applyPodfileToProject(moduleName, podfilePath, projectData, nativeProjectPath) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.$fs.exists(podfilePath)) {
                this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
                return;
            }
            const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName);
            const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath);
            const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : "";
            if (projectPodfileContent.indexOf(podfileContent) === -1) {
                this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
                let finalPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.getPodfileContentWithoutTarget(projectData, this.$fs.readText(pathToProjectPodfile)) : "";
                if (podfileContent.indexOf(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME) !== -1) {
                    finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, podfileContent);
                }
                if (podfilePlatformData) {
                    finalPodfileContent = this.$cocoaPodsPlatformManager.addPlatformSection(projectData, podfilePlatformData, finalPodfileContent);
                }
                finalPodfileContent = `${finalPodfileContent.trim()}${os_1.EOL}${os_1.EOL}${podfileContent.trim()}${os_1.EOL}`;
                this.saveProjectPodfile(projectData, finalPodfileContent, nativeProjectPath);
            }
        });
    }
    removePodfileFromProject(moduleName, podfilePath, projectData, projectRoot) {
        if (this.$fs.exists(this.getProjectPodfilePath(projectRoot))) {
            let projectPodFileContent = this.$fs.readText(this.getProjectPodfilePath(projectRoot));
            const regExpToRemove = new RegExp(`${this.getPluginPodfileHeader(podfilePath)}[\\s\\S]*?${this.getPluginPodfileEnd()}`, "mg");
            projectPodFileContent = projectPodFileContent.replace(regExpToRemove, "");
            projectPodFileContent = this.removePostInstallHook(moduleName, projectPodFileContent);
            projectPodFileContent = this.$cocoaPodsPlatformManager.removePlatformSection(moduleName, projectPodFileContent, podfilePath);
            const defaultPodfileBeginning = this.getPodfileHeader(projectData.projectName);
            const defaultContentWithPostInstallHook = `${defaultPodfileBeginning}${this.getPostInstallHookHeader()}end${os_1.EOL}end`;
            const defaultContentWithoutPostInstallHook = `${defaultPodfileBeginning}${os_1.EOL}end`;
            const trimmedProjectPodFileContent = projectPodFileContent.trim();
            if (!trimmedProjectPodFileContent || trimmedProjectPodFileContent === defaultContentWithPostInstallHook || trimmedProjectPodFileContent === defaultContentWithoutPostInstallHook) {
                this.$fs.deleteFile(this.getProjectPodfilePath(projectRoot));
            }
            else {
                this.$fs.writeFile(this.getProjectPodfilePath(projectRoot), projectPodFileContent);
            }
        }
    }
    getPluginPodfilePath(pluginData) {
        const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(constants_1.PluginNativeDirNames.iOS);
        const pluginPodFilePath = path.join(pluginPlatformsFolderPath, constants_1.PODFILE_NAME);
        return pluginPodFilePath;
    }
    addPostInstallHook(replacedFunctions, finalPodfileContent, pluginPodfileContent) {
        const postInstallHookStart = this.getPostInstallHookHeader();
        let postInstallHookContent = "";
        _.each(replacedFunctions, rubyFunction => {
            let functionExecution = rubyFunction.functionName;
            if (rubyFunction.functionParameters && rubyFunction.functionParameters.length) {
                functionExecution = `${functionExecution} ${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}`;
            }
            postInstallHookContent += `  ${functionExecution}${os_1.EOL}`;
        });
        if (postInstallHookContent) {
            const index = finalPodfileContent.indexOf(postInstallHookStart);
            if (index !== -1) {
                const regExp = new RegExp(`(${helpers_1.regExpEscape(postInstallHookStart)}[\\s\\S]*?)(\\bend\\b)`, "m");
                finalPodfileContent = finalPodfileContent.replace(regExp, `$1${postInstallHookContent.trimRight()}${os_1.EOL}$2`);
            }
            else {
                if (finalPodfileContent.length > 0) {
                    finalPodfileContent += `${os_1.EOL}${os_1.EOL}`;
                }
                const postInstallHook = `${postInstallHookStart}${postInstallHookContent}end`;
                finalPodfileContent = `${finalPodfileContent}${postInstallHook}`;
            }
        }
        return finalPodfileContent;
    }
    getPodfileContentWithoutTarget(projectData, projectPodfileContent) {
        const podFileHeader = this.getPodfileHeader(projectData.projectName);
        if (_.startsWith(projectPodfileContent, podFileHeader)) {
            projectPodfileContent = projectPodfileContent.substr(podFileHeader.length);
            const podFileFooter = this.getPodfileFooter();
            if (_.endsWith(projectPodfileContent, podFileFooter)) {
                projectPodfileContent = projectPodfileContent.substr(0, projectPodfileContent.length - podFileFooter.length);
            }
        }
        return projectPodfileContent.trim();
    }
    saveProjectPodfile(projectData, projectPodfileContent, projectRoot) {
        projectPodfileContent = this.getPodfileContentWithoutTarget(projectData, projectPodfileContent);
        const podFileHeader = this.getPodfileHeader(projectData.projectName);
        const podFileFooter = this.getPodfileFooter();
        const contentToWrite = `${podFileHeader}${projectPodfileContent}${podFileFooter}`;
        const projectPodfilePath = this.getProjectPodfilePath(projectRoot);
        this.$fs.writeFile(projectPodfilePath, contentToWrite);
    }
    removePostInstallHook(moduleName, projectPodFileContent) {
        const regExp = new RegExp(`^.*?${this.getHookBasicFuncNameForPlugin(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, moduleName)}.*?$\\r?\\n`, "gm");
        projectPodFileContent = projectPodFileContent.replace(regExp, "");
        return projectPodFileContent;
    }
    getHookBasicFuncNameForPlugin(hookName, pluginName) {
        const replacedPluginName = pluginName.replace(/_/g, "___").replace(/[^A-Za-z0-9_]/g, "_");
        return `${hookName}${replacedPluginName}`;
    }
    replaceHookContent(hookName, podfileContent, pluginName) {
        const hookStart = `${hookName} do`;
        const hookDefinitionRegExp = new RegExp(`${hookStart} *(\\|(\\w+)\\|)?`, "g");
        const newFunctions = [];
        const replacedContent = podfileContent.replace(hookDefinitionRegExp, (substring, firstGroup, secondGroup, index) => {
            const newFunctionName = `${this.getHookBasicFuncNameForPlugin(hookName, pluginName)}_${newFunctions.length}`;
            let newDefinition = `def ${newFunctionName}`;
            const rubyFunction = { functionName: newFunctionName };
            if (firstGroup && secondGroup) {
                newDefinition = `${newDefinition} (${secondGroup})`;
                rubyFunction.functionParameters = secondGroup;
            }
            newFunctions.push(rubyFunction);
            return newDefinition;
        });
        return { replacedContent, newFunctions };
    }
    getPluginPodfileHeader(pluginPodFilePath) {
        return `# Begin Podfile - ${pluginPodFilePath}`;
    }
    getPluginPodfileEnd() {
        return `# End Podfile${os_1.EOL}`;
    }
    getPostInstallHookHeader() {
        return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${os_1.EOL}`;
    }
    buildPodfileContent(pluginPodFilePath, pluginName) {
        const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
        const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
        const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
        return {
            podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${os_1.EOL}${replacedContent}${os_1.EOL}${this.getPluginPodfileEnd()}`,
            replacedFunctions: data.newFunctions,
            podfilePlatformData
        };
    }
}
CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME = "post_install";
CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME = "installer";
exports.CocoaPodsService = CocoaPodsService;
$injector.register("cocoapodsService", CocoaPodsService);
