"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());
    });
};
const device_discovery_1 = require("./device-discovery");
const ios_core_1 = require("../ios/device/ios-core");
const ref = require("ref");
const ios_device_1 = require("../ios/device/ios-device");
class IOSDeviceDiscovery extends device_discovery_1.DeviceDiscovery {
    constructor($errors, $injector, $utils, $logger, $iTunesValidator) {
        super();
        this.$errors = $errors;
        this.$injector = $injector;
        this.$utils = $utils;
        this.$logger = $logger;
        this.$iTunesValidator = $iTunesValidator;
        this.timerCallbackPtr = null;
        this.notificationCallbackPtr = null;
        this.timerCallbackPtr = ios_core_1.CoreTypes.cf_run_loop_timer_callback.toPointer(IOSDeviceDiscovery.timerCallback);
        this.notificationCallbackPtr = ios_core_1.CoreTypes.am_device_notification_callback.toPointer(IOSDeviceDiscovery.deviceNotificationCallback);
    }
    validateiTunes() {
        if (!this._iTunesErrorMessage) {
            this._iTunesErrorMessage = this.$iTunesValidator.getError();
            if (this._iTunesErrorMessage) {
                this.$logger.warn(this._iTunesErrorMessage);
            }
        }
        return !this._iTunesErrorMessage;
    }
    get $coreFoundation() {
        if (!this._coreFoundation) {
            this._coreFoundation = this.$injector.resolve("$coreFoundation");
        }
        return this._coreFoundation;
    }
    get $mobileDevice() {
        if (!this._mobileDevice) {
            this._mobileDevice = this.$injector.resolve("$mobileDevice");
        }
        return this._mobileDevice;
    }
    startLookingForDevices() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.validateiTunes()) {
                this.subscribeForNotifications();
                yield this.checkForDevices();
            }
        });
    }
    checkForDevices() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.validateiTunes()) {
                let defaultTimeoutInSeconds = 1;
                let parsedTimeout = this.$utils.getParsedTimeout(defaultTimeoutInSeconds);
                let timeout = parsedTimeout > defaultTimeoutInSeconds ? parsedTimeout / 1000 : defaultTimeoutInSeconds;
                this.startRunLoopWithTimer(timeout);
            }
        });
    }
    static deviceNotificationCallback(devicePointer, user) {
        let iOSDeviceDiscovery = $injector.resolve("iOSDeviceDiscovery");
        let deviceInfo = ref.deref(devicePointer);
        if (deviceInfo.msg === IOSDeviceDiscovery.ADNCI_MSG_CONNECTED) {
            iOSDeviceDiscovery.createAndAddDevice(deviceInfo.dev);
        }
        else if (deviceInfo.msg === IOSDeviceDiscovery.ADNCI_MSG_DISCONNECTED) {
            let deviceIdentifier = iOSDeviceDiscovery.$coreFoundation.convertCFStringToCString(iOSDeviceDiscovery.$mobileDevice.deviceCopyDeviceIdentifier(deviceInfo.dev));
            iOSDeviceDiscovery.removeDevice(deviceIdentifier);
        }
        else if (deviceInfo.msg === IOSDeviceDiscovery.ADNCI_MSG_TRUSTED) {
            let deviceIdentifier = iOSDeviceDiscovery.$coreFoundation.convertCFStringToCString(iOSDeviceDiscovery.$mobileDevice.deviceCopyDeviceIdentifier(deviceInfo.dev));
            iOSDeviceDiscovery.removeDevice(deviceIdentifier);
            iOSDeviceDiscovery.createAndAddDevice(deviceInfo.dev);
        }
    }
    static timerCallback() {
        let iOSDeviceDiscovery = $injector.resolve("iOSDeviceDiscovery");
        iOSDeviceDiscovery.$coreFoundation.runLoopStop(iOSDeviceDiscovery.$coreFoundation.runLoopGetCurrent());
    }
    validateResult(result, error) {
        if (result !== 0) {
            this.$errors.fail(error);
        }
    }
    subscribeForNotifications() {
        let notifyFunction = ref.alloc(ios_core_1.CoreTypes.amDeviceNotificationRef);
        let result = this.$mobileDevice.deviceNotificationSubscribe(this.notificationCallbackPtr, 0, 0, 0, notifyFunction);
        let error = IOSDeviceDiscovery.APPLE_SERVICE_NOT_STARTED_ERROR_CODE ?
            "Cannot run and complete operations on iOS devices because Apple Mobile Device Service is not started. Verify that iTunes is installed and running on your system." : "Unable to subscribe for notifications";
        this.validateResult(result, error);
        this.$errors.verifyHeap("subscribeForNotifications");
    }
    startRunLoopWithTimer(timeout) {
        let kCFRunLoopDefaultMode = this.$coreFoundation.kCFRunLoopDefaultMode();
        let timer = null;
        if (timeout > 0) {
            let currentTime = this.$coreFoundation.absoluteTimeGetCurrent() + timeout;
            timer = this.$coreFoundation.runLoopTimerCreate(null, currentTime, 0, 0, 0, this.timerCallbackPtr, null);
            this.$coreFoundation.runLoopAddTimer(this.$coreFoundation.runLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
        }
        this.$coreFoundation.runLoopRun();
        if (timeout > 0) {
            this.$coreFoundation.runLoopRemoveTimer(this.$coreFoundation.runLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
        }
        this.$errors.verifyHeap("startRunLoopWithTimer");
    }
    createAndAddDevice(devicePointer) {
        let device = this.$injector.resolve(ios_device_1.IOSDevice, { devicePointer: devicePointer });
        this.addDevice(device);
    }
}
IOSDeviceDiscovery.ADNCI_MSG_CONNECTED = 1;
IOSDeviceDiscovery.ADNCI_MSG_DISCONNECTED = 2;
IOSDeviceDiscovery.ADNCI_MSG_TRUSTED = 4;
IOSDeviceDiscovery.APPLE_SERVICE_NOT_STARTED_ERROR_CODE = 0xE8000063;
exports.IOSDeviceDiscovery = IOSDeviceDiscovery;
$injector.register("iOSDeviceDiscovery", IOSDeviceDiscovery);
