//
//  SLSDeviceUtils.m
//  AliyunLogCommon
//
//  Created by gordon on 2021/5/31.
//
#import "SLSSystemCapabilities.h"
#import "SLSDeviceUtils.h"
#import <sys/utsname.h>

#if SLS_HAS_UIKIT
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#endif

#if SLS_HAS_CORE_TELEPHONY
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
#endif

#import <sys/stat.h>
#import <dlfcn.h>
//#import "reachable/Rechable.h"
#import "SLSReachability.h"
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/machine.h>

@interface SLSDeviceUtils ()
+ (NSString *) getNetworkType;
+ (NSString *) getReachabilityStatus;
@end

@implementation SLSDeviceUtils

+ (NSString *) getDeviceModelIdentifier {
    struct utsname systemInfo;
    uname(&systemInfo);
    return [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding];
}

+ (NSString *)getDeviceModel {
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding];
    
    // iPhone
    if ([deviceModel isEqualToString:@"iPhone3,1"])    return @"iPhone 4";
    if ([deviceModel isEqualToString:@"iPhone3,2"])    return @"iPhone 4";
    if ([deviceModel isEqualToString:@"iPhone3,3"])    return @"iPhone 4";
    if ([deviceModel isEqualToString:@"iPhone4,1"])    return @"iPhone 4S";
    if ([deviceModel isEqualToString:@"iPhone5,1"])    return @"iPhone 5";
    if ([deviceModel isEqualToString:@"iPhone5,2"])    return @"iPhone 5 (GSM+CDMA)";
    if ([deviceModel isEqualToString:@"iPhone5,3"])    return @"iPhone 5c (GSM)";
    if ([deviceModel isEqualToString:@"iPhone5,4"])    return @"iPhone 5c (GSM+CDMA)";
    if ([deviceModel isEqualToString:@"iPhone6,1"])    return @"iPhone 5s (GSM)";
    if ([deviceModel isEqualToString:@"iPhone6,2"])    return @"iPhone 5s (GSM+CDMA)";
    if ([deviceModel isEqualToString:@"iPhone7,1"])    return @"iPhone 6 Plus";
    if ([deviceModel isEqualToString:@"iPhone7,2"])    return @"iPhone 6";
    if ([deviceModel isEqualToString:@"iPhone8,1"])    return @"iPhone 6s";
    if ([deviceModel isEqualToString:@"iPhone8,2"])    return @"iPhone 6s Plus";
    if ([deviceModel isEqualToString:@"iPhone8,4"])    return @"iPhone SE";
    if ([deviceModel isEqualToString:@"iPhone9,1"])    return @"iPhone 7";
    if ([deviceModel isEqualToString:@"iPhone9,2"])    return @"iPhone 7 Plus";
    if ([deviceModel isEqualToString:@"iPhone9,3"])    return @"iPhone 7";
    if ([deviceModel isEqualToString:@"iPhone9,4"])    return @"iPhone 7 Plus";
    if ([deviceModel isEqualToString:@"iPhone10,1"])   return @"iPhone_8";
    if ([deviceModel isEqualToString:@"iPhone10,4"])   return @"iPhone_8";
    if ([deviceModel isEqualToString:@"iPhone10,2"])   return @"iPhone_8_Plus";
    if ([deviceModel isEqualToString:@"iPhone10,5"])   return @"iPhone_8_Plus";
    if ([deviceModel isEqualToString:@"iPhone10,3"])   return @"iPhone X";
    if ([deviceModel isEqualToString:@"iPhone10,6"])   return @"iPhone X";
    if ([deviceModel isEqualToString:@"iPhone11,8"])   return @"iPhone XR";
    if ([deviceModel isEqualToString:@"iPhone11,2"])   return @"iPhone XS";
    if ([deviceModel isEqualToString:@"iPhone11,6"])   return @"iPhone XS Max";
    if ([deviceModel isEqualToString:@"iPhone11,4"])   return @"iPhone XS Max";
    if ([deviceModel isEqualToString:@"iPhone12,1"])   return @"iPhone 11";
    if ([deviceModel isEqualToString:@"iPhone12,3"])   return @"iPhone 11 Pro";
    if ([deviceModel isEqualToString:@"iPhone12,5"])   return @"iPhone 11 Pro Max";
    if ([deviceModel isEqualToString:@"iPhone12,8"])   return @"iPhone SE2";
    if ([deviceModel isEqualToString:@"iPhone13,1"])   return @"iPhone 12 mini";
    if ([deviceModel isEqualToString:@"iPhone13,2"])   return @"iPhone 12";
    if ([deviceModel isEqualToString:@"iPhone13,3"])   return @"iPhone 12 Pro";
    if ([deviceModel isEqualToString:@"iPhone13,4"])   return @"iPhone 12 Pro Max";
    if ([deviceModel isEqualToString:@"iPhone14,4"])   return @"iPhone 13 mini";
    if ([deviceModel isEqualToString:@"iPhone14,5"])   return @"iPhone 13";
    if ([deviceModel isEqualToString:@"iPhone14,2"])   return @"iPhone 13 Pro";
    if ([deviceModel isEqualToString:@"iPhone14,3"])   return @"iPhone 13 Pro Max";
    
    // iPod Touch
    if ([deviceModel isEqualToString:@"iPod1,1"])      return @"iPod Touch 1G";
    if ([deviceModel isEqualToString:@"iPod2,1"])      return @"iPod Touch 2G";
    if ([deviceModel isEqualToString:@"iPod3,1"])      return @"iPod Touch 3G";
    if ([deviceModel isEqualToString:@"iPod4,1"])      return @"iPod Touch 4G";
    if ([deviceModel isEqualToString:@"iPod5,1"])      return @"iPod Touch (5 Gen)";
    if ([deviceModel isEqualToString:@"iPod7,1"])      return @"iPod Touch (6 Gen)";
    
    // iPad
    if ([deviceModel isEqualToString:@"iPad1,1"])      return @"iPad";
    if ([deviceModel isEqualToString:@"iPad1,2"])      return @"iPad 3G";
    if ([deviceModel isEqualToString:@"iPad2,1"])      return @"iPad 2 (WiFi)";
    if ([deviceModel isEqualToString:@"iPad2,2"])      return @"iPad 2";
    if ([deviceModel isEqualToString:@"iPad2,3"])      return @"iPad 2 (CDMA)";
    if ([deviceModel isEqualToString:@"iPad2,4"])      return @"iPad 2";
    if ([deviceModel isEqualToString:@"iPad3,1"])      return @"iPad 3 (WiFi)";
    if ([deviceModel isEqualToString:@"iPad3,2"])      return @"iPad 3 (GSM+CDMA)";
    if ([deviceModel isEqualToString:@"iPad3,3"])      return @"iPad 3";
    if ([deviceModel isEqualToString:@"iPad3,4"])      return @"iPad 4 (WiFi)";
    if ([deviceModel isEqualToString:@"iPad3,5"])      return @"iPad 4";
    if ([deviceModel isEqualToString:@"iPad3,6"])      return @"iPad 4 (GSM+CDMA)";
    if ([deviceModel isEqualToString:@"iPad6,11"])      return @"iPad 5";
    if ([deviceModel isEqualToString:@"iPad6,12"])      return @"iPad 5";
    if ([deviceModel isEqualToString:@"iPad7,5"])      return @"iPad 6";
    if ([deviceModel isEqualToString:@"iPad7,6"])      return @"iPad 6";
    if ([deviceModel isEqualToString:@"iPad7,11"])      return @"iPad 7";
    if ([deviceModel isEqualToString:@"iPad7,12"])      return @"iPad 7";
    if ([deviceModel isEqualToString:@"iPad11,6"])      return @"iPad 8";
    if ([deviceModel isEqualToString:@"iPad11,6"])      return @"iPad 8";
    if ([deviceModel isEqualToString:@"iPad12,1"])      return @"iPad 9";
    if ([deviceModel isEqualToString:@"iPad12,2"])      return @"iPad 9";
    
    // iPad Air
    if ([deviceModel isEqualToString:@"iPad4,1"])      return @"iPad Air (WiFi)";
    if ([deviceModel isEqualToString:@"iPad4,2"])      return @"iPad Air (Cellular)";
    if ([deviceModel isEqualToString:@"iPad5,3"])      return @"iPad Air 2";
    if ([deviceModel isEqualToString:@"iPad5,4"])      return @"iPad Air 2";
    if ([deviceModel isEqualToString:@"iPad11,3"])      return @"iPad Air 3";
    if ([deviceModel isEqualToString:@"iPad11,4"])      return @"iPad Air 3";
    if ([deviceModel isEqualToString:@"iPad13,1"])      return @"iPad Air 4";
    if ([deviceModel isEqualToString:@"iPad13,2"])      return @"iPad Air 4";
    
    // iPad Mini
    if ([deviceModel isEqualToString:@"iPad2,5"])      return @"iPad Mini (WiFi)";
    if ([deviceModel isEqualToString:@"iPad2,6"])      return @"iPad Mini";
    if ([deviceModel isEqualToString:@"iPad2,7"])      return @"iPad Mini (GSM+CDMA)";
    if ([deviceModel isEqualToString:@"iPad4,4"])      return @"iPad Mini 2 (WiFi)";
    if ([deviceModel isEqualToString:@"iPad4,5"])      return @"iPad Mini 2 (Cellular)";
    if ([deviceModel isEqualToString:@"iPad4,6"])      return @"iPad Mini 2";
    if ([deviceModel isEqualToString:@"iPad4,7"])      return @"iPad Mini 3";
    if ([deviceModel isEqualToString:@"iPad4,8"])      return @"iPad Mini 3";
    if ([deviceModel isEqualToString:@"iPad4,9"])      return @"iPad Mini 3";
    if ([deviceModel isEqualToString:@"iPad5,1"])      return @"iPad Mini 4 (WiFi)";
    if ([deviceModel isEqualToString:@"iPad5,2"])      return @"iPad Mini 4 (LTE)";
    if ([deviceModel isEqualToString:@"iPad11,1"])      return @"iPad Mini 5";
    if ([deviceModel isEqualToString:@"iPad11,2"])      return @"iPad Mini 5";
    if ([deviceModel isEqualToString:@"iPad14,1"])      return @"iPad Mini 6";
    if ([deviceModel isEqualToString:@"iPad14,2"])      return @"iPad Mini 6";

    // iPad Pro
    if ([deviceModel isEqualToString:@"iPad6,3"])      return @"iPad Pro 9.7";
    if ([deviceModel isEqualToString:@"iPad6,4"])      return @"iPad Pro 9.7";
    if ([deviceModel isEqualToString:@"iPad7,3"])      return @"iPad Pro 10.5";
    if ([deviceModel isEqualToString:@"iPad7,4"])      return @"iPad Pro 10.5";
    if ([deviceModel isEqualToString:@"iPad8,1"])      return @"iPad Pro 11";
    if ([deviceModel isEqualToString:@"iPad8,2"])      return @"iPad Pro 11";
    if ([deviceModel isEqualToString:@"iPad8,3"])      return @"iPad Pro 11";
    if ([deviceModel isEqualToString:@"iPad8,4"])      return @"iPad Pro 11";
    if ([deviceModel isEqualToString:@"iPad8,9"])      return @"iPad Pro 11 2";
    if ([deviceModel isEqualToString:@"iPad8,10"])      return @"iPad Pro 11 2";
    if ([deviceModel isEqualToString:@"iPad13,4"])      return @"iPad Pro 11 3";
    if ([deviceModel isEqualToString:@"iPad13,5"])      return @"iPad Pro 11 3";
    if ([deviceModel isEqualToString:@"iPad13,6"])      return @"iPad Pro 11 3";
    if ([deviceModel isEqualToString:@"iPad13,7"])      return @"iPad Pro 11 3";
    if ([deviceModel isEqualToString:@"iPad6,7"])      return @"iPad Pro 12.9";
    if ([deviceModel isEqualToString:@"iPad6,8"])      return @"iPad Pro 12.9";
    if ([deviceModel isEqualToString:@"iPad7,1"])      return @"iPad Pro 12.9 2";
    if ([deviceModel isEqualToString:@"iPad7,2"])      return @"iPad Pro 12.9 2";
    if ([deviceModel isEqualToString:@"iPad8,5"])      return @"iPad Pro 12.9 3";
    if ([deviceModel isEqualToString:@"iPad8,6"])      return @"iPad Pro 12.9 3";
    if ([deviceModel isEqualToString:@"iPad8,7"])      return @"iPad Pro 12.9 3";
    if ([deviceModel isEqualToString:@"iPad8,8"])      return @"iPad Pro 12.9 3";
    if ([deviceModel isEqualToString:@"iPad8,11"])      return @"iPad Pro 12.9 4";
    if ([deviceModel isEqualToString:@"iPad8,12"])      return @"iPad Pro 12.9 4";
    if ([deviceModel isEqualToString:@"iPad13,8"])      return @"iPad Pro 12.9 5";
    if ([deviceModel isEqualToString:@"iPad13,9"])      return @"iPad Pro 12.9 5";
    if ([deviceModel isEqualToString:@"iPad13,10"])      return @"iPad Pro 12.9 5";
    if ([deviceModel isEqualToString:@"iPad13,11"])      return @"iPad Pro 12.9 5";
    
    // Apple TV
    if ([deviceModel isEqualToString:@"AppleTV1,1"])      return @"Apple TV 1";
    if ([deviceModel isEqualToString:@"AppleTV2,1"])      return @"Apple TV 2";
    if ([deviceModel isEqualToString:@"AppleTV3,1"])      return @"Apple TV 3";
    if ([deviceModel isEqualToString:@"AppleTV3,2"])      return @"Apple TV 3";
    if ([deviceModel isEqualToString:@"AppleTV5,3"])      return @"Apple TV 4";
    if ([deviceModel isEqualToString:@"AppleTV6,2"])      return @"Apple TV 4K";
    if ([deviceModel isEqualToString:@"AppleTV11,1"])      return @"Apple TV 4K 2";
    
    // Simulator
    if ([deviceModel isEqualToString:@"i386"])         return @"Simulator";
    if ([deviceModel isEqualToString:@"x86_64"])       return @"Simulator";
    
    return deviceModel;
}


+ (NSString *) isJailBreak
{
    //以下检测的过程是越往下，越狱越高级
    //获取越狱文件路径
    NSString *cydiaPath = @"/Applications/Cydia.app";
    NSString *aptPath = @"/private/var/lib/apt/";
    if ([[NSFileManager defaultManager] fileExistsAtPath:cydiaPath]) {
        return @"true";
    }
    if ([[NSFileManager defaultManager] fileExistsAtPath:aptPath]) {
        return @"true";
    }
    
    //可能存在hook了NSFileManager方法，此处用底层C stat去检测
    struct stat stat_info;
    if (0 == stat("/Library/MobileSubstrate/MobileSubstrate.dylib", &stat_info)) {
        return @"true";
    }
    if (0 == stat("/Applications/Cydia.app", &stat_info)) {
        return @"true";
    }
    if (0 == stat("/var/lib/cydia/", &stat_info)) {
        return @"true";
    }
    if (0 == stat("/var/cache/apt", &stat_info)) {
        return @"true";
    }
    
    //可能存在stat也被hook了，可以看stat是不是出自系统库，有没有被攻击者换掉。这种情况出现的可能性很小
    int ret;
    Dl_info dylib_info;
    int (*func_stat)(const char *,struct stat *) = stat;
    if ((ret = dladdr(func_stat, &dylib_info))) {
        //相等为0，不相等，肯定被攻击
        if (strcmp(dylib_info.dli_fname, "/usr/lib/system/libsystem_kernel.dylib")) {
            return @"true";
        }
    }
    
    //通常，越狱机的输出结果会包含字符串：Library/MobileSubstrate/MobileSubstrate.dylib。
    //攻击者给MobileSubstrate改名，原理都是通过DYLD_INSERT_LIBRARIES注入动态库。那么可以检测当前程序运行的环境变量
    char *env = getenv("DYLD_INSERT_LIBRARIES");
    if (env != NULL) {
        return @"true";
    }
    
    return @"false";
}

+ (NSString *)getResolution {
#if SLS_HAS_UIKIT
    CGSize size = [[UIScreen mainScreen] bounds].size;
    CGFloat scale = [[UIScreen mainScreen] scale];
    return [NSString stringWithFormat:@"%.0f*%.0f",size.height * scale, size.width * scale];
#else
    NSScreen *screen = [NSScreen mainScreen];
    NSDictionary *description = [screen deviceDescription];
    NSSize size = [[description objectForKey:NSDeviceSize] sizeValue];
    return [NSString stringWithFormat:@"%.0f*%.0f",size.height, size.width];
#endif
}

+ (NSString *)getCarrier {
#if SLS_HAS_CORE_TELEPHONY
#if TARGET_IPHONE_SIMULATOR
    return @"Unknown";
#endif
    @try {
        CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
        CTCarrier *carrier = nil;
        if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 12.1) {
            if ([info respondsToSelector:@selector(serviceSubscriberCellularProviders)]) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wunguarded-availability-new"
                carrier = info.serviceSubscriberCellularProviders[info.serviceSubscriberCellularProviders.allKeys.firstObject];
                if (nil != carrier && nil != carrier.mobileNetworkCode) {
                    carrier = info.serviceSubscriberCellularProviders[info.serviceSubscriberCellularProviders.allKeys.lastObject];
                }
    #pragma clang diagnostic pop
            }
        }
        if(carrier == nil) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
            carrier = info.subscriberCellularProvider;
    #pragma clang diagnostic pop
        }
        if (carrier != nil) {
            if (!carrier.isoCountryCode) {
                return @"无运营商";
            } else {
                return [carrier carrierName];
            }
        } else {
            return @"Unknown";
        }
    } @catch (NSException *exception) {
        return @"Unknown";
    }
    
#else
    return @"Unknown";
#endif
}

+ (NSString *)getReachabilityStatus {
    SLSReachability *reachability = [SLSReachability reachabilityWithHostname:@"www.aliyun.com"];
    switch ([reachability currentReachabilityStatus]) {
        case SLSNotReachable:
            return @"Unknown";
        case SLSReachableViaWiFi:
            return @"Wi-Fi";
            break;
        case SLSReachableViaWWAN:
            return @"WWAN";
            break;
        default:
            return @"";
    }
}

+ (NSString *)getNetworkType {
#if SLS_HAS_CORE_TELEPHONY
    @try {
        CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init];
        NSString *currentStatus = networkInfo.currentRadioAccessTechnology;
        return currentStatus;
    } @catch (NSException *exception) {
        return @"Unknown";
    }
#else
    return @"Unknown";
#endif
}

+ (NSString *)getNetworkTypeName {
#if SLS_HAS_CORE_TELEPHONY
    NSString *currentReachabilityStatus = [self getReachabilityStatus];
    if(![@"WWAN" isEqual:currentReachabilityStatus]) {
        return currentReachabilityStatus;
    }
    
    NSString *currentStatus = [self getNetworkType];

    if ([currentStatus isEqualToString:CTRadioAccessTechnologyLTE]) {
        return @"4G";
    }

    if (@available(iOS 14.1, *)) {
        if ([currentStatus isEqualToString:CTRadioAccessTechnologyNRNSA]
            || [currentStatus isEqualToString:CTRadioAccessTechnologyNR]) {
            return @"5G";
        }
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyWCDMA]
       || [currentStatus isEqualToString:CTRadioAccessTechnologyHSDPA]
       || [currentStatus isEqualToString:CTRadioAccessTechnologyHSUPA]
       || [currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]
       || [currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]
       || [currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]
       || [currentStatus isEqualToString:CTRadioAccessTechnologyeHRPD]) {
        return @"3G";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyGPRS]
        || [currentStatus isEqualToString:CTRadioAccessTechnologyEdge]
        || [currentStatus isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
        return @"2G";
    }
    
    return @"Unknown";
#else
    return @"Unknown";
#endif
}

+ (NSString *)getNetworkSubTypeName {
#if SLS_HAS_CORE_TELEPHONY
    NSString *currentReachabilityStatus = [self getReachabilityStatus];
    if(![@"WWAN" isEqual:currentReachabilityStatus]) {
        return @"Unknown";
    }
    
    NSString *currentStatus = [self getNetworkType];
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyGPRS]) {
        return @"GPRS";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyEdge]) {
        return @"EDGE";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyWCDMA]) {
        return @"WCDMA";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyHSDPA]) {
        return @"HSDPA";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyHSUPA]) {
        return @"HSUPA";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
        return @"CDMA1x";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]) {
        return @"EVDOv0";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]) {
        return @"EVDORevA";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]) {
        return @"EVDORevB";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyeHRPD]) {
        return @"HRPD";
    }
    
    if ([currentStatus isEqualToString:CTRadioAccessTechnologyLTE]) {
        return @"LTE";
    }
    
    if (@available(iOS 14.1, *)) {
        if ([currentStatus isEqualToString:CTRadioAccessTechnologyNRNSA]) {
            return @"NRNSA";
        } else if ([currentStatus isEqualToString:CTRadioAccessTechnologyNR]) {
            return @"NR";
        }
    }
    
    return @"Unknown";
#else
    return @"Unknown";
#endif
}

+ (NSString *) getCPUArch {
    NSMutableString *cpu = [[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_64) {
        [cpu appendString:@"x86_64"];
    } else if (type == CPU_TYPE_X86) {
        [cpu appendString:@"x86"];
    } else if (type == CPU_TYPE_ARM) {
        [cpu appendString:@"ARM"];
        switch (subtype) {
            case CPU_SUBTYPE_ARM_V6:
                [cpu appendString:@"v6"];
                break;
            case CPU_SUBTYPE_ARM_V7:
                [cpu appendString:@"v7"];
                break;
            case CPU_SUBTYPE_ARM_V8:
                [cpu appendString:@"v8"];
                break;
            default:
                break;
        }
    } else if (type == CPU_TYPE_ARM64) {
        [cpu appendString:@"ARM64"];
    }
    return [cpu copy];
}

@end
