//
//  UIDevice-Reachability.m
//  CpcProject
//
//  Created by by Jason Lee on 13. 6. 28..
//  Copyright (c) jasondevelop. All rights reserved.
//

@import SystemConfiguration;
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <ifaddrs.h>
#import <dlfcn.h>


#import "UIDevice-Reachability.h"
//#import "URLControllr.h"

@implementation UIDevice (Reachability)
SCNetworkConnectionFlags connectionFlags;
SCNetworkReachabilityRef reachability;

#pragma mark Checking Connections
+ (void) pingReachabilityInternal{
	if (!reachability){
		BOOL ignoresAdHocWiFi = NO;
		struct sockaddr_in ipAddress;
		bzero(&ipAddress, sizeof(ipAddress));
		ipAddress.sin_len = sizeof(ipAddress);
		ipAddress.sin_family = AF_INET;
		ipAddress.sin_addr.s_addr = htonl(ignoresAdHocWiFi ? INADDR_ANY : IN_LINKLOCALNETNUM);
		
		reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&ipAddress);
		CFRetain(reachability);
	}
	
	// Recover reachability flags
	BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(reachability, &connectionFlags);
	if (!didRetrieveFlags) {
//        printf("Error. Could not recover network reachability flags\n");   
    }
}

+ (NSString *) localWiFiIPAddress{
	BOOL success;
	struct ifaddrs * addrs;
	const struct ifaddrs * cursor;
	NSString *temp = nil;
	success = getifaddrs(&addrs) == 0;
	if (success) {
		cursor = addrs;
		while (cursor != NULL) {
			// the second test keeps from picking up the loopback address
			if (cursor->ifa_addr->sa_family ==
				AF_INET && (cursor->ifa_flags & IFF_LOOPBACK) == 0) 
			{
				NSString *name = [NSString stringWithUTF8String:cursor->ifa_name];
				if ([name isEqualToString:@"en0"]){  // Wi-Fi adapter
					//return [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
					temp = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
					break;
				}
			}
			cursor = cursor->ifa_next;
		}
		freeifaddrs(addrs);
	}
	return temp;
}

+ (BOOL) networkAvailable{
	[self pingReachabilityInternal];
	BOOL isReachable = ((connectionFlags & kSCNetworkFlagsReachable) != 0);
    BOOL needsConnection = ((connectionFlags & kSCNetworkFlagsConnectionRequired) != 0);
    return (isReachable && !needsConnection) ? YES : NO;
}

+ (BOOL) activeWWAN{
	if (![self networkAvailable]) return NO;
	return ((connectionFlags & kSCNetworkReachabilityFlagsIsWWAN) != 0);
}

+ (BOOL) activeWLAN{
	return ([UIDevice localWiFiIPAddress] != nil);
}

#pragma mark Monitoring reachability
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkConnectionFlags flags, void* info){
//	NSAutoreleasePool *pool = [NSAutoreleasePool new];
	[(__bridge id)info performSelector:@selector(reachabilityChanged)];
//	[pool release];
}

+ (BOOL) scheduleReachabilityWatcher: (id) watcher{
	if (![watcher conformsToProtocol:@protocol(ReachabilityWatcher)]) {
		NSLog(@"Watcher must conform to ReachabilityWatcher protocol. Cannot continue.");
		return NO;
	}
	
	[self pingReachabilityInternal];
	
	SCNetworkReachabilityContext context = {0, (__bridge void *)(watcher), NULL, NULL, NULL};
	if(SCNetworkReachabilitySetCallback(reachability, ReachabilityCallback, &context)) {
		if(!SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopCommonModes)) {
			NSLog(@"Error: Could not schedule reachability");
			SCNetworkReachabilitySetCallback(reachability, NULL, NULL);
			return NO;
		}
	} else {
		NSLog(@"Error: Could not set reachability callback");
		return NO;
	}
	
	return YES;
}

+ (void) unscheduleReachabilityWatcher{
	SCNetworkReachabilitySetCallback(reachability, NULL, NULL);
	if (SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopCommonModes))
		NSLog(@"Unscheduled reachability");
	else
		NSLog(@"Error: Could not unschedule reachability");
	
	CFRelease(reachability);
	reachability = nil;
}

#ifdef SUPPORTS_UNDOCUMENTED_API
#define SBSERVPATH  "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"
#define UIKITPATH "/System/Library/Framework/UIKit.framework/UIKit"

// Don't use this code in real life, boys and girls. It is not App Store friendly.
// It is, however, really nice for testing callbacks
+ (void) setAPMode: (BOOL) yorn{
	mach_port_t *thePort;
	void *uikit = dlopen(UIKITPATH, RTLD_LAZY);
	int (*SBSSpringBoardServerPort)() = dlsym(uikit, "SBSSpringBoardServerPort");
	thePort = (mach_port_t *)SBSSpringBoardServerPort(); 
	dlclose(uikit);
	
	// Link to SBSetAirplaneModeEnabled
	void *sbserv = dlopen(SBSERVPATH, RTLD_LAZY);
	int (*setAPMode)(mach_port_t* port, BOOL yorn) = dlsym(sbserv, "SBSetAirplaneModeEnabled");
	setAPMode(thePort, yorn);
	dlclose(sbserv);
}
#endif
@end