"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 child_process_1 = require("child_process");
const path_1 = require("path");
const os_1 = require("os");
const fs_1 = require("fs");
const glob_1 = require("glob");
const utils_1 = require("./utils");
const device_1 = require("./device");
const enums_1 = require("./enums");
const ios_device_lib_1 = require("ios-device-lib");
const device_controller_1 = require("./device-controller");
class IOSController {
    static getDl() {
        if (!IOSController._dl) {
            return new Promise((resolve, reject) => {
                const connectionTimeout = setTimeout(() => {
                    resolve(false);
                }, 30000);
                IOSController._dl = new ios_device_lib_1.IOSDeviceLib(d => {
                    console.log("Device found!", d);
                    clearTimeout(connectionTimeout);
                    resolve(IOSController._dl);
                }, device => {
                    console.log("Device LOST!");
                    resolve(false);
                });
            });
        }
        return Promise.resolve(IOSController._dl);
    }
    static disposeDL() {
        if (IOSController._dl) {
            IOSController._dl.dispose("SIGTERM");
            IOSController._dl = undefined;
        }
    }
    static getAllDevices(verbose = false) {
        if (IOSController.devicesScreenInfo.size === 0) {
            IOSController.loadIOSDevicesScreenInfo();
        }
        const devices = IOSController.parseSimulators();
        const allDevices = IOSController.parseRealDevices(devices);
        if (verbose) {
            console.log("All devices: ", devices);
        }
        return Promise.resolve(allDevices);
    }
    static getSimulatorPidByToken(token) {
        const simulatorPidAsString = utils_1.executeCommand(`ps ax | grep ${token} | grep -v grep`);
        const result = utils_1.getRegexResultsAsArray(/^\d+/gi, simulatorPidAsString);
        let pid = undefined;
        if (result.length > 0) {
            try {
                pid = parseInt(result[0].trim());
            }
            catch (error) {
                console.error("Couldn't parse simulator pid", error);
            }
        }
        return pid;
    }
    static deleteDevice(token) {
        const result = child_process_1.spawnSync(`xcrun simctl delete ${token}`, {
            shell: true
        });
        if (result.status !== 0) {
            utils_1.logError("", result.output.toString());
        }
    }
    static fullResetOfSimulator(simulator) {
        if ((!simulator.name || !simulator.initType) && !simulator.apiLevel) {
            console.log("To recreate simulator it is need to be specified simulator name or type and apiLevel!");
            return simulator;
        }
        simulator.name = simulator.name || simulator.initType;
        const iOSDevicesInfo = child_process_1.spawnSync(IOSController.SIMCTL, ["list", "-j"], {
            shell: true,
            encoding: "UTF8"
        });
        const iOSDevicesInfoAsObj = JSON.parse(iOSDevicesInfo.stdout.toString());
        let desiredType = simulator.initType || simulator.name.split(" ").slice(0, 2).join("-");
        const type = iOSDevicesInfoAsObj.devicetypes.filter(dt => dt.identifier.toLowerCase().includes(desiredType.toLowerCase())
            || dt.name.toLowerCase().includes(desiredType.toLowerCase()))[0];
        if (!type) {
            utils_1.logError("Please provide correct simulator type!");
        }
        const runTime = iOSDevicesInfoAsObj.runtimes.filter(drt => new RegExp(drt.version).test(simulator.apiLevel))[0];
        if (!runTime) {
            utils_1.logError("No such runtime available!");
        }
        const oldToken = simulator.token;
        const command = `xcrun simctl create "${simulator.name}" "${type.name}" "${runTime.identifier}"`;
        console.log(command);
        const result = utils_1.executeCommand(command);
        if (result && result.trim()) {
            simulator.token = result.trim();
            if (oldToken) {
                IOSController.deleteDevice(oldToken);
                console.log(`Remove: `, oldToken);
            }
        }
        else {
            utils_1.logError("Failed to create simulator!", result);
        }
        return simulator;
    }
    static startSimulator(simulator, directory = os_1.tmpdir(), shouldFullResetSimulator = true) {
        return __awaiter(this, void 0, void 0, function* () {
            simulator.type = enums_1.DeviceType.SIMULATOR;
            simulator.platform = enums_1.Platform.IOS;
            if (!simulator.token) {
                simulator = (yield device_controller_1.DeviceController.getDevices(simulator))[0];
            }
            let udid = simulator.token;
            // && simulator.name
            // && !simulator.name.toLowerCase().includes("iphone 7")
            // && !simulator.name.toLowerCase().includes("iphone 8")
            if (utils_1.isProcessAlive("Simulator.app") && shouldFullResetSimulator) {
                try {
                    const newSim = IOSController.fullResetOfSimulator(simulator);
                    if (newSim.token) {
                        simulator = newSim;
                        udid = simulator.token;
                    }
                }
                catch (error) { }
            }
            else if (shouldFullResetSimulator) {
                const eraseSimResult = utils_1.executeCommand(`${IOSController.SIMCTL} erase ${udid}`);
            }
            let startedProcess = IOSController.startSimulatorProcess(udid, directory);
            if (startedProcess.stderr.toString().toLowerCase().includes("unable to boot deleted device")
                || startedProcess.stderr.toString().toLowerCase().includes("Failed to load")) {
                simulator = IOSController.fullResetOfSimulator(simulator);
                udid = simulator.token;
                startedProcess = IOSController.startSimulatorProcess(udid, directory);
            }
            // let response: boolean = await waitForOutput(process, /Instruments Trace Complete:/ig, /Failed to load/ig, IOSController.DEVICE_BOOT_TIME);
            if (startedProcess.stderr
                && startedProcess.stderr.toString().trim() !== ""
                || startedProcess.status !== 0) {
                utils_1.logError(`Probably the simulator ${simulator.name}\ ${simulator.token} failed to start!`);
                utils_1.logError(startedProcess.stderr.toString());
            }
            if (startedProcess.stdout.toString().includes("Instruments Trace Complete")) {
                const response = IOSController.checkIfSimulatorIsBooted(udid, IOSController.WAIT_DEVICE_TO_RESPONSE);
                if (response) {
                    simulator.type = enums_1.DeviceType.SIMULATOR;
                    simulator.status = enums_1.Status.BOOTED;
                    simulator.pid = startedProcess.pid;
                    simulator.startedAt = Date.now();
                    console.log(`Launched simulator with name: ${simulator.name}; udid: ${simulator.token}; status: ${simulator.status}`);
                }
            }
            else {
                console.log("Simulator is probably already started!");
            }
            return simulator;
        });
    }
    static restartDevice(device) {
        return __awaiter(this, void 0, void 0, function* () {
            if (device.type === enums_1.DeviceType.SIMULATOR) {
                IOSController.kill(device.token);
                device.status = enums_1.Status.SHUTDOWN;
                device.pid = undefined;
                device.startedAt = -1;
                device.busySince = -1;
                yield IOSController.startSimulator(device);
            }
        });
    }
    static killAll() {
        const log = utils_1.executeCommand("killall Simulator ");
        utils_1.executeCommand(IOSController.OSASCRIPT_QUIT_SIMULATOR_COMMAND);
    }
    static kill(udid) {
        if (!udid) {
            utils_1.logError("Please provide device token!");
        }
        console.log(`Killing simulator with udid ${udid}`);
        utils_1.executeCommand(`${IOSController.SIMCTL} shutdown ${udid}`);
        // Kill all the processes related with sim.id (for example WDA agents).
        utils_1.killAllProcessAndRelatedCommand(udid);
    }
    static getInstalledApps(device) {
        const apps = new Array();
        if (device.type === enums_1.DeviceType.DEVICE) {
            const rowData = utils_1.executeCommand(`ideviceinstaller -u ${device.token} -l`).replace("package:", "").split("\n");
            rowData.forEach(data => {
                const appBundle = /(\w+\.)+\w+/ig.exec(data);
                if (appBundle && appBundle.length > 0 && appBundle[0].includes(".")) {
                    apps.push(appBundle[0]);
                }
            });
        }
        else {
            const simLocation = `${IOSController.getSimLocation(device.token)}`;
            const installedApps = glob_1.glob.sync(`${simLocation}/**/*.app`);
            const rowData = installedApps
                .filter(f => {
                return f.endsWith(".app") && fs_1.statSync(f).isDirectory();
            });
            rowData.forEach(data => {
                const rowBundle = utils_1.executeCommand(`defaults read ${data}/Info.plist | grep CFBundleIdentifier`);
                const appId = rowBundle.split("\"")[1];
                apps.push(appId);
            });
        }
        return apps;
    }
    static installApp(device, fullAppName) {
        return __awaiter(this, void 0, void 0, function* () {
            if (device.type === enums_1.DeviceType.DEVICE) {
                const installProcess = yield (yield IOSController.getDl()).install(fullAppName, [device.token])[0];
                yield IOSController.disposeDL();
                if (!installProcess.response.includes("Successfully installed application")) {
                    console.error(installProcess.response);
                }
            }
            else {
                const result = utils_1.executeCommand(`${IOSController.SIMCTL} install ${device.token} ${fullAppName}`);
            }
        });
    }
    /**
    * @param device - of type {token: string, type: DeviceType}
    * @param bundleId - should be provided when DeviceType.DEVICE else undefined
    * @param appName - should be provided when DeviceType.SIMULATOR else undefined
    **/
    static stopApplication(device, bundleId, appName) {
        return __awaiter(this, void 0, void 0, function* () {
            const apps = IOSController.getInstalledApps(device);
            if (apps.some(app => app.includes(bundleId))) {
                if (!device.type) {
                    device.platform = enums_1.Platform.IOS;
                    const d = (yield device_controller_1.DeviceController.getDevices(device))[0];
                    device.type = d && d.type;
                }
                if (device.type && device.type === enums_1.DeviceType.SIMULATOR) {
                    utils_1.executeCommand(`${IOSController.SIMCTL} ${device.token} terminate ${bundleId}`);
                    utils_1.killAllProcessAndRelatedCommand([device.token, appName]);
                }
                else {
                    const appInfo = { ddi: undefined, appId: bundleId, deviceId: device.token };
                    const dl = yield IOSController.getDl();
                    if (dl) {
                        return new Promise((res, reject) => {
                            Promise.all(dl.stop([appInfo]))
                                .then((response) => __awaiter(this, void 0, void 0, function* () {
                                console.log("App " + bundleId + " stopped !", response);
                                yield IOSController.disposeDL();
                                res(true);
                            })).catch((err) => __awaiter(this, void 0, void 0, function* () {
                                console.log("An error occurred! Probably app is still running!", err);
                                yield IOSController.disposeDL();
                                res(false);
                            }));
                        });
                    }
                }
            }
        });
    }
    static uninstallApp(device, fullAppName, bundleId = undefined) {
        return __awaiter(this, void 0, void 0, function* () {
            bundleId = bundleId || IOSController.getIOSPackageId(device.type, fullAppName);
            let result = "";
            try {
                yield IOSController.stopApplication(device, bundleId, path_1.basename(fullAppName));
                utils_1.wait(500);
            }
            catch (error) {
                console.dir(error);
            }
            if (device.type === enums_1.DeviceType.DEVICE) {
                result = utils_1.executeCommand(`ideviceinstaller -u ${device.token} -U ${bundleId}`);
            }
            else {
                result = utils_1.executeCommand(`${IOSController.SIMCTL} uninstall ${device.token} ${bundleId}`);
            }
        });
    }
    static reinstallApplication(device, fullAppName, bundleId = undefined) {
        return __awaiter(this, void 0, void 0, function* () {
            bundleId = bundleId || IOSController.getIOSPackageId(device.type, fullAppName);
            yield IOSController.uninstallApp(device, fullAppName, bundleId);
            yield IOSController.installApp(device, fullAppName);
        });
    }
    static refreshApplication(device, fullAppName, bundleId = undefined) {
        return __awaiter(this, void 0, void 0, function* () {
            bundleId = bundleId || IOSController.getIOSPackageId(device.type, fullAppName);
            yield IOSController.reinstallApplication(device, fullAppName, bundleId);
            yield IOSController.startApplication(device, fullAppName, bundleId);
        });
    }
    static startApplication(device, fullAppName, bundleId = undefined) {
        return __awaiter(this, void 0, void 0, function* () {
            bundleId = bundleId || IOSController.getIOSPackageId(device.type, fullAppName);
            let output = "";
            let result = false;
            if (device.type === enums_1.DeviceType.DEVICE) {
                let startProcess;
                try {
                    output = yield (yield IOSController.getDl()).start([{ "ddi": undefined, "appId": bundleId, "deviceId": device.token }])[0];
                    if (!startProcess.response.includes("Successfully started application")) {
                        throw new Error(`Failed to start application ${bundleId}`);
                    }
                    else {
                        result = true;
                    }
                }
                catch (error) {
                    yield IOSController.disposeDL();
                }
                yield IOSController.disposeDL();
            }
            else {
                output = utils_1.executeCommand(`${IOSController.SIMCTL} launch ${device.token} ${bundleId}`, process.cwd(), 60000);
                result = output.includes(bundleId);
            }
            return Promise.resolve({ output: output, result: result });
        });
    }
    static startSimulatorProcess(udid, cwd = os_1.tmpdir(), timeout = IOSController.DEVICE_BOOT_TIME) {
        // xcrun instruments -v -t 'Blank' -l 100 -w
        // xcrun instruments -w ${udid} -t Blank `;
        const simProcess = child_process_1.spawnSync(IOSController.XCRUN, ['instruments', '-w', udid, '-t', 'Blank'], {
            shell: true,
            cwd: cwd,
            timeout: timeout
        });
        return simProcess;
    }
    static isRunning(token) {
        const out = utils_1.executeCommand(`${IOSController.SIMCTL} spawn ${token} launchctl print system | grep com.apple.springboard.services `);
        return out.includes("M   A   com.apple.springboard.services");
    }
    static parseSimulators(stdout = undefined) {
        if (!stdout) {
            stdout = utils_1.executeCommand(IOSController.XCRUNLISTDEVICES_COMMAND);
        }
        let deviceSectionRe = /-- iOS (.+) --(\n\s{4}.+)*/mg;
        let matches = [];
        let match = deviceSectionRe.exec(stdout);
        while (match !== null) {
            matches.push(match);
            match = deviceSectionRe.exec(stdout);
        }
        if (matches.length < 1) {
            throw new Error("No matching devices!!!");
        }
        const devices = new Map();
        for (match of matches) {
            let apiLevel = match[1];
            // split the full match into lines and remove the first
            for (let line of match[0].split('\n').slice(1)) {
                let lineRe = /([^\s].+) \((\w+-.+\w+)\) \((\w+\s?\w+)\)/; // https://regex101.com/r/lG7mK6/3
                let lineMatch = lineRe.exec(line);
                if (lineMatch === null) {
                    throw new Error(`Could not match line: ${line}`);
                }
                const status = lineMatch[3].toLowerCase();
                const device = new IOSDevice(lineMatch[2], lineMatch[1], status, enums_1.DeviceType.SIMULATOR, apiLevel);
                IOSController.devicesScreenInfo.forEach((v, k, m) => {
                    if (device.name.includes(k)) {
                        device.config = {
                            density: v.density,
                            offsetPixels: v.actionBarHeight
                        };
                    }
                });
                if (!devices.has(device.name)) {
                    devices.set(device.name, new Array());
                    devices.get(device.name).push(device);
                }
                else {
                    devices.get(device.name).push(device);
                }
            }
        }
        return devices;
    }
    static parseRealDevices(devices = new Map()) {
        const devicesUDID = utils_1.executeCommand("idevice_id  --list").split('\n');
        devicesUDID.forEach(udid => {
            if (udid && udid !== "") {
                const deviceInfo = utils_1.executeCommand(`ideviceinfo -s -u ${udid}`).split('\n');
                const device = new device_1.Device(undefined, undefined, enums_1.DeviceType.DEVICE, enums_1.Platform.IOS, udid, enums_1.Status.BOOTED);
                deviceInfo.forEach(info => {
                    if (info && info.trim() !== "") {
                        if (info.toLowerCase().includes('devicename')) {
                            device.name = info.split(": ")[1].trim();
                        }
                        if (info.toLowerCase().includes('productversion')) {
                            device.apiLevel = info.split(": ")[1].trim();
                        }
                    }
                });
                if (device.name) {
                    if (devices.has(device.name)) {
                        devices.get(device.name).push(device);
                    }
                    else {
                        devices.set(device.name, [device]);
                    }
                }
            }
        });
        return devices;
    }
    static getSimLocation(token) {
        const simRootHome = path_1.resolve(process.env["HOME"], "Library/Developer/CoreSimulator/Devices/", token, "data/Containers/Bundle/Application");
        const simRoot = path_1.resolve("/Library/Developer/CoreSimulator/Devices/", token, "data/Containers/Bundle/Application");
        return fs_1.existsSync(simRootHome) ? simRootHome : simRoot;
    }
    static filterDeviceBy(...args) {
        const mappedDevices = IOSController.parseSimulators();
        const result = new Array();
        mappedDevices.forEach(devices => {
            devices.forEach(device => {
                let shouldAdd = true;
                const deviceToString = device.toString().toLocaleLowerCase();
                args.forEach(arg => {
                    if (deviceToString.includes(arg.toLocaleLowerCase())) {
                        shouldAdd = shouldAdd && true;
                    }
                    else {
                        shouldAdd = false;
                    }
                });
                if (shouldAdd) {
                    result.push(device);
                }
            });
        });
        return result;
    }
    static getScreenshot(device, dir, fileName) {
        return __awaiter(this, void 0, void 0, function* () {
            const pathToScreenshotPng = path_1.resolve(dir, `${fileName}.png`);
            if (device.type === enums_1.DeviceType.DEVICE) {
                utils_1.executeCommand(`idevicescreenshot -u ${device.token} ${pathToScreenshotPng}`);
            }
            else {
                utils_1.executeCommand(`${IOSController.SIMCTL} io ${device.token} screenshot ${pathToScreenshotPng}`);
            }
            return pathToScreenshotPng;
        });
    }
    static recordVideo(device, dir, fileName, callback) {
        return __awaiter(this, void 0, void 0, function* () {
            const { pathToVideo, videoRecoringProcess } = IOSController.startRecordingVideo(device, dir, fileName);
            return new Promise((res, reject) => __awaiter(this, void 0, void 0, function* () {
                callback().then((result) => {
                    videoRecoringProcess.kill("SIGINT");
                    console.log(result);
                    res(pathToVideo);
                }).catch((error) => {
                    if (videoRecoringProcess) {
                        videoRecoringProcess.kill("SIGINT");
                    }
                    console.log('', error);
                    reject(error);
                });
            }));
        });
    }
    static startRecordingVideo(device, dir, fileName) {
        let pathToVideo = path_1.resolve(dir, `${fileName}.mp4`).replace(" ", "\ ");
        if (fs_1.existsSync(pathToVideo)) {
            fs_1.unlinkSync(pathToVideo);
        }
        let videoRecoringProcess;
        if (device.type === enums_1.DeviceType.DEVICE) {
            const p = path_1.resolve(__dirname, "../", "bin", "xrecord");
            console.log(`${p} --quicktime --id=${device.token} --out=${pathToVideo} --force`);
            const startRecording = () => {
                return child_process_1.spawn(`${p}`, [`--quicktime`, `--id=\"${device.token}\"`, `--out=\"${pathToVideo}\"`, `--force`], {
                    stdio: 'inherit',
                    shell: true
                });
            };
            videoRecoringProcess = startRecording();
            utils_1.wait(3000);
            const checkHasStartedRecording = (timeout, pathToVideo) => {
                const startTime = Date.now();
                while (Date.now() - startTime <= timeout && !fs_1.existsSync(pathToVideo)) {
                }
                return fs_1.existsSync(pathToVideo);
            };
            let retryCount = 10;
            let awaitOnRecordingStart = false;
            while (!checkHasStartedRecording(5000, pathToVideo) && retryCount >= 0) {
                try {
                    child_process_1.execSync("killall 'QuickTime Player'");
                }
                catch (error) { }
                try {
                    videoRecoringProcess.kill("SIGTERM");
                }
                catch (error) { }
                retryCount--;
                utils_1.wait(2000);
                const quicktimeAppleScriptPath = path_1.resolve(__dirname, "../bin/startQuickTimePlayer.scpt");
                child_process_1.spawnSync('osascript', [quicktimeAppleScriptPath, '10'], {
                    shell: true,
                    stdio: 'inherit'
                });
                videoRecoringProcess = startRecording();
                awaitOnRecordingStart = true;
            }
            if (awaitOnRecordingStart)
                utils_1.wait(3000);
            if (!fs_1.existsSync(pathToVideo)) {
                console.error(`Couldn't start recording process!`);
                console.error(`Recording couldn't be started! Check device connection and quick time player`);
                videoRecoringProcess = null;
                pathToVideo = null;
            }
        }
        else {
            console.log(`${IOSController.XCRUN} simctl io ${device.token} recordVideo ${pathToVideo}`);
            videoRecoringProcess = child_process_1.spawn(`xcrun`, ['simctl ', 'io', device.token, 'recordVideo', `'${pathToVideo}'`], {
                cwd: process.cwd(),
                shell: true,
                stdio: 'inherit'
            });
        }
        if (videoRecoringProcess) {
            IOSController.runningProcesses.push(videoRecoringProcess.pid);
        }
        return { pathToVideo: pathToVideo, videoRecoringProcess: videoRecoringProcess };
    }
    // Should find a better way
    static checkIfSimulatorIsBooted(udid, timeout) {
        const startTime = new Date().getTime();
        let currentTime = new Date().getTime();
        console.log("Check if simulator is booted!");
        let booted = false;
        while ((currentTime - startTime) < timeout && !booted) {
            currentTime = new Date().getTime();
            const devices = IOSController.filterDeviceBy(udid, enums_1.Status.BOOTED);
            booted = devices.length > 0 && IOSController.isRunning(udid);
        }
        if (!booted) {
            let error = `Simulator with " ${udid} failed to boot`;
            console.log(error, true);
        }
        else {
            console.log("Simulator is booted!");
        }
        return booted;
    }
    static getIOSPackageId(deviceType, fullAppName) {
        let result = "";
        const plistPath = IOSController.getPlistPath(fullAppName);
        if (fs_1.existsSync(plistPath)) {
            const command = "/usr/libexec/PlistBuddy -c 'Print CFBundleIdentifier' " + plistPath;
            result = utils_1.executeCommand(command);
        }
        else {
            console.error("File " + plistPath + " does not exist.");
        }
        return result.trim();
    }
    static getDevicesScreenInfo() {
        if (IOSController.devicesScreenInfo.size == 0) {
            IOSController.loadIOSDevicesScreenInfo();
        }
        return IOSController.devicesScreenInfo;
    }
    /**
     * Get path of Info.plist of iOS app under test.
     * Info.plist holds information for app under test.
     *
     * @return path to Info.plist
     */
    static getPlistPath(fullAppName) {
        let plistPath = null;
        const ext = path_1.extname(fullAppName);
        if (ext.includes('ipa')) {
            const appFullName = path_1.dirname(fullAppName) + path_1.sep + path_1.basename(fullAppName).replace(".ipa", "");
            const command = `unzip -o ${fullAppName} -d ${appFullName}`;
            console.log(command);
            utils_1.executeCommand(command);
            const appName = utils_1.executeCommand(`ls ${path_1.resolve(appFullName, "Payload")}`).split('\n').filter(f => f.includes(".app"))[0];
            plistPath = path_1.resolve(appFullName, "Payload", appName, "Info.plist");
        }
        else {
            plistPath = path_1.resolve(fullAppName, "Info.plist");
        }
        return plistPath;
    }
    // Not testes to the end
    static waitForBootInSystemLog(simulator, bootedIndicator, startupTimeout) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield IOSController.tailLogsUntil(simulator.token, bootedIndicator, startupTimeout);
        });
    }
    static tailLogsUntil(token, bootedIndicator, timeoutMs) {
        return __awaiter(this, void 0, void 0, function* () {
            let simLog = path_1.resolve(IOSController.getLogDir(token), 'system.log');
            // we need to make sure log file exists before we can tail it
            let exists = fs_1.existsSync(simLog);
            while (!exists) {
                exists = fs_1.existsSync(simLog);
            }
            console.info(`Simulator log at '${simLog}'`);
            console.info(`Tailing simulator logs until we encounter the string "${bootedIndicator}"`);
            console.info(`We will time out after ${timeoutMs}ms`);
            let isBooted = false;
            try {
                let result = utils_1.tailFileUntil(simLog, "com.apple.intents.intents-image-service", 0);
                while (!result.result) {
                    result = utils_1.tailFileUntil(simLog, bootedIndicator, result.index);
                }
                isBooted = result.result;
            }
            catch (err) {
                console.debug('Simulator startup timed out. Continuing anyway.');
            }
            return isBooted;
        });
    }
    static getLogDir(token) {
        let home = process.env.HOME;
        return path_1.resolve(home, 'Library', 'Logs', 'CoreSimulator', token);
    }
    static loadIOSDevicesScreenInfo() {
        const devices = new Map();
        // IOSController.devicesScreenInfo.set("iPhone 5", new IOSDeviceScreenInfo("iPhone 5", 640, 1336, 326, 30));
        // IOSController.devicesScreenInfo.set("iPhone 5C", new IOSDeviceScreenInfo("iPhone 5C", 640, 1336, 326, 30));
        // IOSController.devicesScreenInfo.set("iPhone 5S", new IOSDeviceScreenInfo("iPhone 5S", 640, 1336, 326, 30));
        IOSController.devicesScreenInfo.set("iPhone 6", new IOSDeviceScreenInfo("iPhone 6", 750, 1334, 2, 33));
        IOSController.devicesScreenInfo.set("iPhone 6s", new IOSDeviceScreenInfo("iPhone 6s", 750, 1334, 2, 33));
        IOSController.devicesScreenInfo.set("iPhone 7", new IOSDeviceScreenInfo("iPhone 7", 750, 1334, 2, 33));
        IOSController.devicesScreenInfo.set("iPhone 8", new IOSDeviceScreenInfo("iPhone 8", 750, 1334, 2, 33));
        IOSController.devicesScreenInfo.set("6 Plus", new IOSDeviceScreenInfo("iPhone 6 Plus", 1242, 2208, 3, 50));
        IOSController.devicesScreenInfo.set("6s Plus", new IOSDeviceScreenInfo("iPhone 6 Plus", 1242, 2208, 3, 50));
        IOSController.devicesScreenInfo.set("7 Plus", new IOSDeviceScreenInfo("iPhone 7 Plus", 1242, 2208, 3, 50));
        IOSController.devicesScreenInfo.set("8 Plus", new IOSDeviceScreenInfo("iPhone 8 Plus", 1242, 2208, 3, 50));
        IOSController.devicesScreenInfo.set("X", new IOSDeviceScreenInfo("iPhone X", 11242, 2208, 3, 87));
        // IOSController.devicesScreenInfo("Mini 2", new IOSDeviceScreenInfo("Mini 2", 11242, 2208, 401));
        // IOSController.devicesScreenInfo("Mini 3", new IOSDeviceScreenInfo("Mini 3", 11242, 2208, 401));
        // IOSController.devicesScreenInfo("Mini 4", new IOSDeviceScreenInfo("Mini 4", 11242, 2208, 401));
        // IOSController.devicesScreenInfo("iPad 3", new IOSDeviceScreenInfo("iPad 3", 1536, 2048, 264));
        // IOSController.devicesScreenInfo("iPad 4", new IOSDeviceScreenInfo("iPad 4", 1536, 2048, 264));
        // IOSController.devicesScreenInfo("iPad Air", new IOSDeviceScreenInfo("iPad Air", 1536, 2048, 264));
        // IOSController.devicesScreenInfo.set("iPad Air 2", new IOSDeviceScreenInfo("iPad Air 2", 1536, 2048, 264, 32));
        // IOSController.devicesScreenInfo("9.7-inch Pro", new IOSDeviceScreenInfo("iPad Pro", 1536, 2048, 264));
        // IOSController.devicesScreenInfo("12.9-inch iPad Pro", new IOSDeviceScreenInfo("12.9-inch iPad Pro", 1536, 2048, 264));
    }
}
IOSController.XCRUN = "xcrun ";
IOSController.SIMCTL = `${IOSController.XCRUN} simctl`;
IOSController.XCRUNLISTDEVICES_COMMAND = `${IOSController.SIMCTL} list devices `;
IOSController.GET_BOOTED_DEVICES_COMMAND = `${IOSController.SIMCTL} list devices `;
IOSController.OSASCRIPT_QUIT_SIMULATOR_COMMAND = "osascript -e 'tell application \"Simulator\" to quit'";
IOSController.IOS_DEVICE = "ios-device";
IOSController.devicesScreenInfo = new Map();
IOSController.DEVICE_BOOT_TIME = 180000;
IOSController.WAIT_DEVICE_TO_RESPONSE = 180000;
IOSController.runningProcesses = new Array();
exports.IOSController = IOSController;
class IOSDevice extends device_1.Device {
    constructor(token, name, status, type, apiLevel, pid) {
        super(name, apiLevel, type, enums_1.Platform.IOS, token, status, pid);
    }
}
exports.IOSDevice = IOSDevice;
class IOSDeviceScreenInfo {
    // In the context of iOS, the proper term for `density` is `screen scale`. Adhere to `density` for unified API.
    constructor(deviceType, width, height, density, actionBarHeight) {
        this.deviceType = deviceType;
        this.width = width;
        this.height = height;
        this.density = density;
        this.actionBarHeight = actionBarHeight;
    }
}
exports.IOSDeviceScreenInfo = IOSDeviceScreenInfo;
//# sourceMappingURL=ios-controller.js.map