export const enum connectionType {
    none = 0,
    wifi = 1,
    mobile = 2,
}

// Get Connection Type
declare const sockaddr;
function _createReachability(host?: string): any {
    if (host) {
        return SCNetworkReachabilityCreateWithName(null, host);
    } else {
        const zeroAddress = new interop.Reference<sockaddr>(sockaddr, {
            sa_len: 16,
            sa_family: 2
        });
        return SCNetworkReachabilityCreateWithAddress(null, zeroAddress);
    }
}

function _getReachabilityFlags(host?: string): number {
    const reachability = _createReachability(host);
    const flagsRef = new interop.Reference<number>();
    const gotFlags = SCNetworkReachabilityGetFlags(reachability, flagsRef);
    if (!gotFlags) {
        return null;
    }
    return flagsRef.value;
}

function _getConnectionType(host?: string): number {
    const flags = _getReachabilityFlags(host);
    return _getConnectionTypeFromFlags(flags);
}

function _getConnectionTypeFromFlags(flags: number): number {
    if (!flags) {
        return connectionType.none;
    }

    const isReachable = flags & SCNetworkReachabilityFlags.kSCNetworkReachabilityFlagsReachable;
    const connectionRequired = flags & SCNetworkReachabilityFlags.kSCNetworkReachabilityFlagsConnectionRequired;
    if (!isReachable || connectionRequired) {
        return connectionType.none;
    }

    const isWWAN = flags & SCNetworkReachabilityFlags.kSCNetworkReachabilityFlagsIsWWAN;
    if (isWWAN) {
        return connectionType.mobile;
    }

    return connectionType.wifi;
}

export function getConnectionType(): number {
    return _getConnectionType();
}

// Start/Stop Monitoring
function _reachabilityCallback(target: any, flags: number, info: any) {
    if (_connectionTypeChangedCallback) {
        const newConnectionType = _getConnectionTypeFromFlags(flags);
        _connectionTypeChangedCallback(newConnectionType);
    }
}

const _reachabilityCallbackFunctionRef = new interop.FunctionReference(_reachabilityCallback);

let _monitorReachabilityRef: any;
let _connectionTypeChangedCallback: (newConnectionType: number) => void;

export function startMonitoring(connectionTypeChangedCallback: (newConnectionType: number) => void): void {
    if (!_monitorReachabilityRef) {
        _monitorReachabilityRef = _createReachability();
        _connectionTypeChangedCallback = connectionTypeChangedCallback;
        SCNetworkReachabilitySetCallback(_monitorReachabilityRef, _reachabilityCallbackFunctionRef, null);
        SCNetworkReachabilityScheduleWithRunLoop(_monitorReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    }
}

export function stopMonitoring(): void {
    if (_monitorReachabilityRef) {
        SCNetworkReachabilityUnscheduleFromRunLoop(_monitorReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        _monitorReachabilityRef = undefined;
        _connectionTypeChangedCallback = undefined;
    }
}