"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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());
    });
};
const path = require("path");
const ref = require("ref");
const ffi = require("ffi");
const struct = require("ref-struct");
const bufferpack = require("bufferpack");
const plistlib = require("plistlib");
const plist = require("plist");
const helpers = require("../../../helpers");
const net = require("net");
const util = require("util");
const bplistParser = require("bplist-parser");
const string_decoder = require("string_decoder");
const stream = require("stream");
const assert = require("assert");
const os_1 = require("os");
const decorators_1 = require("../../../decorators");
class CoreTypes {
}
CoreTypes.pointerSize = ref.types.size_t.size;
CoreTypes.voidPtr = ref.refType(ref.types.void);
CoreTypes.intPtr = ref.refType(ref.types.int);
CoreTypes.uintPtr = ref.refType(ref.types.uint);
CoreTypes.charPtr = ref.refType(ref.types.char);
CoreTypes.ptrToVoidPtr = ref.refType(ref.refType(ref.types.void));
CoreTypes.uintType = ref.types.uint;
CoreTypes.uint32Type = ref.types.uint32;
CoreTypes.intType = ref.types.int;
CoreTypes.longType = ref.types.long;
CoreTypes.boolType = ref.types.bool;
CoreTypes.doubleType = ref.types.double;
CoreTypes.am_device_p = CoreTypes.voidPtr;
CoreTypes.cfDictionaryRef = CoreTypes.voidPtr;
CoreTypes.cfDataRef = CoreTypes.voidPtr;
CoreTypes.cfStringRef = CoreTypes.voidPtr;
CoreTypes.afcConnectionRef = CoreTypes.voidPtr;
CoreTypes.afcFileRef = ref.types.uint64;
CoreTypes.afcDirectoryRef = CoreTypes.voidPtr;
CoreTypes.afcError = ref.types.uint32;
CoreTypes.amDeviceRef = CoreTypes.voidPtr;
CoreTypes.amDeviceNotificationRef = CoreTypes.voidPtr;
CoreTypes.cfTimeInterval = ref.types.double;
CoreTypes.kCFPropertyListXMLFormat_v1_0 = 100;
CoreTypes.kCFPropertyListBinaryFormat_v1_0 = 200;
CoreTypes.kCFPropertyListImmutable = 0;
CoreTypes.am_device_notification = struct({
    unknown0: ref.types.uint32,
    unknown1: ref.types.uint32,
    unknown2: ref.types.uint32,
    callback: CoreTypes.voidPtr,
    cookie: ref.types.uint32
});
CoreTypes.am_device_notification_callback_info = struct({
    dev: CoreTypes.am_device_p,
    msg: ref.types.uint,
    subscription: ref.refType(CoreTypes.am_device_notification)
});
CoreTypes.am_device_notification_callback = ffi.Function("void", [ref.refType(CoreTypes.am_device_notification_callback_info), CoreTypes.voidPtr]);
CoreTypes.am_device_install_application_callback = ffi.Function("void", [CoreTypes.cfDictionaryRef, CoreTypes.voidPtr]);
CoreTypes.am_device_mount_image_callback = ffi.Function("void", [CoreTypes.voidPtr, CoreTypes.intType]);
CoreTypes.cf_run_loop_timer_callback = ffi.Function("void", [CoreTypes.voidPtr, CoreTypes.voidPtr]);
exports.CoreTypes = CoreTypes;
class IOSCore {
    constructor($hostInfo) {
        this.$hostInfo = $hostInfo;
        this.cfDictionaryKeyCallBacks = struct({
            version: CoreTypes.uintType,
            retain: CoreTypes.voidPtr,
            release: CoreTypes.voidPtr,
            copyDescription: CoreTypes.voidPtr,
            equal: CoreTypes.voidPtr,
            hash: CoreTypes.voidPtr
        });
        this.cfDictionaryValueCallBacks = struct({
            version: CoreTypes.uintType,
            retain: CoreTypes.voidPtr,
            release: CoreTypes.voidPtr,
            copyDescription: CoreTypes.voidPtr,
            equal: CoreTypes.voidPtr
        });
        this.adjustDllSearchPath();
    }
    get CoreFoundationDir() {
        if (this.$hostInfo.isWindows) {
            return path.join(this.CommonProgramFilesPath, "Apple", "Apple Application Support");
        }
        else if (this.$hostInfo.isDarwin) {
            return "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
        }
        return null;
    }
    get MobileDeviceDir() {
        if (this.$hostInfo.isWindows) {
            return path.join(this.CommonProgramFilesPath, "Apple", "Mobile Device Support");
        }
        else if (this.$hostInfo.isDarwin) {
            return "/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice";
        }
        return null;
    }
    get CommonProgramFilesPath() {
        return process.env.CommonProgramFiles;
    }
    getForeignPointer(lib, name, type) {
        let pointer = lib.get(name);
        pointer.type = ref.refType(type);
        return pointer;
    }
    adjustDllSearchPath() {
        if (this.$hostInfo.isWindows) {
            process.env.PATH = this.CoreFoundationDir + ";" + process.env.PATH;
            process.env.PATH += ";" + this.MobileDeviceDir;
        }
    }
    getCoreFoundationLibrary() {
        let coreFoundationDll = this.$hostInfo.isWindows ? path.join(this.CoreFoundationDir, "CoreFoundation.dll") : this.CoreFoundationDir;
        let lib = ffi.DynamicLibrary(coreFoundationDll);
        return {
            "CFRunLoopRun": ffi.ForeignFunction(lib.get("CFRunLoopRun"), "void", []),
            "CFRunLoopStop": ffi.ForeignFunction(lib.get("CFRunLoopStop"), "void", [CoreTypes.voidPtr]),
            "CFRunLoopGetCurrent": ffi.ForeignFunction(lib.get("CFRunLoopGetCurrent"), CoreTypes.voidPtr, []),
            "CFStringCreateWithCString": ffi.ForeignFunction(lib.get("CFStringCreateWithCString"), CoreTypes.cfStringRef, [CoreTypes.voidPtr, "string", "uint"]),
            "CFDictionaryGetValue": ffi.ForeignFunction(lib.get("CFDictionaryGetValue"), CoreTypes.voidPtr, [CoreTypes.cfDictionaryRef, CoreTypes.cfStringRef]),
            "CFNumberGetValue": ffi.ForeignFunction(lib.get("CFNumberGetValue"), CoreTypes.boolType, [CoreTypes.voidPtr, "uint", CoreTypes.voidPtr]),
            "CFStringGetCStringPtr": ffi.ForeignFunction(lib.get("CFStringGetCStringPtr"), CoreTypes.charPtr, [CoreTypes.cfStringRef, "uint"]),
            "CFStringGetCString": ffi.ForeignFunction(lib.get("CFStringGetCString"), CoreTypes.boolType, [CoreTypes.cfStringRef, CoreTypes.charPtr, "uint", "uint"]),
            "CFStringGetLength": ffi.ForeignFunction(lib.get("CFStringGetLength"), "ulong", [CoreTypes.cfStringRef]),
            "CFDictionaryGetCount": ffi.ForeignFunction(lib.get("CFDictionaryGetCount"), CoreTypes.intType, [CoreTypes.cfDictionaryRef]),
            "CFDictionaryGetKeysAndValues": ffi.ForeignFunction(lib.get("CFDictionaryGetKeysAndValues"), "void", [CoreTypes.cfDictionaryRef, CoreTypes.ptrToVoidPtr, CoreTypes.ptrToVoidPtr]),
            "CFDictionaryCreate": ffi.ForeignFunction(lib.get("CFDictionaryCreate"), CoreTypes.cfDictionaryRef, [CoreTypes.voidPtr, CoreTypes.ptrToVoidPtr, CoreTypes.ptrToVoidPtr, "int", ref.refType(this.cfDictionaryKeyCallBacks), ref.refType(this.cfDictionaryValueCallBacks)]),
            "kCFTypeDictionaryKeyCallBacks": lib.get("kCFTypeDictionaryKeyCallBacks"),
            "kCFTypeDictionaryValueCallBacks": lib.get("kCFTypeDictionaryValueCallBacks"),
            "CFRunLoopRunInMode": ffi.ForeignFunction(lib.get("CFRunLoopRunInMode"), CoreTypes.intType, [CoreTypes.cfStringRef, CoreTypes.cfTimeInterval, CoreTypes.boolType]),
            "kCFRunLoopDefaultMode": this.getForeignPointer(lib, "kCFRunLoopDefaultMode", ref.types.void),
            "kCFRunLoopCommonModes": this.getForeignPointer(lib, "kCFRunLoopCommonModes", ref.types.void),
            "CFRunLoopTimerCreate": ffi.ForeignFunction(lib.get("CFRunLoopTimerCreate"), CoreTypes.voidPtr, [CoreTypes.voidPtr, CoreTypes.doubleType, CoreTypes.doubleType, CoreTypes.uintType, CoreTypes.uintType, CoreTypes.cf_run_loop_timer_callback, CoreTypes.voidPtr]),
            "CFRunLoopAddTimer": ffi.ForeignFunction(lib.get("CFRunLoopAddTimer"), "void", [CoreTypes.voidPtr, CoreTypes.voidPtr, CoreTypes.cfStringRef]),
            "CFRunLoopRemoveTimer": ffi.ForeignFunction(lib.get("CFRunLoopRemoveTimer"), "void", [CoreTypes.voidPtr, CoreTypes.voidPtr, CoreTypes.cfStringRef]),
            "CFAbsoluteTimeGetCurrent": ffi.ForeignFunction(lib.get("CFAbsoluteTimeGetCurrent"), CoreTypes.doubleType, []),
            "CFPropertyListCreateData": ffi.ForeignFunction(lib.get("CFPropertyListCreateData"), CoreTypes.voidPtr, [CoreTypes.voidPtr, CoreTypes.voidPtr, ref.types.long, ref.types.ulong, CoreTypes.voidPtr]),
            "CFPropertyListCreateWithData": ffi.ForeignFunction(lib.get("CFPropertyListCreateWithData"), CoreTypes.voidPtr, [CoreTypes.voidPtr, CoreTypes.voidPtr, ref.types.ulong, ref.refType(ref.types.long), CoreTypes.voidPtr]),
            "CFGetTypeID": ffi.ForeignFunction(lib.get("CFGetTypeID"), ref.types.long, [CoreTypes.voidPtr]),
            "CFStringGetTypeID": ffi.ForeignFunction(lib.get("CFStringGetTypeID"), ref.types.long, []),
            "CFDictionaryGetTypeID": ffi.ForeignFunction(lib.get("CFDictionaryGetTypeID"), ref.types.long, []),
            "CFDataGetTypeID": ffi.ForeignFunction(lib.get("CFDataGetTypeID"), ref.types.long, []),
            "CFNumberGetTypeID": ffi.ForeignFunction(lib.get("CFNumberGetTypeID"), ref.types.long, []),
            "CFBooleanGetTypeID": ffi.ForeignFunction(lib.get("CFBooleanGetTypeID"), ref.types.long, []),
            "CFArrayGetTypeID": ffi.ForeignFunction(lib.get("CFArrayGetTypeID"), ref.types.long, []),
            "CFDateGetTypeID": ffi.ForeignFunction(lib.get("CFDateGetTypeID"), ref.types.long, []),
            "CFSetGetTypeID": ffi.ForeignFunction(lib.get("CFSetGetTypeID"), ref.types.long, []),
            "CFDataGetBytePtr": ffi.ForeignFunction(lib.get("CFDataGetBytePtr"), ref.refType(ref.types.uint8), [CoreTypes.voidPtr]),
            "CFDataGetLength": ffi.ForeignFunction(lib.get("CFDataGetLength"), ref.types.long, [CoreTypes.voidPtr]),
            "CFDataCreate": ffi.ForeignFunction(lib.get("CFDataCreate"), CoreTypes.voidPtr, [CoreTypes.voidPtr, CoreTypes.voidPtr, ref.types.long]),
            "CFStringGetMaximumSizeForEncoding": ffi.ForeignFunction(lib.get("CFStringGetMaximumSizeForEncoding"), CoreTypes.intType, [CoreTypes.intType, CoreTypes.uint32Type])
        };
    }
    getMobileDeviceLibrary() {
        let mobileDeviceDll = this.$hostInfo.isWindows ? path.join(this.MobileDeviceDir, "MobileDevice.dll") : this.MobileDeviceDir;
        let lib = ffi.DynamicLibrary(mobileDeviceDll);
        return {
            "AMDeviceNotificationSubscribe": ffi.ForeignFunction(lib.get("AMDeviceNotificationSubscribe"), "uint", [CoreTypes.am_device_notification_callback, "uint", "uint", "uint", CoreTypes.ptrToVoidPtr]),
            "AMDeviceConnect": ffi.ForeignFunction(lib.get("AMDeviceConnect"), "uint", [CoreTypes.am_device_p]),
            "AMDeviceIsPaired": ffi.ForeignFunction(lib.get("AMDeviceIsPaired"), "uint", [CoreTypes.am_device_p]),
            "AMDevicePair": ffi.ForeignFunction(lib.get("AMDevicePair"), "uint", [CoreTypes.am_device_p]),
            "AMDeviceValidatePairing": ffi.ForeignFunction(lib.get("AMDeviceValidatePairing"), "uint", [CoreTypes.am_device_p]),
            "AMDeviceStartSession": ffi.ForeignFunction(lib.get("AMDeviceStartSession"), "uint", [CoreTypes.am_device_p]),
            "AMDeviceStopSession": ffi.ForeignFunction(lib.get("AMDeviceStopSession"), "uint", [CoreTypes.am_device_p]),
            "AMDeviceDisconnect": ffi.ForeignFunction(lib.get("AMDeviceDisconnect"), "uint", [CoreTypes.am_device_p]),
            "AMDeviceStartService": ffi.ForeignFunction(lib.get("AMDeviceStartService"), "uint", [CoreTypes.am_device_p, CoreTypes.cfStringRef, CoreTypes.intPtr, CoreTypes.voidPtr]),
            "AMDeviceTransferApplication": ffi.ForeignFunction(lib.get("AMDeviceTransferApplication"), "uint", ["int", CoreTypes.cfStringRef, CoreTypes.cfDictionaryRef, CoreTypes.am_device_install_application_callback, CoreTypes.voidPtr]),
            "AMDeviceInstallApplication": ffi.ForeignFunction(lib.get("AMDeviceInstallApplication"), "uint", ["int", CoreTypes.cfStringRef, CoreTypes.cfDictionaryRef, CoreTypes.am_device_install_application_callback, CoreTypes.voidPtr]),
            "AMDeviceLookupApplications": ffi.ForeignFunction(lib.get("AMDeviceLookupApplications"), CoreTypes.uintType, [CoreTypes.am_device_p, CoreTypes.uintType, ref.refType(CoreTypes.cfDictionaryRef)]),
            "AMDeviceUninstallApplication": ffi.ForeignFunction(lib.get("AMDeviceUninstallApplication"), "uint", ["int", CoreTypes.cfStringRef, CoreTypes.cfDictionaryRef, CoreTypes.am_device_install_application_callback, CoreTypes.voidPtr]),
            "AMDeviceStartHouseArrestService": ffi.ForeignFunction(lib.get("AMDeviceStartHouseArrestService"), CoreTypes.intType, [CoreTypes.am_device_p, CoreTypes.cfStringRef, CoreTypes.voidPtr, CoreTypes.intPtr, CoreTypes.voidPtr]),
            "AFCConnectionOpen": ffi.ForeignFunction(lib.get("AFCConnectionOpen"), "uint", ["int", "uint", ref.refType(CoreTypes.afcConnectionRef)]),
            "AFCConnectionClose": ffi.ForeignFunction(lib.get("AFCConnectionClose"), "uint", [CoreTypes.afcConnectionRef]),
            "AFCDirectoryCreate": ffi.ForeignFunction(lib.get("AFCDirectoryCreate"), "uint", [CoreTypes.afcConnectionRef, "string"]),
            "AFCFileInfoOpen": ffi.ForeignFunction(lib.get("AFCFileInfoOpen"), "uint", [CoreTypes.afcConnectionRef, "string", CoreTypes.cfDictionaryRef]),
            "AFCFileRefOpen": (this.$hostInfo.isDarwin || process.arch === "x64") ? ffi.ForeignFunction(lib.get("AFCFileRefOpen"), "uint", [CoreTypes.afcConnectionRef, "string", "uint", ref.refType(CoreTypes.afcFileRef)]) : ffi.ForeignFunction(lib.get("AFCFileRefOpen"), "uint", [CoreTypes.afcConnectionRef, "string", "uint", "uint", ref.refType(CoreTypes.afcFileRef)]),
            "AFCFileRefClose": ffi.ForeignFunction(lib.get("AFCFileRefClose"), "uint", [CoreTypes.afcConnectionRef, CoreTypes.afcFileRef]),
            "AFCFileRefWrite": ffi.ForeignFunction(lib.get("AFCFileRefWrite"), "uint", [CoreTypes.afcConnectionRef, CoreTypes.afcFileRef, CoreTypes.voidPtr, "uint"]),
            "AFCFileRefRead": ffi.ForeignFunction(lib.get("AFCFileRefRead"), "uint", [CoreTypes.afcConnectionRef, CoreTypes.afcFileRef, CoreTypes.voidPtr, CoreTypes.uintPtr]),
            "AFCRemovePath": ffi.ForeignFunction(lib.get("AFCRemovePath"), "uint", [CoreTypes.afcConnectionRef, "string"]),
            "AFCDirectoryOpen": ffi.ForeignFunction(lib.get("AFCDirectoryOpen"), CoreTypes.afcError, [CoreTypes.afcConnectionRef, "string", ref.refType(CoreTypes.afcDirectoryRef)]),
            "AFCDirectoryRead": ffi.ForeignFunction(lib.get("AFCDirectoryRead"), CoreTypes.afcError, [CoreTypes.afcConnectionRef, CoreTypes.afcDirectoryRef, ref.refType(CoreTypes.charPtr)]),
            "AFCDirectoryClose": ffi.ForeignFunction(lib.get("AFCDirectoryClose"), CoreTypes.afcError, [CoreTypes.afcConnectionRef, CoreTypes.afcDirectoryRef]),
            "AMDeviceCopyDeviceIdentifier": ffi.ForeignFunction(lib.get("AMDeviceCopyDeviceIdentifier"), CoreTypes.cfStringRef, [CoreTypes.am_device_p]),
            "AMDeviceCopyValue": ffi.ForeignFunction(lib.get("AMDeviceCopyValue"), CoreTypes.cfStringRef, [CoreTypes.am_device_p, CoreTypes.cfStringRef, CoreTypes.cfStringRef]),
            "AMDeviceNotificationUnsubscribe": ffi.ForeignFunction(lib.get("AMDeviceNotificationUnsubscribe"), CoreTypes.intType, [CoreTypes.amDeviceNotificationRef]),
            "AMDeviceMountImage": this.$hostInfo.isDarwin ? ffi.ForeignFunction(lib.get("AMDeviceMountImage"), CoreTypes.uintType, [CoreTypes.am_device_p, CoreTypes.cfStringRef, CoreTypes.cfDictionaryRef, CoreTypes.am_device_mount_image_callback, CoreTypes.voidPtr]) : null,
            "AMDSetLogLevel": ffi.ForeignFunction(lib.get("AMDSetLogLevel"), CoreTypes.intType, [CoreTypes.intType]),
            "AMDeviceGetInterfaceType": ffi.ForeignFunction(lib.get("AMDeviceGetInterfaceType"), CoreTypes.longType, [CoreTypes.am_device_p]),
            "AMDeviceGetConnectionID": ffi.ForeignFunction(lib.get("AMDeviceGetConnectionID"), CoreTypes.longType, [CoreTypes.am_device_p]),
            "USBMuxConnectByPort": ffi.ForeignFunction(lib.get("USBMuxConnectByPort"), CoreTypes.intType, [CoreTypes.intType, CoreTypes.intType, CoreTypes.intPtr])
        };
    }
    static getWinSocketLibrary() {
        let winSocketDll = path.join(process.env.SystemRoot, "System32", "ws2_32.dll");
        return ffi.Library(winSocketDll, {
            "closesocket": ["int", ["uint"]],
            "recv": ["int", ["uint", CoreTypes.charPtr, "int", "int"]],
            "send": ["int", ["uint", CoreTypes.charPtr, "int", "int"]],
            "setsockopt": ["int", ["uint", "int", "int", CoreTypes.voidPtr, "int"]],
            "WSAGetLastError": ["int", []]
        });
    }
}
IOSCore.kCFStringEncodingUTF8 = 0x08000100;
$injector.register("iOSCore", IOSCore);
class CoreFoundation {
    constructor($iOSCore) {
        this.coreFoundationLibrary = $iOSCore.getCoreFoundationLibrary();
    }
    stringGetMaximumSizeForEncoding(len, encoding) {
        return this.coreFoundationLibrary.CFStringGetMaximumSizeForEncoding(len, encoding);
    }
    runLoopRun() {
        this.coreFoundationLibrary.CFRunLoopRun();
    }
    runLoopGetCurrent() {
        return this.coreFoundationLibrary.CFRunLoopGetCurrent();
    }
    kCFRunLoopCommonModes() {
        return this.coreFoundationLibrary.kCFRunLoopCommonModes.deref();
    }
    kCFRunLoopDefaultMode() {
        return this.coreFoundationLibrary.kCFRunLoopDefaultMode.deref();
    }
    kCFTypeDictionaryValueCallBacks() {
        return this.coreFoundationLibrary.kCFTypeDictionaryValueCallBacks;
    }
    kCFTypeDictionaryKeyCallBacks() {
        return this.coreFoundationLibrary.kCFTypeDictionaryKeyCallBacks;
    }
    runLoopTimerCreate(allocator, fireDate, interval, flags, order, callout, context) {
        return this.coreFoundationLibrary.CFRunLoopTimerCreate(allocator, fireDate, interval, flags, order, callout, context);
    }
    absoluteTimeGetCurrent() {
        return this.coreFoundationLibrary.CFAbsoluteTimeGetCurrent();
    }
    runLoopAddTimer(r1, timer, mode) {
        this.coreFoundationLibrary.CFRunLoopAddTimer(r1, timer, mode);
    }
    runLoopRemoveTimer(r1, timer, mode) {
        this.coreFoundationLibrary.CFRunLoopRemoveTimer(r1, timer, mode);
    }
    runLoopStop(r1) {
        this.coreFoundationLibrary.CFRunLoopStop(r1);
    }
    stringGetCStringPtr(theString, encoding) {
        return this.coreFoundationLibrary.CFStringGetCStringPtr(theString, encoding);
    }
    stringGetLength(theString) {
        return this.coreFoundationLibrary.CFStringGetLength(theString);
    }
    stringGetCString(theString, buffer, bufferSize, encoding) {
        return this.coreFoundationLibrary.CFStringGetCString(theString, buffer, bufferSize, encoding);
    }
    stringCreateWithCString(alloc, str, encoding) {
        return this.coreFoundationLibrary.CFStringCreateWithCString(alloc, str, encoding);
    }
    createCFString(str) {
        return this.stringCreateWithCString(null, str, IOSCore.kCFStringEncodingUTF8);
    }
    dictionaryCreate(allocator, keys, values, count, dictionaryKeyCallbacks, dictionaryValueCallbacks) {
        return this.coreFoundationLibrary.CFDictionaryCreate(allocator, keys, values, count, dictionaryKeyCallbacks, dictionaryValueCallbacks);
    }
    dictionaryGetValue(theDict, value) {
        return this.coreFoundationLibrary.CFDictionaryGetValue(theDict, value);
    }
    dictionaryGetCount(theDict) {
        return this.coreFoundationLibrary.CFDictionaryGetCount(theDict);
    }
    dictionaryGetKeysAndValues(dictionary, keys, values) {
        this.coreFoundationLibrary.CFDictionaryGetKeysAndValues(dictionary, keys, values);
    }
    dictionaryGetTypeID() {
        return this.coreFoundationLibrary.CFDictionaryGetTypeID();
    }
    numberGetValue(num, theType, valuePtr) {
        return this.coreFoundationLibrary.CFNumberGetValue(num, theType, valuePtr);
    }
    getTypeID(buffer) {
        return this.coreFoundationLibrary.CFGetTypeID(buffer);
    }
    propertyListCreateData(allocator, propertyListRef, propertyListFormat, optionFlags, error) {
        return this.coreFoundationLibrary.CFPropertyListCreateData(allocator, propertyListRef, propertyListFormat, optionFlags, error);
    }
    propertyListCreateWithData(allocator, propertyList, optionFlags, propertyListFormat, error) {
        return this.coreFoundationLibrary.CFPropertyListCreateWithData(allocator, propertyList, optionFlags, propertyListFormat, error);
    }
    stringGetTypeID() {
        return this.coreFoundationLibrary.CFStringGetTypeID();
    }
    dataGetTypeID() {
        return this.coreFoundationLibrary.CFDataGetTypeID();
    }
    numberGetTypeID() {
        return this.coreFoundationLibrary.CFNumberGetTypeID();
    }
    booleanGetTypeID() {
        return this.coreFoundationLibrary.CFBooleanGetTypeID();
    }
    arrayGetTypeID() {
        return this.coreFoundationLibrary.CFArrayGetTypeID();
    }
    dateGetTypeID() {
        return this.coreFoundationLibrary.CFDateGetTypeID();
    }
    setGetTypeID() {
        return this.coreFoundationLibrary.CFSetGetTypeID();
    }
    dataGetBytePtr(buffer) {
        return this.coreFoundationLibrary.CFDataGetBytePtr(buffer);
    }
    dataGetLength(buffer) {
        return this.coreFoundationLibrary.CFDataGetLength(buffer);
    }
    dataCreate(allocator, data, length) {
        return this.coreFoundationLibrary.CFDataCreate(allocator, data, length);
    }
    convertCFStringToCString(cfstr) {
        let result;
        if (cfstr != null) {
            let rawData = this.stringGetCStringPtr(cfstr, IOSCore.kCFStringEncodingUTF8);
            if (ref.address(rawData) === 0) {
                let cfstrLength = this.stringGetLength(cfstr);
                let length = cfstrLength + 1;
                let stringBuffer = new Buffer(length);
                let status = this.stringGetCString(cfstr, stringBuffer, length, IOSCore.kCFStringEncodingUTF8);
                if (status) {
                    result = stringBuffer.toString("utf8", 0, cfstrLength);
                }
            }
            else {
                result = ref.readCString(rawData, 0);
            }
        }
        return result;
    }
    cfTypeFrom(value) {
        let keys = _.keys(value);
        let values = _.values(value);
        let len = keys.length;
        let keysBuffer = new Buffer(CoreTypes.pointerSize * len);
        let valuesBuffer = new Buffer(CoreTypes.pointerSize * len);
        let offset = 0;
        for (let i = 0; i < len; i++) {
            let cfKey = this.createCFString(keys[i]);
            let cfValue;
            if (typeof values[i] === "string") {
                cfValue = this.createCFString(values[i]);
            }
            else if (values[i] instanceof Buffer) {
                cfValue = this.dataCreate(null, values[i], values[i].length);
            }
            else {
                cfValue = this.cfTypeFrom(values[i]);
            }
            ref.writePointer(keysBuffer, offset, cfKey);
            ref.writePointer(valuesBuffer, offset, cfValue);
            offset += CoreTypes.pointerSize;
        }
        return this.dictionaryCreate(null, keysBuffer, valuesBuffer, len, this.kCFTypeDictionaryKeyCallBacks(), this.kCFTypeDictionaryValueCallBacks());
    }
    cfTypeTo(dataRef) {
        let typeId = this.getTypeID(dataRef);
        if (typeId === this.stringGetTypeID()) {
            return this.convertCFStringToCString(dataRef);
        }
        else if (typeId === this.dataGetTypeID()) {
            let len = this.dataGetLength(dataRef);
            let retval = ref.reinterpret(this.dataGetBytePtr(dataRef), len);
            return retval;
        }
        else if (typeId === this.dictionaryGetTypeID()) {
            let count = this.dictionaryGetCount(dataRef);
            let keys = new Buffer(count * CoreTypes.pointerSize);
            let values = new Buffer(count * CoreTypes.pointerSize);
            this.dictionaryGetKeysAndValues(dataRef, keys, values);
            let jsDictionary = Object.create(null);
            let offset = 0;
            for (let i = 0; i < count; i++) {
                let keyPointer = ref.readPointer(keys, offset, CoreTypes.pointerSize);
                let valuePointer = ref.readPointer(values, offset, CoreTypes.pointerSize);
                offset += CoreTypes.pointerSize;
                let jsKey = this.cfTypeTo(keyPointer);
                let jsValue = this.cfTypeTo(valuePointer);
                jsDictionary[jsKey] = jsValue;
            }
            return jsDictionary;
        }
        else {
            return "";
        }
    }
    dictToPlistEncoding(dict, format) {
        let cfDict = this.cfTypeFrom(dict);
        let cfData = this.propertyListCreateData(null, cfDict, format, 0, null);
        return this.cfTypeTo(cfData);
    }
    dictFromPlistEncoding(str) {
        let retval = null;
        let cfData = this.dataCreate(null, str, str.length);
        if (cfData) {
            let cfDict = this.propertyListCreateWithData(null, cfData, CoreTypes.kCFPropertyListImmutable, null, null);
            if (cfDict) {
                retval = this.cfTypeTo(cfDict);
            }
        }
        return retval;
    }
}
exports.CoreFoundation = CoreFoundation;
$injector.register("coreFoundation", CoreFoundation);
class MobileDevice {
    constructor($iOSCore, $errors, $hostInfo) {
        this.$errors = $errors;
        this.$hostInfo = $hostInfo;
        this.mobileDeviceLibrary = $iOSCore.getMobileDeviceLibrary();
    }
    deviceNotificationSubscribe(notificationCallback, p1, p2, p3, callbackSignature) {
        return this.mobileDeviceLibrary.AMDeviceNotificationSubscribe(notificationCallback, p1, p2, p3, callbackSignature);
    }
    deviceCopyDeviceIdentifier(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceCopyDeviceIdentifier(devicePointer);
    }
    deviceCopyValue(devicePointer, domain, name) {
        return this.mobileDeviceLibrary.AMDeviceCopyValue(devicePointer, domain, name);
    }
    deviceConnect(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceConnect(devicePointer);
    }
    deviceIsPaired(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceIsPaired(devicePointer);
    }
    devicePair(devicePointer) {
        return this.mobileDeviceLibrary.AMDevicePair(devicePointer);
    }
    deviceValidatePairing(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceValidatePairing(devicePointer);
    }
    deviceStartSession(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceStartSession(devicePointer);
    }
    deviceStopSession(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceStopSession(devicePointer);
    }
    deviceDisconnect(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceDisconnect(devicePointer);
    }
    deviceStartService(devicePointer, serviceName, socketNumber) {
        return this.mobileDeviceLibrary.AMDeviceStartService(devicePointer, serviceName, socketNumber, null);
    }
    deviceTransferApplication(service, packageFile, options, installationCallback) {
        return this.mobileDeviceLibrary.AMDeviceTransferApplication(service, packageFile, options, installationCallback, null);
    }
    deviceInstallApplication(service, packageFile, options, installationCallback) {
        return this.mobileDeviceLibrary.AMDeviceInstallApplication(service, packageFile, options, installationCallback, null);
    }
    deviceUninstallApplication(service, bundleId, options, callback) {
        return this.mobileDeviceLibrary.AMDeviceUninstallApplication(service, bundleId, options, callback, null);
    }
    deviceStartHouseArrestService(devicePointer, bundleId, options, fdRef) {
        return this.mobileDeviceLibrary.AMDeviceStartHouseArrestService(devicePointer, bundleId, options, fdRef, null);
    }
    deviceMountImage(devicePointer, imagePath, options, mountCallBack) {
        if (this.$hostInfo.isDarwin) {
            return this.mobileDeviceLibrary.AMDeviceMountImage(devicePointer, imagePath, options, mountCallBack, null);
        }
        this.$errors.fail("AMDeviceMountImage is exported only on Darwin OS");
    }
    deviceLookupApplications(devicePointer, appType, result) {
        return this.mobileDeviceLibrary.AMDeviceLookupApplications(devicePointer, appType, result);
    }
    deviceGetInterfaceType(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceGetInterfaceType(devicePointer);
    }
    deviceGetConnectionId(devicePointer) {
        return this.mobileDeviceLibrary.AMDeviceGetConnectionID(devicePointer);
    }
    afcConnectionOpen(service, timeout, afcConnection) {
        return this.mobileDeviceLibrary.AFCConnectionOpen(service, timeout, afcConnection);
    }
    afcConnectionClose(afcConnection) {
        return this.mobileDeviceLibrary.AFCConnectionClose(afcConnection);
    }
    afcDirectoryCreate(afcConnection, path) {
        return this.mobileDeviceLibrary.AFCDirectoryCreate(afcConnection, path);
    }
    afcFileInfoOpen(afcConnection, path, afcDirectory) {
        return this.mobileDeviceLibrary.AFCFileInfoOpen(afcConnection, path, afcDirectory);
    }
    afcFileRefOpen(afcConnection, path, mode, afcFileRef) {
        if (this.$hostInfo.isWindows && process.arch === "ia32") {
            return this.mobileDeviceLibrary.AFCFileRefOpen(afcConnection, path, mode, 0, afcFileRef);
        }
        else if (this.$hostInfo.isDarwin || process.arch === "x64") {
            return this.mobileDeviceLibrary.AFCFileRefOpen(afcConnection, path, mode, afcFileRef);
        }
    }
    afcFileRefClose(afcConnection, afcFileRef) {
        return this.mobileDeviceLibrary.AFCFileRefClose(afcConnection, afcFileRef);
    }
    afcFileRefWrite(afcConnection, afcFileRef, buffer, byteLength) {
        return this.mobileDeviceLibrary.AFCFileRefWrite(afcConnection, afcFileRef, buffer, byteLength);
    }
    afcFileRefRead(afcConnection, afcFileRef, buffer, byteLength) {
        return this.mobileDeviceLibrary.AFCFileRefRead(afcConnection, afcFileRef, buffer, byteLength);
    }
    afcRemovePath(afcConnection, path) {
        return this.mobileDeviceLibrary.AFCRemovePath(afcConnection, path);
    }
    afcDirectoryOpen(afcConnection, path, afcDirectory) {
        return this.mobileDeviceLibrary.AFCDirectoryOpen(afcConnection, path, afcDirectory);
    }
    afcDirectoryRead(afcConnection, afcDirectory, name) {
        return this.mobileDeviceLibrary.AFCDirectoryRead(afcConnection, afcDirectory, name);
    }
    afcDirectoryClose(afcConnection, afcDirectory) {
        return this.mobileDeviceLibrary.AFCDirectoryClose(afcConnection, afcDirectory);
    }
    isDataReceivingCompleted(reply) {
        return reply.Status && reply.Complete && !reply.PercentComplete;
    }
    setLogLevel(logLevel) {
        return this.mobileDeviceLibrary.AMDSetLogLevel(logLevel);
    }
    uSBMuxConnectByPort(connectionId, port, socketRef) {
        return this.mobileDeviceLibrary.USBMuxConnectByPort(connectionId, port, socketRef);
    }
}
exports.MobileDevice = MobileDevice;
$injector.register("mobileDevice", MobileDevice);
class WinSocket {
    constructor(service, format, $logger, $errors, $childProcess, $staticConfig) {
        this.service = service;
        this.format = format;
        this.$logger = $logger;
        this.$errors = $errors;
        this.$childProcess = $childProcess;
        this.$staticConfig = $staticConfig;
        this.winSocketLibrary = null;
        this.winSocketLibrary = IOSCore.getWinSocketLibrary();
    }
    read(bytes) {
        let data = new Buffer(bytes);
        let result;
        helpers.block(() => {
            result = this.winSocketLibrary.recv(this.service, data, bytes, 0);
        });
        if (result < 0) {
            this.$errors.fail("Error receiving data: %s", result);
        }
        else if (result === 0) {
            return null;
        }
        return data;
    }
    readSystemLogBlocking() {
        let data = this.read(WinSocket.BYTES_TO_READ);
        while (data) {
            const messageWithoutNullCharacters = data.toString().replace("\0", "");
            const bufferWithoutNullCharacters = new Buffer(messageWithoutNullCharacters);
            const output = ref.readCString(bufferWithoutNullCharacters, 0);
            process.send(output);
            data = this.read(WinSocket.BYTES_TO_READ);
        }
        this.close();
    }
    readSystemLog(printData) {
        let serviceArg = this.service || '';
        let formatArg = this.format || '';
        let sysLog = this.$childProcess.fork(path.join(__dirname, "ios-sys-log.js"), [this.$staticConfig.PATH_TO_BOOTSTRAP, serviceArg.toString(), formatArg.toString()], { silent: true });
        sysLog.on('message', (data) => {
            printData(data);
        });
    }
    receiveMessage() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.format === CoreTypes.kCFPropertyListXMLFormat_v1_0) {
                let message = this.receiveMessageCore();
                return plist.parse(message.toString());
            }
            if (this.format === CoreTypes.kCFPropertyListBinaryFormat_v1_0) {
                return this.receiveBinaryMessage();
            }
            return null;
        });
    }
    sendMessage(data) {
        let message = null;
        if (typeof (data) === "string") {
            message = new Buffer(data);
        }
        else {
            let payload = new Buffer(plistlib.toString(this.createPlist(data)));
            let packed = bufferpack.pack(">i", [payload.length]);
            message = Buffer.concat([packed, payload]);
        }
        let writtenBytes = this.sendCore(message);
        this.$logger.debug("WinSocket-> sending message: '%s', written bytes: '%s'", message.toString(), writtenBytes.toString());
        this.$errors.verifyHeap("sendMessage");
    }
    sendAll(data) {
        while (data.length !== 0) {
            let result = this.sendCore(data);
            if (result < 0) {
                this.$errors.fail("Error sending data: %s", result);
            }
            data = data.slice(result);
        }
    }
    receiveAll(handler) {
        let data = this.read(WinSocket.BYTES_TO_READ);
        while (data) {
            handler(data);
            data = this.read(WinSocket.BYTES_TO_READ);
        }
        this.close();
    }
    exchange(message) {
        this.sendMessage(message);
        return this.receiveMessage();
    }
    close() {
        this.winSocketLibrary.closesocket(this.service);
        this.$errors.verifyHeap("socket close");
    }
    receiveBinaryMessage() {
        let result = [];
        while (true) {
            let partialReply = this.receiveMessageCore();
            if (!partialReply.length) {
                break;
            }
            let currentResult = bplistParser.parseBuffer(partialReply)[0];
            if (currentResult.Status === "Complete" || !currentResult.Status) {
                break;
            }
            if (currentResult.Error) {
                throw new Error(currentResult.Error);
            }
            result = result.concat(currentResult);
            this.$errors.verifyHeap("receiveMessage");
        }
        return result;
    }
    receiveMessageCore() {
        let data = this.read(4);
        let reply = new Buffer(0);
        if (data !== null && data.length === 4) {
            let bufferLength = bufferpack.unpack(">i", data)[0];
            let remainingSymbols = bufferLength;
            while (remainingSymbols > 0) {
                let currentBuffer = this.read(remainingSymbols);
                if (currentBuffer === null) {
                    this.$errors.fail("Unable to read reply");
                }
                reply = Buffer.concat([reply, currentBuffer]);
                remainingSymbols -= currentBuffer.length;
            }
        }
        this.$errors.verifyHeap("receiveMessage");
        return reply;
    }
    sendCore(data) {
        let writtenBytes = this.winSocketLibrary.send(this.service, data, data.length, 0);
        this.$logger.debug("WinSocket-> sendCore: writtenBytes '%s'", writtenBytes);
        return writtenBytes;
    }
    createPlist(data) {
        let plistData = {
            type: "dict", value: this.getDataFromObject(data)
        };
        this.$logger.trace("created plist: '%s'", plistData.toString());
        return plistData;
    }
    getDataFromObject(data) {
        let keys = _.keys(data);
        let values = _.values(data);
        let plistData = {};
        for (let i = 0; i < keys.length; i++) {
            plistData[keys[i]] = this.parseValue(values[i]);
        }
        return plistData;
    }
    parseValue(data) {
        let type = "", value;
        if (data instanceof Buffer) {
            type = "data";
            value = data.toString("base64");
        }
        else if (data instanceof Array) {
            type = "array";
            let objs = _.map(data, v => this.parseValue(v));
            value = objs;
        }
        else if (data instanceof Object) {
            type = "dict";
            value = this.getDataFromObject(data);
        }
        else if (typeof (data) === "number") {
            type = "integer";
            value = data;
        }
        else {
            type = "string";
            value = data;
        }
        return { type: type, value: value };
    }
}
WinSocket.BYTES_TO_READ = 1024;
exports.WinSocket = WinSocket;
var ReadState;
(function (ReadState) {
    ReadState[ReadState["Length"] = 0] = "Length";
    ReadState[ReadState["Plist"] = 1] = "Plist";
})(ReadState || (ReadState = {}));
class PosixSocket {
    constructor(service, format, $coreFoundation, $logger, $errors) {
        this.format = format;
        this.$coreFoundation = $coreFoundation;
        this.$logger = $logger;
        this.$errors = $errors;
        this.socket = null;
        this.buffer = new Buffer(0);
        this.state = ReadState.Length;
        this.length = 4;
        this.socket = new net.Socket({ fd: service.toString() });
    }
    receiveMessage() {
        return new Promise((resolve, reject) => {
            let messages = [];
            let isResolved = false;
            this.socket
                .on("data", (data) => {
                this.buffer = Buffer.concat([this.buffer, data]);
                try {
                    while (this.buffer.length >= this.length) {
                        switch (this.state) {
                            case ReadState.Length:
                                this.length = this.buffer.readInt32BE(0);
                                this.buffer = this.buffer.slice(4);
                                this.state = ReadState.Plist;
                                break;
                            case ReadState.Plist:
                                try {
                                    let plistBuffer = this.buffer.slice(0, this.length);
                                    let message;
                                    if (this.format === CoreTypes.kCFPropertyListBinaryFormat_v1_0) {
                                        message = bplistParser.parseBuffer(plistBuffer);
                                    }
                                    else if (this.format === CoreTypes.kCFPropertyListXMLFormat_v1_0) {
                                        message = plist.parse(this.buffer.toString());
                                    }
                                    this.$logger.trace("MESSAGE RECEIVING");
                                    this.$logger.trace(message);
                                    try {
                                        message = _.isArray(message) && message[0] || message;
                                        if (message && typeof (message) === "object") {
                                            let output = "";
                                            if (message.Status) {
                                                output += util.format("Status: %s", message.Status);
                                            }
                                            if (message.PercentComplete) {
                                                output += util.format(" PercentComplete: %s", message.PercentComplete);
                                            }
                                            this.$logger.trace(output);
                                            let errorMessage = "";
                                            if (message.Error) {
                                                errorMessage += `Error: ${message.Error} ${os_1.EOL}`;
                                            }
                                            if (message.ErrorDescription) {
                                                errorMessage += `ErrorDescription: ${message.ErrorDescription} ${os_1.EOL}`;
                                            }
                                            if (message.ErrorDetail) {
                                                errorMessage += `ErrorDetail: ${message.ErrorDetail} ${os_1.EOL}`;
                                            }
                                            if (errorMessage && !isResolved) {
                                                isResolved = true;
                                                reject(new Error(errorMessage));
                                            }
                                            if (message.Status && message.Status === "Complete") {
                                                if (!isResolved) {
                                                    isResolved = true;
                                                    resolve(messages);
                                                }
                                            }
                                            else {
                                                messages.push(message);
                                            }
                                            let status = message.Status;
                                            let percentComplete = message.PercentComplete;
                                            this.$logger.trace("Status: " + status + " PercentComplete: " + percentComplete);
                                        }
                                    }
                                    catch (e) {
                                        this.$logger.trace("Failed to retreive state: " + e);
                                    }
                                }
                                catch (e) {
                                    this.$logger.trace("Failed to parse bplist: " + e);
                                }
                                this.buffer = this.buffer.slice(this.length);
                                this.state = ReadState.Length;
                                this.length = 4;
                                break;
                        }
                    }
                }
                catch (e) {
                    this.$logger.trace("Exception thrown: " + e);
                }
            })
                .on("error", (error) => {
                if (!isResolved) {
                    isResolved = true;
                    reject(error);
                }
            });
        });
    }
    readSystemLog(action) {
        this.socket
            .on("data", (data) => {
            action(data.toString());
        })
            .on("end", () => {
            this.close();
            this.$errors.verifyHeap("readSystemLog");
        })
            .on("error", (error) => {
            this.$errors.fail(error);
        });
    }
    sendMessage(message, format) {
        if (typeof (message) === "string") {
            this.socket.write(message);
        }
        else {
            let data = this.$coreFoundation.dictToPlistEncoding(message, format);
            let payload = bufferpack.pack(">i", [data.length]);
            this.$logger.trace("PlistService sending: ");
            this.$logger.trace(data.toString());
            this.socket.write(payload);
            this.socket.write(data);
        }
        this.$errors.verifyHeap("sendMessage");
    }
    receiveAll(handler) {
        this.socket.on('data', handler);
    }
    exchange(message) {
        return __awaiter(this, void 0, void 0, function* () {
            this.$errors.fail("Exchange function is not implemented for OSX");
            return null;
        });
    }
    close() {
        this.socket.destroy();
        this.$errors.verifyHeap("socket close");
    }
}
class PlistService {
    constructor(service, format, $injector, $processService, $hostInfo) {
        this.service = service;
        this.format = format;
        this.$injector = $injector;
        this.$processService = $processService;
        this.$hostInfo = $hostInfo;
        this.socket = null;
        if (this.$hostInfo.isWindows) {
            this.socket = this.$injector.resolve(WinSocket, { service: this.service, format: this.format });
        }
        else if (this.$hostInfo.isDarwin) {
            this.socket = this.$injector.resolve(PosixSocket, { service: this.service, format: this.format });
        }
        this.$processService.attachToProcessExitSignals(this, this.close);
    }
    receiveMessage() {
        return this.socket.receiveMessage();
    }
    readSystemLog(action) {
        this.socket.readSystemLog(action);
    }
    sendMessage(message) {
        this.socket.sendMessage(message, this.format);
    }
    exchange(message) {
        return this.socket.exchange(message);
    }
    close() {
        this.socket.close();
    }
    sendAll(data) {
        this.socket.sendAll(data);
    }
    receiveAll(handler) {
        if (this.socket.receiveAll) {
            this.socket.receiveAll(handler);
        }
    }
}
exports.PlistService = PlistService;
function getCharacterCodePoint(ch) {
    assert.equal(ch.length, 1);
    let code = ch.charCodeAt(0);
    assert.ok(!(0xD800 <= code && code <= 0xffff));
    return code;
}
class GDBStandardOutputAdapter extends stream.Transform {
    constructor(deviceIdentifier, $deviceLogProvider, $devicePlatformsConstants) {
        super();
        this.deviceIdentifier = deviceIdentifier;
        this.$deviceLogProvider = $deviceLogProvider;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.utf8StringDecoder = new string_decoder.StringDecoder("utf8");
    }
    _transform(packet, encoding, done) {
        try {
            let result = "";
            for (let i = 0; i < packet.length; i++) {
                if (packet[i] === getCharacterCodePoint("$")) {
                    let start = ++i;
                    while (packet[i] !== getCharacterCodePoint("#")) {
                        i++;
                    }
                    let end = i;
                    i++;
                    i++;
                    if (!(packet[start] === getCharacterCodePoint("O") && packet[start + 1] !== getCharacterCodePoint("K"))) {
                        continue;
                    }
                    start++;
                    let hexString = packet.toString("ascii", start, end);
                    let hex = new Buffer(hexString, "hex");
                    result += this.utf8StringDecoder.write(hex);
                }
            }
            if (this.$deviceLogProvider) {
                this.$deviceLogProvider.logData(result, this.$devicePlatformsConstants.iOS, this.deviceIdentifier);
            }
            done(null, result);
        }
        catch (e) {
            done(e);
        }
    }
}
class GDBSignalWatcher extends stream.Writable {
    constructor(opts) {
        super(opts);
    }
    _write(packet, encoding, callback) {
        try {
            const dollarCodePoint = getCharacterCodePoint("$");
            const TCodePoint = getCharacterCodePoint("T");
            const SCodePoint = getCharacterCodePoint("S");
            for (let i = 0; i < packet.length - 3; i++) {
                if (packet[i] === dollarCodePoint && (packet[i + 1] === TCodePoint || packet[i + 1] === SCodePoint)) {
                    let signalHex = packet.toString("ascii", i + 2, i + 4);
                    let signalDecimal = parseInt(signalHex, 16);
                    if (signalDecimal === 5 || signalDecimal === 6 || signalDecimal === 9 || signalDecimal === 11 || signalDecimal === 145) {
                        process.exit(1);
                    }
                }
            }
            callback(null);
        }
        catch (e) {
            callback(e);
        }
    }
}
class GDBServer {
    constructor(socket, deviceIdentifier, $injector, $hostInfo, $options, $logger, $errors) {
        this.socket = socket;
        this.deviceIdentifier = deviceIdentifier;
        this.$injector = $injector;
        this.$hostInfo = $hostInfo;
        this.$options = $options;
        this.$logger = $logger;
        this.$errors = $errors;
        this.okResponse = "$OK#";
        if (this.$hostInfo.isWindows) {
            let winSocket = this.$injector.resolve(WinSocket, { service: this.socket, format: 0 });
            this.socket = {
                write: (message) => {
                    winSocket.sendMessage(message);
                }
            };
        }
        this.socket.on("close", (hadError) => this.$logger.trace("GDB socket get closed. HadError", hadError.toString()));
    }
    init(argv) {
        return __awaiter(this, void 0, void 0, function* () {
            this.awaitResponse("QStartNoAckMode", "+ await ");
            this.sendCore("+");
            yield this.awaitResponse("QEnvironmentHexEncoded:");
            yield this.awaitResponse("QSetDisableASLR:1");
            let encodedArguments = _.map(argv, (arg, index) => util.format("%d,%d,%s", arg.length * 2, index, this.toHex(arg))).join(",");
            yield this.awaitResponse("A" + encodedArguments);
        });
    }
    run(argv) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.init(argv);
            yield this.awaitResponse("qLaunchSuccess");
            if (this.$hostInfo.isWindows) {
                this.send("vCont;c");
            }
            else {
                if (this.$options.justlaunch) {
                    if (this.$options.watch) {
                        this.sendCore(this.encodeData("vCont;c"));
                    }
                    else {
                        this.sendCore(this.encodeData("D"));
                    }
                }
                else {
                    this.socket.pipe(this.$injector.resolve(GDBStandardOutputAdapter, { deviceIdentifier: this.deviceIdentifier }));
                    this.socket.pipe(new GDBSignalWatcher());
                    this.sendCore(this.encodeData("vCont;c"));
                }
            }
        });
    }
    kill(argv) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.init(argv);
            yield this.awaitResponse("\x03", "thread", this.sendx03Message);
            yield this.send("k");
        });
    }
    destroy() {
        this.socket.destroy();
    }
    awaitResponse(packet, expectedResponse, getResponseAction) {
        return __awaiter(this, void 0, void 0, function* () {
            expectedResponse = expectedResponse || this.okResponse;
            let actualResponse = getResponseAction ? yield getResponseAction.apply(this, []) : yield this.send(packet);
            if (actualResponse.indexOf(expectedResponse) === -1 || _.startsWith(actualResponse, "$E")) {
                this.$logger.trace(`GDB: actual response: ${actualResponse}, expected response: ${expectedResponse}`);
                this.$errors.failWithoutHelp(`Unable to send ${packet}.`);
            }
        });
    }
    send(packet) {
        return new Promise((resolve, reject) => {
            let isResolved = false;
            let dataCallback = (data) => {
                this.$logger.trace(`GDB: read packet: ${data}`);
                this.socket.removeListener("data", dataCallback);
                if (!isResolved) {
                    isResolved = true;
                    resolve(data.toString());
                }
            };
            this.socket.on("data", dataCallback);
            this.socket.on("error", (error) => {
                if (!isResolved) {
                    isResolved = true;
                    reject(new Error(error));
                }
            });
            this.sendCore(this.encodeData(packet));
        });
    }
    sendCore(data) {
        this.$logger.trace(`GDB: send packet ${data}`);
        this.socket.write(data);
    }
    sendx03Message() {
        return new Promise((resolve, reject) => {
            let isResolved = false;
            let retryCount = 3;
            let isDataReceived = false;
            let timer = setInterval(() => {
                this.sendCore("\x03");
                retryCount--;
                let secondTimer = setTimeout(() => {
                    if (isDataReceived || !retryCount) {
                        clearTimeout(secondTimer);
                        clearInterval(timer);
                    }
                    if (!retryCount && !isResolved) {
                        isResolved = true;
                        reject(new Error("Unable to kill the application."));
                    }
                }, 1000);
            }, 1000);
            let dataCallback = (data) => {
                let dataAsString = data.toString();
                if (dataAsString.indexOf("thread") > -1) {
                    isDataReceived = true;
                    this.socket.removeListener("data", dataCallback);
                    clearInterval(timer);
                    if (!isResolved) {
                        isResolved = true;
                        resolve(data.toString());
                    }
                }
            };
            this.socket.on("data", dataCallback);
            this.sendCore("\x03");
        });
    }
    encodeData(packet) {
        let sum = 0;
        for (let i = 0; i < packet.length; i++) {
            sum += getCharacterCodePoint(packet[i]);
        }
        sum = sum & 255;
        let data = util.format("$%s#%s", packet, sum.toString(16));
        return data;
    }
    toHex(value) {
        return new Buffer(value).toString("hex");
    }
}
__decorate([
    decorators_1.cache()
], GDBServer.prototype, "init", null);
exports.GDBServer = GDBServer;
$injector.register("gdbServer", GDBServer);
