"use strict";
const path = require("path");
const shell = require("shelljs");
const os = require("os");
const semver = require("semver");
const xcode = require("xcode");
const constants = require("../constants");
const helpers = require("../common/helpers");
const projectServiceBaseLib = require("./platform-project-service-base");
const Future = require("fibers/future");
const plist_merge_patch_1 = require("plist-merge-patch");
const os_1 = require("os");
const temp = require("temp");
const plist = require("plist");
const ios_mobileprovision_finder_1 = require("ios-mobileprovision-finder");
const xcode_1 = require("pbxproj-dom/xcode");
class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase {
    constructor($projectData, $fs, $childProcess, $cocoapodsService, $errors, $logger, $iOSEmulatorServices, $iOSSimResolver, $options, $injector, $projectDataService, $prompter, $config, $devicePlatformsConstants, $devicesService, $mobileHelper, $pluginVariablesService, $xcprojService) {
        super($fs, $projectData, $projectDataService);
        this.$childProcess = $childProcess;
        this.$cocoapodsService = $cocoapodsService;
        this.$errors = $errors;
        this.$logger = $logger;
        this.$iOSEmulatorServices = $iOSEmulatorServices;
        this.$iOSSimResolver = $iOSSimResolver;
        this.$options = $options;
        this.$injector = $injector;
        this.$prompter = $prompter;
        this.$config = $config;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$devicesService = $devicesService;
        this.$mobileHelper = $mobileHelper;
        this.$pluginVariablesService = $pluginVariablesService;
        this.$xcprojService = $xcprojService;
    }
    get $npmInstallationManager() {
        return this.$injector.resolve("npmInstallationManager");
    }
    get platformData() {
        let projectRoot = path.join(this.$projectData.platformsDir, "ios");
        return {
            frameworkPackageName: "tns-ios",
            normalizedPlatformName: "iOS",
            appDestinationDirectoryPath: path.join(projectRoot, this.$projectData.projectName),
            platformProjectService: this,
            emulatorServices: this.$iOSEmulatorServices,
            projectRoot: projectRoot,
            deviceBuildOutputPath: path.join(projectRoot, "build", "device"),
            emulatorBuildOutputPath: path.join(projectRoot, "build", "emulator"),
            validPackageNamesForDevice: [
                this.$projectData.projectName + ".ipa"
            ],
            validPackageNamesForEmulator: [
                this.$projectData.projectName + ".app"
            ],
            frameworkFilesExtensions: [".a", ".framework", ".bin"],
            frameworkDirectoriesExtensions: [".framework"],
            frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"],
            targetedOS: ['darwin'],
            configurationFileName: "Info.plist",
            configurationFilePath: path.join(projectRoot, this.$projectData.projectName, this.$projectData.projectName + "-Info.plist"),
            relativeToFrameworkConfigurationFilePath: path.join("__PROJECT_NAME__", "__PROJECT_NAME__-Info.plist"),
            fastLivesyncFileExtensions: [".tiff", ".tif", ".jpg", "jpeg", "gif", ".png", ".bmp", ".BMPf", ".ico", ".cur", ".xbm"]
        };
    }
    getAppResourcesDestinationDirectoryPath() {
        let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName);
        if (semver.lt(frameworkVersion, "1.3.0")) {
            return path.join(this.platformData.projectRoot, this.$projectData.projectName, "Resources", "icons");
        }
        return path.join(this.platformData.projectRoot, this.$projectData.projectName, "Resources");
    }
    validate() {
        return (() => {
            try {
                this.$childProcess.exec("which xcodebuild").wait();
            }
            catch (error) {
                this.$errors.fail("Xcode is not installed. Make sure you have Xcode installed and added to your PATH");
            }
            let xcodeBuildVersion = this.getXcodeVersion();
            if (helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) {
                this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION);
            }
        }).future()();
    }
    createProject(frameworkDir, frameworkVersion, pathToTemplate) {
        return (() => {
            this.$fs.ensureDirectoryExists(path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER));
            if (pathToTemplate) {
                this.$fs.readDirectory(frameworkDir)
                    .filter(dirName => dirName.indexOf(IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER) === -1)
                    .forEach(dirName => shell.cp("-R", path.join(frameworkDir, dirName), this.platformData.projectRoot));
                shell.cp("-rf", path.join(pathToTemplate, "*"), this.platformData.projectRoot);
            }
            else {
                shell.cp("-R", path.join(frameworkDir, "*"), this.platformData.projectRoot);
            }
        }).future()();
    }
    interpolateData() {
        return (() => {
            let projectRootFilePath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER);
            if (this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + "-Info.plist"))) {
                this.replaceFileName("-Info.plist", projectRootFilePath);
            }
            this.replaceFileName("-Prefix.pch", projectRootFilePath);
            let xcschemeDirPath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_PROJECT_EXT_NAME, "xcshareddata/xcschemes");
            let xcschemeFilePath = path.join(xcschemeDirPath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_SCHEME_EXT_NAME);
            if (this.$fs.exists(xcschemeFilePath)) {
                this.$logger.debug("Found shared scheme at xcschemeFilePath, renaming to match project name.");
                this.$logger.debug("Checkpoint 0");
                this.replaceFileContent(xcschemeFilePath);
                this.$logger.debug("Checkpoint 1");
                this.replaceFileName(IOSProjectService.XCODE_SCHEME_EXT_NAME, xcschemeDirPath);
                this.$logger.debug("Checkpoint 2");
            }
            else {
                this.$logger.debug("Copying xcscheme from template not found at " + xcschemeFilePath);
            }
            this.replaceFileName(IOSProjectService.XCODE_PROJECT_EXT_NAME, this.platformData.projectRoot);
            let pbxprojFilePath = this.pbxProjPath;
            this.replaceFileContent(pbxprojFilePath);
        }).future()();
    }
    interpolateConfigurationFile(configurationFilePath) {
        return Future.fromResult();
    }
    afterCreateProject(projectRoot) {
        this.$fs.rename(path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER), path.join(projectRoot, this.$projectData.projectName));
    }
    archive(options) {
        return (() => {
            let projectRoot = this.platformData.projectRoot;
            let archivePath = options && options.archivePath ? path.resolve(options.archivePath) : path.join(projectRoot, "/build/archive/", this.$projectData.projectName + ".xcarchive");
            let args = ["archive", "-archivePath", archivePath]
                .concat(this.xcbuildProjectArgs(projectRoot, "scheme"));
            this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
            return archivePath;
        }).future()();
    }
    exportArchive(options) {
        return (() => {
            let projectRoot = this.platformData.projectRoot;
            let archivePath = options.archivePath;
            let exportPath = path.resolve(options.exportDir || path.join(projectRoot, "/build/archive"));
            let exportFile = path.join(exportPath, this.$projectData.projectName + ".ipa");
            let plistTemplate = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
`;
            if (options && options.teamID) {
                plistTemplate += `    <key>teamID</key>
    <string>${options.teamID}</string>
`;
            }
            plistTemplate += `    <key>method</key>
    <string>app-store</string>
    <key>uploadBitcode</key>
    <false/>
    <key>uploadSymbols</key>
    <false/>
</dict>
</plist>`;
            temp.track();
            let exportOptionsPlist = temp.path({ prefix: "export-", suffix: ".plist" });
            this.$fs.writeFile(exportOptionsPlist, plistTemplate);
            let args = ["-exportArchive",
                "-archivePath", archivePath,
                "-exportPath", exportPath,
                "-exportOptionsPlist", exportOptionsPlist
            ];
            this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
            return exportFile;
        }).future()();
    }
    xcbuildProjectArgs(projectRoot, product) {
        let xcworkspacePath = path.join(projectRoot, this.$projectData.projectName + ".xcworkspace");
        if (this.$fs.exists(xcworkspacePath)) {
            return ["-workspace", xcworkspacePath, product ? "-" + product : "-scheme", this.$projectData.projectName];
        }
        else {
            let xcodeprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj");
            return ["-project", xcodeprojPath, product ? "-" + product : "-target", this.$projectData.projectName];
        }
    }
    buildProject(projectRoot, buildConfig) {
        return (() => {
            let basicArgs = [
                "-configuration", this.$options.release ? "Release" : "Debug",
                "build",
                'SHARED_PRECOMPS_DIR=' + path.join(projectRoot, 'build', 'sharedpch')
            ];
            basicArgs = basicArgs.concat(this.xcbuildProjectArgs(projectRoot));
            let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName);
            if (semver.lt(frameworkVersion, "1.4.0")) {
                basicArgs.push("-xcconfig", path.join(projectRoot, this.$projectData.projectName, "build.xcconfig"));
            }
            let buildForDevice = this.$options.forDevice || (buildConfig && buildConfig.buildForDevice);
            if (buildForDevice) {
                this.buildForDevice(projectRoot, basicArgs, buildConfig).wait();
            }
            else {
                this.buildForSimulator(projectRoot, basicArgs, buildConfig).wait();
            }
        }).future()();
    }
    buildForDevice(projectRoot, args, buildConfig) {
        return (() => {
            let defaultArchitectures = [
                'ARCHS=armv7 arm64',
                'VALID_ARCHS=armv7 arm64'
            ];
            if (!this.$options.release && !(buildConfig && buildConfig.architectures)) {
                this.$devicesService.initialize({ platform: this.$devicePlatformsConstants.iOS.toLowerCase(), deviceId: this.$options.device }).wait();
                let instances = this.$devicesService.getDeviceInstances();
                let devicesArchitectures = _(instances)
                    .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform) && d.deviceInfo.activeArchitecture)
                    .map(d => d.deviceInfo.activeArchitecture)
                    .uniq()
                    .value();
                if (devicesArchitectures.length > 0) {
                    let architectures = [
                        `ARCHS=${devicesArchitectures.join(" ")}`,
                        `VALID_ARCHS=${devicesArchitectures.join(" ")}`
                    ];
                    if (devicesArchitectures.length > 1) {
                        architectures.push('ONLY_ACTIVE_ARCH=NO');
                    }
                    if (!buildConfig) {
                        buildConfig = {};
                    }
                    buildConfig.architectures = architectures;
                }
            }
            args = args.concat((buildConfig && buildConfig.architectures) || defaultArchitectures);
            args = args.concat([
                "-sdk", "iphoneos",
                "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "device")
            ]);
            let xcodeBuildVersion = this.getXcodeVersion();
            if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) {
                buildConfig = this.getBuildConfig(projectRoot, buildConfig).wait();
            }
            if (buildConfig && buildConfig.codeSignIdentity) {
                args.push(`CODE_SIGN_IDENTITY=${buildConfig.codeSignIdentity}`);
            }
            if (buildConfig && buildConfig.mobileProvisionIdentifier) {
                args.push(`PROVISIONING_PROFILE=${buildConfig.mobileProvisionIdentifier}`);
            }
            if (buildConfig && buildConfig.teamIdentifier) {
                args.push(`DEVELOPMENT_TEAM=${buildConfig.teamIdentifier}`);
            }
            this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
            this.createIpa(projectRoot).wait();
        }).future()();
    }
    buildForSimulator(projectRoot, args, buildConfig) {
        return (() => {
            args = args.concat([
                "-sdk", "iphonesimulator",
                "ARCHS=i386 x86_64",
                "VALID_ARCHS=i386 x86_64",
                "ONLY_ACTIVE_ARCH=NO",
                "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "emulator"),
                "CODE_SIGN_IDENTITY="
            ]);
            this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
        }).future()();
    }
    createIpa(projectRoot) {
        return (() => {
            let buildOutputPath = path.join(projectRoot, "build", "device");
            let xcrunArgs = [
                "-sdk", "iphoneos",
                "PackageApplication",
                "-v", path.join(buildOutputPath, this.$projectData.projectName + ".app"),
                "-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa")
            ];
            this.$childProcess.spawnFromEvent("xcrun", xcrunArgs, "exit", { cwd: this.$options, stdio: 'inherit' }).wait();
        }).future()();
    }
    isPlatformPrepared(projectRoot) {
        return this.$fs.exists(path.join(projectRoot, this.$projectData.projectName, constants.APP_FOLDER_NAME));
    }
    deploy(deviceIdentifier) {
        return Future.fromResult();
    }
    addFramework(frameworkPath) {
        return (() => {
            this.validateFramework(frameworkPath).wait();
            let project = this.createPbxProj();
            let frameworkName = path.basename(frameworkPath, path.extname(frameworkPath));
            let frameworkBinaryPath = path.join(frameworkPath, frameworkName);
            let isDynamic = _.includes(this.$childProcess.spawnFromEvent("otool", ["-Vh", frameworkBinaryPath], "close").wait().stdout, " DYLIB ");
            let frameworkAddOptions = { customFramework: true };
            if (isDynamic) {
                frameworkAddOptions["embed"] = true;
            }
            let frameworkRelativePath = '$(SRCROOT)/' + this.getLibSubpathRelativeToProjectPath(frameworkPath);
            project.addFramework(frameworkRelativePath, frameworkAddOptions);
            this.savePbxProj(project);
        }).future()();
    }
    addStaticLibrary(staticLibPath) {
        return (() => {
            this.validateStaticLibrary(staticLibPath).wait();
            let libraryName = path.basename(staticLibPath, ".a");
            let headersSubpath = path.join(path.dirname(staticLibPath), "include", libraryName);
            let project = this.createPbxProj();
            let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(staticLibPath);
            project.addFramework(relativeStaticLibPath);
            let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath));
            project.addToHeaderSearchPaths({ relativePath: relativeHeaderSearchPath });
            this.generateModulemap(headersSubpath, libraryName);
            this.savePbxProj(project);
        }).future()();
    }
    canUpdatePlatform(installedModuleDir) {
        let currentXcodeProjectFile = this.buildPathToCurrentXcodeProjectFile();
        let currentXcodeProjectFileContent = this.$fs.readFile(currentXcodeProjectFile);
        let newXcodeProjectFile = this.buildPathToNewXcodeProjectFile(installedModuleDir);
        this.replaceFileContent(newXcodeProjectFile);
        let newXcodeProjectFileContent = this.$fs.readFile(newXcodeProjectFile);
        let contentIsTheSame = currentXcodeProjectFileContent.toString() === newXcodeProjectFileContent.toString();
        if (!contentIsTheSame) {
            this.$logger.warn(`The content of the current project file: ${currentXcodeProjectFile} and the new project file: ${newXcodeProjectFile} is different.`);
        }
        return contentIsTheSame;
    }
    provideLaunchScreenIfMissing() {
        try {
            this.$logger.trace("Checking if we need to provide compatability LaunchScreen.xib");
            let platformData = this.platformData;
            let projectPath = path.join(platformData.projectRoot, this.$projectData.projectName);
            let projectPlist = this.getInfoPlistPath();
            let plistContent = plist.parse(this.$fs.readText(projectPlist));
            let storyName = plistContent["UILaunchStoryboardName"];
            this.$logger.trace(`Examining ${projectPlist} UILaunchStoryboardName: "${storyName}".`);
            if (storyName !== "LaunchScreen") {
                this.$logger.trace("The project has its UILaunchStoryboardName set to " + storyName + " which is not the pre v2.1.0 default LaunchScreen, probably the project is migrated so we are good to go.");
                return;
            }
            let expectedStoryPath = path.join(projectPath, "Resources", "LaunchScreen.storyboard");
            if (this.$fs.exists(expectedStoryPath)) {
                this.$logger.trace("LaunchScreen.storyboard was found. Project is up to date.");
                return;
            }
            this.$logger.trace("LaunchScreen file not found at: " + expectedStoryPath);
            let expectedXibPath = path.join(projectPath, "en.lproj", "LaunchScreen.xib");
            if (this.$fs.exists(expectedXibPath)) {
                this.$logger.trace("Obsolete LaunchScreen.xib was found. It'k OK, we are probably running with iOS runtime from pre v2.1.0.");
                return;
            }
            this.$logger.trace("LaunchScreen file not found at: " + expectedXibPath);
            let isTheLaunchScreenFile = (fileName) => fileName === "LaunchScreen.xib" || fileName === "LaunchScreen.storyboard";
            let matches = this.$fs.enumerateFilesInDirectorySync(projectPath, isTheLaunchScreenFile, { enumerateDirectories: false });
            if (matches.length > 0) {
                this.$logger.trace("Found LaunchScreen by slowly traversing all files here: " + matches + "\nConsider moving the LaunchScreen so it could be found at: " + expectedStoryPath);
                return;
            }
            let compatabilityXibPath = path.join(projectPath, "Resources", "LaunchScreen.xib");
            this.$logger.warn(`Failed to find LaunchScreen.storyboard but it was specified in the Info.plist.
Consider updating the resources in app/App_Resources/iOS/.
A good starting point would be to create a new project and diff the changes with your current one.
Also the following repo may be helpful: https://github.com/NativeScript/template-hello-world/tree/master/App_Resources/iOS
We will now place an empty obsolete compatability white screen LauncScreen.xib for you in ${path.relative(this.$projectData.projectDir, compatabilityXibPath)} so your app may appear as it did in pre v2.1.0 versions of the ios runtime.`);
            let content = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6751" systemVersion="14A389" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB">
            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
            <nil key="simulatedStatusBarMetrics"/>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <point key="canvasLocation" x="548" y="455"/>
        </view>
    </objects>
</document>`;
            try {
                this.$fs.createDirectory(path.dirname(compatabilityXibPath));
                this.$fs.writeFile(compatabilityXibPath, content);
            }
            catch (e) {
                this.$logger.warn("We have failed to add compatability LaunchScreen.xib due to: " + e);
            }
        }
        catch (e) {
            this.$logger.warn("We have failed to check if we need to add a compatability LaunchScreen.xib due to: " + e);
        }
    }
    prepareProject() {
        let project = this.createPbxProj();
        this.provideLaunchScreenIfMissing();
        let resources = project.pbxGroupByName("Resources");
        if (resources) {
            let references = project.pbxFileReferenceSection();
            let xcodeProjectImages = _.map(resources.children, resource => this.replace(references[resource.value].name));
            this.$logger.trace("Images from Xcode project");
            this.$logger.trace(xcodeProjectImages);
            let appResourcesImages = this.$fs.readDirectory(this.getAppResourcesDestinationDirectoryPath());
            this.$logger.trace("Current images from App_Resources");
            this.$logger.trace(appResourcesImages);
            let imagesToAdd = _.difference(appResourcesImages, xcodeProjectImages);
            this.$logger.trace(`New images to add into xcode project: ${imagesToAdd.join(", ")}`);
            _.each(imagesToAdd, image => project.addResourceFile(path.relative(this.platformData.projectRoot, path.join(this.getAppResourcesDestinationDirectoryPath(), image))));
            let imagesToRemove = _.difference(xcodeProjectImages, appResourcesImages);
            this.$logger.trace(`Images to remove from xcode project: ${imagesToRemove.join(", ")}`);
            _.each(imagesToRemove, image => project.removeResourceFile(path.join(this.getAppResourcesDestinationDirectoryPath(), image)));
            this.savePbxProj(project);
        }
    }
    prepareAppResources(appResourcesDirectoryPath) {
        let platformFolder = path.join(appResourcesDirectoryPath, this.platformData.normalizedPlatformName);
        let filterFile = (filename) => this.$fs.deleteFile(path.join(platformFolder, filename));
        filterFile(this.platformData.configurationFileName);
        this.$fs.deleteDirectory(this.getAppResourcesDestinationDirectoryPath());
    }
    processConfigurationFilesFromAppResources() {
        return (() => {
            this.mergeInfoPlists().wait();
            this.mergeProjectXcconfigFiles().wait();
            _(this.getAllInstalledPlugins().wait())
                .map(pluginData => this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.platformData.configurationFilePath).wait())
                .value();
            this.$pluginVariablesService.interpolateAppIdentifier(this.platformData.configurationFilePath);
        }).future()();
    }
    getInfoPlistPath() {
        return this.$options.baseConfig ||
            path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, this.platformData.configurationFileName);
    }
    ensureConfigurationFileInAppResources() {
        return null;
    }
    mergeInfoPlists() {
        return (() => {
            let projectDir = this.$projectData.projectDir;
            let infoPlistPath = this.$options.baseConfig || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, this.platformData.configurationFileName);
            this.ensureConfigurationFileInAppResources();
            if (!this.$fs.exists(infoPlistPath)) {
                this.$logger.trace("Info.plist: No app/App_Resources/iOS/Info.plist found, falling back to pre-1.6.0 Info.plist behavior.");
                return;
            }
            let session = new plist_merge_patch_1.PlistSession({ log: (txt) => this.$logger.trace("Info.plist: " + txt) });
            let makePatch = (plistPath) => {
                if (!this.$fs.exists(plistPath)) {
                    this.$logger.trace("No plist found at: " + plistPath);
                    return;
                }
                this.$logger.trace("Schedule merge plist at: " + plistPath);
                session.patch({
                    name: path.relative(projectDir, plistPath),
                    read: () => this.$fs.readText(plistPath)
                });
            };
            let allPlugins = this.getAllInstalledPlugins().wait();
            for (let plugin of allPlugins) {
                let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME), this.platformData.configurationFileName);
                makePatch(pluginInfoPlistPath);
            }
            makePatch(infoPlistPath);
            if (this.$projectData.projectId) {
                session.patch({
                    name: "CFBundleIdentifier from package.json nativescript.id",
                    read: () => `<?xml version="1.0" encoding="UTF-8"?>
						<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
						<plist version="1.0">
						<dict>
							<key>CFBundleIdentifier</key>
							<string>${this.$projectData.projectId}</string>
						</dict>
						</plist>`
                });
            }
            let plistContent = session.build();
            this.$logger.trace("Info.plist: Write to: " + this.platformData.configurationFilePath);
            this.$fs.writeFile(this.platformData.configurationFilePath, plistContent);
        }).future()();
    }
    getAllInstalledPlugins() {
        return this.$injector.resolve("pluginsService").getAllInstalledPlugins();
    }
    get xcodeprojPath() {
        return path.join(this.platformData.projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME);
    }
    get projectPodFilePath() {
        return path.join(this.platformData.projectRoot, "Podfile");
    }
    get pluginsDebugXcconfigFilePath() {
        return path.join(this.platformData.projectRoot, "plugins-debug.xcconfig");
    }
    get pluginsReleaseXcconfigFilePath() {
        return path.join(this.platformData.projectRoot, "plugins-release.xcconfig");
    }
    replace(name) {
        if (_.startsWith(name, '"')) {
            name = name.substr(1, name.length - 2);
        }
        return name.replace(/\\\"/g, "\"");
    }
    getLibSubpathRelativeToProjectPath(targetPath) {
        let frameworkPath = path.relative(this.platformData.projectRoot, targetPath);
        return frameworkPath;
    }
    get pbxProjPath() {
        return path.join(this.xcodeprojPath, "project.pbxproj");
    }
    createPbxProj() {
        let project = new xcode.project(this.pbxProjPath);
        project.parseSync();
        return project;
    }
    savePbxProj(project) {
        return this.$fs.writeFile(this.pbxProjPath, project.writeSync());
    }
    preparePluginNativeCode(pluginData, opts) {
        return (() => {
            let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
            this.prepareFrameworks(pluginPlatformsFolderPath, pluginData).wait();
            this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData).wait();
            this.prepareCocoapods(pluginPlatformsFolderPath);
        }).future()();
    }
    removePluginNativeCode(pluginData) {
        let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
        this.removeFrameworks(pluginPlatformsFolderPath, pluginData);
        this.removeStaticLibs(pluginPlatformsFolderPath, pluginData);
        this.removeCocoapods(pluginPlatformsFolderPath);
    }
    afterPrepareAllPlugins() {
        return (() => {
            if (this.$fs.exists(this.projectPodFilePath)) {
                let projectPodfileContent = this.$fs.readText(this.projectPodFilePath);
                this.$logger.trace("Project Podfile content");
                this.$logger.trace(projectPodfileContent);
                let firstPostInstallIndex = projectPodfileContent.indexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME);
                if (firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) {
                    this.$cocoapodsService.mergePodfileHookContent(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME, this.projectPodFilePath);
                }
                let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata");
                let sharedDataPath = path.join(this.xcodeprojPath, "xcshareddata");
                if (!this.$fs.exists(xcuserDataPath) && !this.$fs.exists(sharedDataPath)) {
                    this.$logger.info("Creating project scheme...");
                    this.checkIfXcodeprojIsRequired().wait();
                    let createSchemeRubyScript = `ruby -e "require 'xcodeproj'; xcproj = Xcodeproj::Project.open('${this.$projectData.projectName}.xcodeproj'); xcproj.recreate_user_schemes; xcproj.save"`;
                    this.$childProcess.exec(createSchemeRubyScript, { cwd: this.platformData.projectRoot }).wait();
                }
                this.executePodInstall().wait();
            }
        }).future()();
    }
    beforePrepareAllPlugins() {
        return Future.fromResult();
    }
    getAllLibsForPluginWithFileExtension(pluginData, fileExtension) {
        let filterCallback = (fileName, pluginPlatformsFolderPath) => path.extname(fileName) === fileExtension;
        return this.getAllNativeLibrariesForPlugin(pluginData, IOSProjectService.IOS_PLATFORM_NAME, filterCallback);
    }
    ;
    buildPathToCurrentXcodeProjectFile() {
        return path.join(this.$projectData.platformsDir, "ios", `${this.$projectData.projectName}.xcodeproj`, "project.pbxproj");
    }
    buildPathToNewXcodeProjectFile(newModulesDir) {
        return path.join(newModulesDir, constants.PROJECT_FRAMEWORK_FOLDER_NAME, `${IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER}.xcodeproj`, "project.pbxproj");
    }
    validateFramework(libraryPath) {
        return (() => {
            let infoPlistPath = path.join(libraryPath, "Info.plist");
            if (!this.$fs.exists(infoPlistPath)) {
                this.$errors.failWithoutHelp("The bundle at %s does not contain an Info.plist file.", libraryPath);
            }
            let packageType = this.$childProcess.spawnFromEvent("/usr/libexec/PlistBuddy", ["-c", "Print :CFBundlePackageType", infoPlistPath], "close").wait().stdout.trim();
            if (packageType !== "FMWK") {
                this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath);
            }
        }).future()();
    }
    validateStaticLibrary(libraryPath) {
        return (() => {
            if (path.extname(libraryPath) !== ".a") {
                this.$errors.failWithoutHelp(`The bundle at ${libraryPath} does not contain a valid static library in the '.a' file format.`);
            }
            let expectedArchs = ["armv7", "arm64", "i386"];
            let archsInTheFatFile = this.$childProcess.exec("lipo -i " + libraryPath).wait();
            expectedArchs.forEach(expectedArch => {
                if (archsInTheFatFile.indexOf(expectedArch) < 0) {
                    this.$errors.failWithoutHelp(`The static library at ${libraryPath} is not built for one or more of the following required architectures: ${expectedArchs.join(", ")}. The static library must be built for all required architectures.`);
                }
            });
        }).future()();
    }
    replaceFileContent(file) {
        let fileContent = this.$fs.readText(file);
        let replacedContent = helpers.stringReplaceAll(fileContent, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, this.$projectData.projectName);
        this.$fs.writeFile(file, replacedContent);
    }
    replaceFileName(fileNamePart, fileRootLocation) {
        let oldFileName = IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + fileNamePart;
        let newFileName = this.$projectData.projectName + fileNamePart;
        this.$fs.rename(path.join(fileRootLocation, oldFileName), path.join(fileRootLocation, newFileName));
    }
    executePodInstall() {
        return (() => {
            try {
                this.$childProcess.exec("gem which cocoapods").wait();
                this.$childProcess.exec("gem which xcodeproj").wait();
            }
            catch (e) {
                this.$errors.failWithoutHelp("CocoaPods or ruby gem 'xcodeproj' is not installed. Run `sudo gem install cocoapods` and try again.");
            }
            this.$xcprojService.verifyXcproj(true).wait();
            this.$logger.info("Installing pods...");
            let podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod";
            let childProcess = this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: this.platformData.projectRoot, stdio: ['pipe', process.stdout, 'pipe'] }).wait();
            if (childProcess.stderr) {
                let warnings = childProcess.stderr.match(/(\u001b\[(?:\d*;){0,5}\d*m[\s\S]+?\u001b\[(?:\d*;){0,5}\d*m)|(\[!\].*?\n)|(.*?warning.*)/gi);
                _.each(warnings, (warning) => {
                    this.$logger.warnWithLabel(warning.replace("\n", ""));
                });
                let errors = childProcess.stderr;
                _.each(warnings, warning => {
                    errors = errors.replace(warning, "");
                });
                if (errors.trim()) {
                    this.$errors.failWithoutHelp(`Pod install command failed. Error output: ${errors}`);
                }
            }
            if (this.$xcprojService.getXcprojInfo().wait().shouldUseXcproj) {
                this.$childProcess.spawnFromEvent("xcproj", ["--project", this.xcodeprojPath, "touch"], "close").wait();
            }
            return childProcess;
        }).future()();
    }
    prepareFrameworks(pluginPlatformsFolderPath, pluginData) {
        return (() => {
            _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => this.addFramework(path.join(pluginPlatformsFolderPath, fileName)).wait());
        }).future()();
    }
    prepareStaticLibs(pluginPlatformsFolderPath, pluginData) {
        return (() => {
            _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".a"), fileName => this.addStaticLibrary(path.join(pluginPlatformsFolderPath, fileName)).wait());
        }).future()();
    }
    prepareCocoapods(pluginPlatformsFolderPath, opts) {
        let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile");
        if (this.$fs.exists(pluginPodFilePath)) {
            let pluginPodFileContent = this.$fs.readText(pluginPodFilePath), pluginPodFilePreparedContent = this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent), projectPodFileContent = this.$fs.exists(this.projectPodFilePath) ? this.$fs.readText(this.projectPodFilePath) : "";
            if (!~projectPodFileContent.indexOf(pluginPodFilePreparedContent)) {
                let podFileHeader = this.$cocoapodsService.getPodfileHeader(this.$projectData.projectName), podFileFooter = this.$cocoapodsService.getPodfileFooter();
                if (_.startsWith(projectPodFileContent, podFileHeader)) {
                    projectPodFileContent = projectPodFileContent.substr(podFileHeader.length);
                }
                if (_.endsWith(projectPodFileContent, podFileFooter)) {
                    projectPodFileContent = projectPodFileContent.substr(0, projectPodFileContent.length - podFileFooter.length);
                }
                let contentToWrite = `${podFileHeader}${projectPodFileContent}${pluginPodFilePreparedContent}${podFileFooter}`;
                this.$fs.writeFile(this.projectPodFilePath, contentToWrite);
                let project = this.createPbxProj();
                this.savePbxProj(project);
            }
        }
        if (opts && opts.executePodInstall && this.$fs.exists(pluginPodFilePath)) {
            this.executePodInstall().wait();
        }
    }
    removeFrameworks(pluginPlatformsFolderPath, pluginData) {
        let project = this.createPbxProj();
        _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => {
            let relativeFrameworkPath = this.getLibSubpathRelativeToProjectPath(fileName);
            project.removeFramework(relativeFrameworkPath, { customFramework: true, embed: true });
        });
        this.savePbxProj(project);
    }
    removeStaticLibs(pluginPlatformsFolderPath, pluginData) {
        let project = this.createPbxProj();
        _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".a"), fileName => {
            let staticLibPath = path.join(pluginPlatformsFolderPath, fileName);
            let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(path.basename(staticLibPath));
            project.removeFramework(relativeStaticLibPath);
            let headersSubpath = path.join("include", path.basename(staticLibPath, ".a"));
            let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath));
            project.removeFromHeaderSearchPaths({ relativePath: relativeHeaderSearchPath });
        });
        this.savePbxProj(project);
    }
    removeCocoapods(pluginPlatformsFolderPath) {
        let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile");
        if (this.$fs.exists(pluginPodFilePath) && this.$fs.exists(this.projectPodFilePath)) {
            let pluginPodFileContent = this.$fs.readText(pluginPodFilePath);
            let projectPodFileContent = this.$fs.readText(this.projectPodFilePath);
            let contentToRemove = this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent);
            projectPodFileContent = helpers.stringReplaceAll(projectPodFileContent, contentToRemove, "");
            if (projectPodFileContent.trim() === `use_frameworks!${os.EOL}${os.EOL}target "${this.$projectData.projectName}" do${os.EOL}${os.EOL}end`) {
                this.$fs.deleteFile(this.projectPodFilePath);
            }
            else {
                this.$fs.writeFile(this.projectPodFilePath, projectPodFileContent);
            }
        }
    }
    buildPodfileContent(pluginPodFilePath, pluginPodFileContent) {
        return `# Begin Podfile - ${pluginPodFilePath} ${os.EOL} ${pluginPodFileContent} ${os.EOL} # End Podfile ${os.EOL}`;
    }
    generateModulemap(headersFolderPath, libraryName) {
        let headersFilter = (fileName, containingFolderPath) => (path.extname(fileName) === ".h" && this.$fs.getFsStats(path.join(containingFolderPath, fileName)).isFile());
        let headersFolderContents = this.$fs.readDirectory(headersFolderPath);
        let headers = _(headersFolderContents).filter(item => headersFilter(item, headersFolderPath)).value();
        if (!headers.length) {
            this.$fs.deleteFile(path.join(headersFolderPath, "module.modulemap"));
            return;
        }
        headers = _.map(headers, value => `header "${value}"`);
        let modulemap = `module ${libraryName} { explicit module ${libraryName} { ${headers.join(" ")} } }`;
        this.$fs.writeFile(path.join(headersFolderPath, "module.modulemap"), modulemap);
    }
    mergeXcconfigFiles(pluginFile, projectFile) {
        return (() => {
            if (!this.$fs.exists(projectFile)) {
                this.$fs.writeFile(projectFile, "");
            }
            this.checkIfXcodeprojIsRequired().wait();
            let escapedProjectFile = projectFile.replace(/'/g, "\\'"), escapedPluginFile = pluginFile.replace(/'/g, "\\'"), mergeScript = `require 'xcodeproj'; Xcodeproj::Config.new('${escapedProjectFile}').merge(Xcodeproj::Config.new('${escapedPluginFile}')).save_as(Pathname.new('${escapedProjectFile}'))`;
            this.$childProcess.exec(`ruby -e "${mergeScript}"`).wait();
        }).future()();
    }
    mergeProjectXcconfigFiles() {
        return (() => {
            this.$fs.deleteFile(this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath);
            let allPlugins = this.$injector.resolve("pluginsService").getAllInstalledPlugins().wait();
            for (let plugin of allPlugins) {
                let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
                let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig");
                if (this.$fs.exists(pluginXcconfigFilePath)) {
                    this.mergeXcconfigFiles(pluginXcconfigFilePath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait();
                }
            }
            let appResourcesXcconfigPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, "build.xcconfig");
            if (this.$fs.exists(appResourcesXcconfigPath)) {
                this.mergeXcconfigFiles(appResourcesXcconfigPath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait();
            }
            let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${this.$projectData.projectName}`);
            let podFolder = path.join(this.platformData.projectRoot, podFilesRootDirName);
            if (this.$fs.exists(podFolder)) {
                if (this.$options.release) {
                    this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.release.xcconfig`), this.pluginsReleaseXcconfigFilePath).wait();
                }
                else {
                    this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.debug.xcconfig`), this.pluginsDebugXcconfigFilePath).wait();
                }
            }
        }).future()();
    }
    checkIfXcodeprojIsRequired() {
        return (() => {
            let xcprojInfo = this.$xcprojService.getXcprojInfo().wait();
            if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) {
                let errorMessage = `You are using CocoaPods version ${xcprojInfo.cocoapodVer} which does not support Xcode ${xcprojInfo.xcodeVersion.major}.${xcprojInfo.xcodeVersion.minor} yet.${os_1.EOL}${os_1.EOL}You can update your cocoapods by running $sudo gem install cocoapods from a terminal.${os_1.EOL}${os_1.EOL}In order for the NativeScript CLI to be able to work correctly with this setup you need to install xcproj command line tool and add it to your PATH. Xcproj can be installed with homebrew by running $ brew install xcproj from the terminal`;
                this.$errors.failWithoutHelp(errorMessage);
                return true;
            }
        }).future()();
    }
    getXcodeVersion() {
        let xcodeBuildVersion = "";
        try {
            xcodeBuildVersion = this.$childProcess.exec("xcodebuild -version | head -n 1 | sed -e 's/Xcode //'").wait();
        }
        catch (error) {
            this.$errors.fail("xcodebuild execution failed. Make sure that you have latest Xcode and tools installed.");
        }
        let splitedXcodeBuildVersion = xcodeBuildVersion.split(".");
        if (splitedXcodeBuildVersion.length === 3) {
            xcodeBuildVersion = `${splitedXcodeBuildVersion[0]}.${splitedXcodeBuildVersion[1]}`;
        }
        return xcodeBuildVersion;
    }
    getBuildConfig(projectRoot, buildConfig) {
        return (() => {
            buildConfig = buildConfig || {};
            let isXCConfigDefined = false;
            if (this.$options.teamId) {
                buildConfig.teamIdentifier = this.$options.teamId;
            }
            else {
                buildConfig = this.readXCConfigSigning();
                isXCConfigDefined = true;
                if (!buildConfig.codeSignIdentity && !buildConfig.mobileProvisionIdentifier && !buildConfig.teamIdentifier) {
                    buildConfig = this.readBuildConfigFromPlatforms();
                }
            }
            let signingStyle;
            if (buildConfig.codeSignIdentity || buildConfig.mobileProvisionIdentifier) {
                signingStyle = "Manual";
            }
            else if (buildConfig.teamIdentifier) {
                signingStyle = "Automatic";
            }
            else if (helpers.isInteractive()) {
                isXCConfigDefined = false;
                let signingStyles = [
                    "Manual - Select existing provisioning profile for use",
                    "Automatic - Select Team ID for signing and let Xcode select managed provisioning profile"
                ];
                let signingStyleIndex = signingStyles.indexOf(this.$prompter.promptForChoice("Select codesiging style", signingStyles).wait());
                signingStyle = new Array("Manual", "Automatic")[signingStyleIndex];
                switch (signingStyle) {
                    case "Manual":
                        let profile = this.getProvisioningProfile().wait();
                        if (!profile) {
                            this.$errors.failWithoutHelp("No matching provisioning profile found.");
                        }
                        this.persistProvisioningProfiles(profile.UUID).wait();
                        this.$logger.info("Apply provisioning profile: " + profile.Name + " (" + profile.TeamName + ") " + profile.Type + " UUID: " + profile.UUID);
                        buildConfig.mobileProvisionIdentifier = profile.UUID;
                        buildConfig.teamIdentifier = profile.TeamIdentifier[0];
                        break;
                    case "Automatic":
                        buildConfig.teamIdentifier = this.getDevelopmentTeam().wait();
                        this.persistDevelopmentTeam(buildConfig.teamIdentifier).wait();
                        break;
                }
            }
            if (signingStyle && !isXCConfigDefined) {
                const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj");
                const xcode = xcode_1.Xcode.open(pbxprojPath);
                switch (signingStyle) {
                    case "Manual":
                        xcode.setManualSigningStyle(this.$projectData.projectName);
                        break;
                    case "Automatic":
                        xcode.setAutomaticSigningStyle(this.$projectData.projectName, buildConfig.teamIdentifier);
                        break;
                }
                xcode.save();
            }
            if (isXCConfigDefined) {
                delete buildConfig.mobileProvisionIdentifier;
                delete buildConfig.teamIdentifier;
            }
            return buildConfig;
        }).future()();
    }
    getDevelopmentTeams() {
        let dir = path.join(process.env.HOME, "Library/MobileDevice/Provisioning Profiles/");
        let files = this.$fs.readDirectory(dir);
        let teamIds = {};
        for (let file of files) {
            let filePath = path.join(dir, file);
            let data = this.$fs.readText(filePath, "utf8");
            let teamId = this.getProvisioningProfileValue("TeamIdentifier", data);
            let teamName = this.getProvisioningProfileValue("TeamName", data);
            if (teamId) {
                teamIds[teamId] = teamName;
            }
        }
        let teamIdsArray = new Array();
        for (let teamId in teamIds) {
            teamIdsArray.push({ id: teamId, name: teamIds[teamId] });
        }
        return teamIdsArray;
    }
    getProvisioningProfileValue(name, text) {
        let findStr = "<key>" + name + "</key>";
        let index = text.indexOf(findStr);
        if (index > 0) {
            index = text.indexOf("<string>", index + findStr.length);
            if (index > 0) {
                index += "<string>".length;
                let endIndex = text.indexOf("</string>", index);
                let result = text.substring(index, endIndex);
                return result;
            }
        }
        return null;
    }
    readXCConfigSigning() {
        const result = {};
        let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig");
        if (this.$fs.exists(xcconfigFile)) {
            let text = this.$fs.readText(xcconfigFile);
            text.split(/\r?\n/).forEach((line) => {
                line = line.replace(/\/(\/)[^\n]*$/, "");
                const read = (name) => {
                    if (line.indexOf(name) >= 0) {
                        let value = line.substr(line.lastIndexOf("=") + 1).trim();
                        if (value.charAt(value.length - 1) === ';') {
                            value = value.substr(0, value.length - 1).trim();
                        }
                        return value;
                    }
                    return undefined;
                };
                result.teamIdentifier = read("DEVELOPMENT_TEAM") || result.teamIdentifier;
                result.codeSignIdentity = read("CODE_SIGN_IDENTITY") || result.codeSignIdentity;
                result.mobileProvisionIdentifier = read("PROVISIONING_PROFILE[sdk=iphoneos*]") || result.mobileProvisionIdentifier;
            });
        }
        return result;
    }
    getProvisioningProfile() {
        return (() => {
            let profile;
            const allCertificates = ios_mobileprovision_finder_1.cert.read();
            const allProfiles = ios_mobileprovision_finder_1.provision.read();
            const query = {
                Certificates: allCertificates.valid,
                AppId: this.$projectData.projectId,
                Type: "Development"
            };
            if (this.$options.device) {
                query.ProvisionedDevices = [this.$options.device];
            }
            else {
                this.$devicesService.initialize().wait();
                let deviceUDIDs = _(this.$devicesService.getDeviceInstances())
                    .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform))
                    .map(d => d.deviceInfo.identifier)
                    .toJSON();
                query.ProvisionedDevices = deviceUDIDs;
            }
            const result = ios_mobileprovision_finder_1.provision.select(allProfiles, query);
            const choiceMap = result.eligable.reduce((acc, p) => {
                acc[`'${p.Name}' (${p.TeamName}) ${p.Type}`] = p;
                return acc;
            }, {});
            const choices = Object.keys(choiceMap);
            if (choices.length > 0) {
                const choice = this.$prompter.promptForChoice(`Select provisioning profiles (found ${result.eligable.length} eligable, and ${result.nonEligable.length} non-eligable)`, choices).wait();
                profile = choiceMap[choice];
            }
            return profile;
        }).future()();
    }
    getDevelopmentTeam() {
        return (() => {
            let teamId;
            let teams = this.getDevelopmentTeams();
            this.$logger.warn("Xcode 8 requires a team id to be specified when building for device.");
            this.$logger.warn("You can specify the team id by setting the DEVELOPMENT_TEAM setting in build.xcconfig file located in App_Resources folder of your app, or by using the --teamId option when calling run, debug or livesync commnads.");
            if (teams.length === 1) {
                teamId = teams[0].id;
                this.$logger.warn("Found and using the following development team installed on your system: " + teams[0].name + " (" + teams[0].id + ")");
            }
            else if (teams.length > 0) {
                let choices = [];
                for (let team of teams) {
                    choices.push(team.name + " (" + team.id + ")");
                }
                let choice = this.$prompter.promptForChoice('Found multiple development teams, select one:', choices).wait();
                teamId = teams[choices.indexOf(choice)].id;
            }
            return teamId;
        }).future()();
    }
    persistProvisioningProfiles(uuid) {
        return (() => {
            let choicesPersist = [
                "Yes, set the PROVISIONING_PROFILE[sdk=iphoneos*] setting in build.xcconfig file.",
                "Yes, persist the mobileprovision uuid in platforms folder.",
                "No, don't persist this setting."
            ];
            let choicePersist = this.$prompter.promptForChoice("Do you want to make mobileprovision: " + uuid + " a persistent choice for your app?", choicesPersist).wait();
            switch (choicesPersist.indexOf(choicePersist)) {
                case 0:
                    let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig");
                    this.$fs.appendFile(xcconfigFile, "\nPROVISIONING_PROFILE[sdk=iphoneos*] = " + uuid + "\n");
                    break;
                case 1:
                    this.$fs.writeFile(path.join(this.platformData.projectRoot, "mobileprovision"), uuid);
                    const teamidPath = path.join(this.platformData.projectRoot, "teamid");
                    if (this.$fs.exists(teamidPath)) {
                        this.$fs.deleteFile(teamidPath);
                    }
                    break;
                default:
                    break;
            }
        }).future()();
    }
    persistDevelopmentTeam(teamId) {
        return (() => {
            let choicesPersist = [
                "Yes, set the DEVELOPMENT_TEAM setting in build.xcconfig file.",
                "Yes, persist the team id in platforms folder.",
                "No, don't persist this setting."
            ];
            let choicePersist = this.$prompter.promptForChoice("Do you want to make teamId: " + teamId + " a persistent choice for your app?", choicesPersist).wait();
            switch (choicesPersist.indexOf(choicePersist)) {
                case 0:
                    let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig");
                    this.$fs.appendFile(xcconfigFile, "\nDEVELOPMENT_TEAM = " + teamId + "\n");
                    break;
                case 1:
                    this.$fs.writeFile(path.join(this.platformData.projectRoot, "teamid"), teamId);
                    const mobileprovisionPath = path.join(this.platformData.projectRoot, "mobileprovision");
                    if (this.$fs.exists(mobileprovisionPath)) {
                        this.$fs.deleteFile(mobileprovisionPath);
                    }
                    break;
                default:
                    break;
            }
        }).future()();
    }
    readBuildConfigFromPlatforms() {
        let mobileprovisionPath = path.join(this.platformData.projectRoot, "mobileprovision");
        if (this.$fs.exists(mobileprovisionPath)) {
            return { mobileProvisionIdentifier: this.$fs.readText(mobileprovisionPath) };
        }
        let teamidPath = path.join(this.platformData.projectRoot, "teamid");
        if (this.$fs.exists(teamidPath)) {
            return { teamIdentifier: this.$fs.readText(teamidPath) };
        }
        return {};
    }
}
IOSProjectService.XCODE_PROJECT_EXT_NAME = ".xcodeproj";
IOSProjectService.XCODE_SCHEME_EXT_NAME = ".xcscheme";
IOSProjectService.XCODEBUILD_MIN_VERSION = "6.0";
IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__";
IOSProjectService.IOS_PLATFORM_NAME = "ios";
IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME = "post_install";
exports.IOSProjectService = IOSProjectService;
$injector.register("iOSProjectService", IOSProjectService);
