//
//  PhobosUtil.m
//  Pods
//
//  Created by Thierry on 16/2/15.
//
//

#import "PhobosUtil.h"
#import <zlib.h>
#import "PhobosConfig.h"
#import <AdSupport/AdSupport.h>
#import <ifaddrs.h>
#import <arpa/inet.h>
#import <net/if.h>
#import <net/if_dl.h>
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <sys/types.h>
#import <sys/sysctl.h>
#import <mach/machine.h>
#import "sys/utsname.h"
#import "Phobos.h"
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonDigest.h>

#define IOS_CELLULAR    @"pdp_ip0"
#define IOS_WIFI        @"en0"
#define IOS_VPN         @"utun0"
#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

@implementation PhobosUtil

+ (NSData *)compressData:(NSData *)originData{
    if (!originData || [originData length] == 0)
    {
        return nil;
    }
    
    z_stream zlibStreamStruct;
    zlibStreamStruct.zalloc    = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so
    zlibStreamStruct.zfree     = Z_NULL; // that when we call deflateInit2 they will be
    zlibStreamStruct.opaque    = Z_NULL; // updated to use default allocation functions.
    zlibStreamStruct.total_out = 0; // Total number of output bytes produced so far
    zlibStreamStruct.next_in   = (Bytef*)[originData bytes]; // Pointer to input bytes
    zlibStreamStruct.avail_in  = (uInt)[originData length]; // Number of input bytes left to process
    
    int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
    if (initError != Z_OK)
    {
        NSString *errorMsg = nil;
        switch (initError)
        {
            case Z_STREAM_ERROR:
                errorMsg = @"Invalid parameter passed in to function.";
                break;
            case Z_MEM_ERROR:
                errorMsg = @"Insufficient memory.";
                break;
            case Z_VERSION_ERROR:
                errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
                break;
            default:
                errorMsg = @"Unknown error code.";
                break;
        }
        return nil;
    }
    NSMutableData *compressedData = [NSMutableData dataWithLength:[originData length] * 1.01 + 12];
    NSInteger deflateStatus;
    do
    {
        zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;
        zlibStreamStruct.avail_out = (uInt)([compressedData length] - zlibStreamStruct.total_out);
        deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);
        
    } while ( deflateStatus == Z_OK );
    
    // Check for zlib error and convert code to usable error message if appropriate
    if (deflateStatus != Z_STREAM_END)
    {
        NSString *errorMsg = nil;
        switch (deflateStatus)
        {
            case Z_ERRNO:
                errorMsg = @"Error occured while reading file.";
                break;
            case Z_STREAM_ERROR:
                errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";
                break;
            case Z_DATA_ERROR:
                errorMsg = @"The deflate data was invalid or incomplete.";
                break;
            case Z_MEM_ERROR:
                errorMsg = @"Memory could not be allocated for processing.";
                break;
            case Z_BUF_ERROR:
                errorMsg = @"Ran out of output buffer for writing compressed bytes.";
                break;
            case Z_VERSION_ERROR:
                errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
                break;
            default:
                errorMsg = @"Unknown error code.";
                break;
        }
        deflateEnd(&zlibStreamStruct);
        return nil;
    }
    deflateEnd(&zlibStreamStruct);
    [compressedData setLength: zlibStreamStruct.total_out];
    phobosLog([NSString stringWithFormat:@"Compressed file from %lu B to %lu B", [originData length], [compressedData length]]);
    return compressedData;
}

+ (void)sendData:(NSData *)data success:(SendDataSuccessBlock)success {
    @try {
         NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:Phobos.sharedClient.serverAPI]];
        [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
        request.HTTPBody = data;
        request.HTTPMethod = @"POST";
        // sendAsynchronousRequest 在iOS9以后y被废除了
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (success) {
                if (error) {
                    success(error.code);
                } else {
                    success(200);
                }
            }
        }];
        [dataTask resume];
    }
    @catch (NSException *exception) {
        phobosLog(exception);
    }
}

+ (void)sendData:(NSData *)data currentAPI:(NSString *)currentAPI success:(SendDataSuccessBlock)success {
    @try {
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:currentAPI]];
        [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
        request.HTTPBody = data;
        request.HTTPMethod = @"POST";
        // sendAsynchronousRequest 在iOS9以后y被废除了
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (success) {
                if (error) {
                    success(error.code);
                } else {
                    success(200);
                }
            }
        }];
        [dataTask resume];
    }
    @catch (NSException *exception) {
        phobosLog(exception);
    }
}

/**
 *  @brief 将对象转成JSON格式数据
 *
 *  @since 0.0.1
 */
+ (NSData *)encodeJSON:(id)obj {
    NSData *data = [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil];
    return data;
}


/**
 *  @brief 获取当前时间的秒数
 *  @since 0.0.1
 */
+ (NSString *)currentTime {
    NSDate *date = [NSDate date];
    NSTimeInterval interval = [date timeIntervalSince1970];
    NSString *timeIntervalStr = [NSString stringWithFormat:@"%ld",(long)interval];
    return timeIntervalStr;
}

/**
 获取当前时间的毫秒数

 @return v7.14.0
 */
+ (NSString *)currentMMTime {
    NSDate *date = [NSDate date];
    NSTimeInterval interval = [date timeIntervalSince1970];
    NSString *timeIntervalStr = [NSString stringWithFormat:@"%ld",(long)(interval*1000)];
    return timeIntervalStr;
}

/**
 *  @brief 获取当前APP的版本号
 *
 *  @since 0.0.1
 */
+ (NSString *)getAppVersion {
    NSString *buildVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    if (buildVersion) {
        return buildVersion;
    }else{
        return @"";
    }
}

+ (BOOL)isNonEmpty:(NSString *)string {
    if (!string) {
        return NO;
    }
    NSMutableCharacterSet *emptyStringSet = [[NSMutableCharacterSet alloc] init];
    [emptyStringSet formUnionWithCharacterSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
    [emptyStringSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @"　"]];
    if ([string length] == 0) {
        return NO;
    }
    NSString* str = [string stringByTrimmingCharactersInSet:emptyStringSet];
    return [str length] > 0;
}

+ (NSString *)deviceId {
    NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
    NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

    if (idfa != nil && ![idfa isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
        return idfa;
    }

    return idfv;
}

+ (NSString *)getIPAddress:(BOOL)preferIPv4 {
    NSArray *searchArray = preferIPv4 ?
    @[ IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
    @[ IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
    
    NSDictionary *addresses = [self getIPAddresses];

    __block NSString *address;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
     {
         address = addresses[key];
         //筛选出IP地址格式
         if([self isValidatIP:address]) *stop = YES;
     } ];
    return address ? address : @"0.0.0.0";
}

+ (BOOL)isValidatIP:(NSString *)ipAddress {
    if (ipAddress.length == 0) {
        return NO;
    }
    NSString *urlRegEx = @"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
    
    NSError *error;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:urlRegEx options:0 error:&error];
    
    if (regex != nil) {
        NSTextCheckingResult *firstMatch=[regex firstMatchInString:ipAddress options:0 range:NSMakeRange(0, [ipAddress length])];
        
        if (firstMatch) {
            return YES;
        }
    }
    return NO;
}

+ (NSDictionary *)getIPAddresses {
    NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
    // retrieve the current interfaces - returns 0 on success
    struct ifaddrs *interfaces;
    if(!getifaddrs(&interfaces)) {
        // Loop through linked list of interfaces
        struct ifaddrs *interface;
        for(interface=interfaces; interface; interface=interface->ifa_next) {
            if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
                continue; // deeply nested code harder to read
            }
            const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
            char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
            if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
                NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                NSString *type;
                if(addr->sin_family == AF_INET) {
                    if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv4;
                    }
                } else {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
                    if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv6;
                    }
                }
                if(type) {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }
        // Free memory
        freeifaddrs(interfaces);
    }
    return [addresses count] ? addresses : nil;
}

/**
 * 这里就叫做CPU指令集吧，目前主要采集这四种，armv6、armv7、armv7s、arm64
 */
+ (NSString *)currentDeviceCPUType {
    NSMutableString *cpuType = [[NSMutableString alloc] init];
    size_t size;
    cpu_type_t type;
    cpu_subtype_t subtype;
    size = sizeof(type);
    sysctlbyname("hw.cputype", &type, &size, NULL, 0);
    
    size = sizeof(subtype);
    sysctlbyname("hw.cpusubtype", &subtype, &size, NULL, 0);
    
    if (type == CPU_TYPE_X86) {
        [cpuType appendString:@"x86"];
    } else if (type == CPU_TYPE_ARM) {
        [cpuType appendString:@"ARM"];
        switch(subtype) {
            case CPU_SUBTYPE_ARM_V6:
                [cpuType appendString:@"V6"];
                break;
            case CPU_SUBTYPE_ARM_V7:
                [cpuType appendString:@"V7"];
                break;
            case CPU_SUBTYPE_ARM_V7S:
                [cpuType appendString:@"V7s"];
                break;
        }
    } else if (type == CPU_TYPE_ARM64) {
        [cpuType appendString:@"ARM64"];
    } else {
        // 暂时不采集其他的
        [cpuType appendString:@"nuknow type"];
    }
    
    return cpuType;
}

/***
 * CPU总数目
 **/
+ (NSString *)currentDeviceCPUCount {
    NSInteger count = [NSProcessInfo processInfo].activeProcessorCount;
    return [NSString stringWithFormat:@"%ld", (long)count];
}

/** 获取手机型号 */
+ (NSString *)platform {
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    return platform;
}

/***
 * 网卡地址
 **/
+ (NSString *)getMacAddress {
    int mib[6];
    size_t len;
    char *buf;
    unsigned char *ptr;
    struct if_msghdr *ifm;
    struct sockaddr_dl *sdl;
    
    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;
    
    if ((mib[5] = if_nametoindex("en0")) == 0) {
        printf("Error: if_nametoindex error/n");
        return @"";
    }
    
    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 1/n");
        return @"";
    }
    
    if ((buf = malloc(len)) == NULL) {
        printf("Could not allocate memory. error!/n");
        return @"";
    }
    
    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 2");
        return @"";
    }
    
    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    
    NSString *outstring = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);
    
    NSString *result = [outstring uppercaseString];
    return result ?: @"";
}

/**
 * 获取运营商信息
 */
+ (NSString *)getTelephonyInfo {
    CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
    CTCarrier *carrier = [info subscriberCellularProvider];
    if (carrier == nil) {
        return @"";
    }
    return [carrier carrierName] != nil ? [carrier carrierName] : @"";
}

/**
 * 内存大小，单位：兆
 */
+ (NSString *)getTotalMemorySize {
    unsigned long long resultSize = [NSProcessInfo processInfo].physicalMemory/1024/1024;
    return [NSString stringWithFormat:@"%llu", resultSize];
}

/**
 * 获取手机运行时间，从开机到现在（秒）
 */
+ (NSString *)deviceRunTime {
    struct timeval boottime;
    int mib[2] = {CTL_KERN, KERN_BOOTTIME};
    size_t size = sizeof(boottime);
    time_t now;
    time_t uptime = -1;//默认为没有使用吧
    (void)time(&now);
    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) {
        uptime = now - boottime.tv_sec;
    }
    return [NSString stringWithFormat:@"%ld", uptime];
}

+ (NSString *)MD5String:(NSString *)str
{
    if(str == nil || [str length] == 0)
        return nil;
    
    const char *value = [str UTF8String];
    
    unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5(value, (uint32_t)strlen(value), outputBuffer);
    
    NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
        [outputString appendFormat:@"%02x",outputBuffer[count]];
    }
    
    return outputString;
}

+ (NSString *)convertToJsonString:(id)object {
    NSString *jsonString = @"";
    NSError *error;
    if (!object) {
        NSAssert(0, @"convertToJsonString object 是nil");
        return @"";
    }
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:&error];
    if (!jsonData) {
        NSAssert(0, @"convertToJsonString NSJSONSerialization 出现错误");
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }

    if (jsonString == nil) {
        NSAssert(0, @"convertToJsonString jsonString 是nil");
        jsonString = @"";
    }
    return jsonString;
}
@end
