//
//  ZZHUtils.m
//  HD wallpaper
//
//  Created by aaa on 2024/7/16.
//

#import "ZZHUtils.h"
#include <sys/sysctl.h>
#include <mach/mach_time.h>
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#import <sys/utsname.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <SystemConfiguration/CaptiveNetwork.h>
#import <netinet/in.h>

#import <UIKit/UIKit.h>
#import <sys/stat.h>
#import <AdSupport/AdSupport.h>
#import <CoreBluetooth/CoreBluetooth.h>

#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
#include <mach/mach_host.h>
#include <sys/mount.h>


#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#import "ifaddrs.h"
#import <arpa/inet.h>
#import <Network/Network.h>

#import "resolv.h"

@implementation ZZHUtils
+ (long long)collectKernelBootTimeMillis {
    struct timeval bootTime;
    int mib[2] = {CTL_KERN,KERN_BOOTTIME};
    size_t size = sizeof(bootTime);
    if (sysctl(mib, 2, &bootTime, &size, NULL, 0) != -1) {
        return bootTime.tv_sec * 1000;///秒
    };
    return 0;
    
}

+ (long long)collectMonotonicRawClockTimeMillis {
    struct timespec tp;
    if(clock_gettime(CLOCK_MONOTONIC_RAW, &tp) != -1) {
        long  xx = tp.tv_sec*1000 + tp.tv_nsec/1000000;
        return xx;
    }
    return 0;
    
}

+ (unsigned long long)collectAccessibilityFeatures {
    NSInteger features = 0;
    if (UIAccessibilityIsClosedCaptioningEnabled()) {
        features |= 0x2;
    }
    if (UIAccessibilityIsMonoAudioEnabled()) {
        features |= 0x4;
    }
    if (UIAccessibilityIsReduceMotionEnabled()) {
        features |= 0x8;
    }
    
    if (UIAccessibilityIsGuidedAccessEnabled()) {
        features |= 0x10;
    }
    
    if (UIAccessibilityIsInvertColorsEnabled()) {
        features |= 0x20;
    }
    
    if (UIAccessibilityIsVoiceOverRunning()) {
        features |= 0x40;
    }
    
    if (UIAccessibilityDarkerSystemColorsEnabled()) {
        features |= 0x80;
    }
    
    if (UIAccessibilityIsBoldTextEnabled()) {
        features |= 0x100;
    }
    
    if (UIAccessibilityIsGrayscaleEnabled()) {
        features |= 0x200;
    }
    
    if (UIAccessibilityIsReduceTransparencyEnabled()) {
        features |= 0x400;
    }
    
    if (UIAccessibilityIsSpeakScreenEnabled()) {
        features |= 0x800;
    }
    
    if (UIAccessibilityIsSpeakSelectionEnabled()) {
        features |= 0x1000;
    }
    
    if (UIAccessibilityIsSwitchControlRunning()) {
        features |= 0x2000;
    }
    
    if (UIAccessibilityIsShakeToUndoEnabled()) {
        features |= 0x4000;
    }
    
    if (UIAccessibilityIsAssistiveTouchRunning()) {
        features |= 0x8000;
    }
    
    if (UIAccessibilityHearingDevicePairedEar() != 0) {
        features |= 0x400000;
    }
    
    if (@available(iOS 13.0, *)) {
        if ([UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark) {
            features |= 0x10000;
        }
        
        if (UIAccessibilityIsOnOffSwitchLabelsEnabled()) {
            features |= 0x20000;
        }
        
        if (UIAccessibilityIsVideoAutoplayEnabled()) {
            features |= 0x40000;
        }
        
        if (UIAccessibilityShouldDifferentiateWithoutColor()) {
            features |= 0x80000;
        }
        
        if (@available(iOS 14.0, *)) {
            if (UIAccessibilityPrefersCrossFadeTransitions()) {
                features |= 0x100000;
            }
            
            if (UIAccessibilityButtonShapesEnabled()) {
                features |= 0x200000;
            }
            
        }
    }
    NSLog(@"features:\n%ld",features);
    
    return features;
}

+ (NSString *)collectDeviceKeyboards{
    NSArray *lanagues = [NSLocale preferredLanguages];
    __block NSString *str = @"";
    [lanagues enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (idx < lanagues.count-1) {
            str = [str stringByAppendingFormat:@"%@,",obj];
        }
        else {
            str = [str stringByAppendingFormat:@"%@",obj];
        }
    }];
    return str;
}

+ (void)userAgent {
    static WKWebView *webView = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        webView = [WKWebView new];
    });
    [webView evaluateJavaScript:@"function getAgent() {return navigator.userAgent;} getAgent(); " completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"zzh the useragent:%@ ",result);
    }];
    
    //    NSString *geoStr = @"if (\"geolocation\" in navigator) { navigator.geolocation.getCurrentPosition(function(position) { console.log(\"Latitude is :\", position.coords.latitude);console.log(\"Longitude is :\", position.coords.longitude);}, function(error) {console.error(\"Error Code = \" + error.code + \" - \" + error.message);});";
    //
    //    [webView evaluateJavaScript:geoStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
    //        NSLog(@"zzh the geo err:%@ ",error);
    //    }];
}

+ (BOOL)isReachableViaWWAN {
    SCNetworkReachabilityFlags flags;
    struct sockaddr_in zeroAddress;
   bzero(&zeroAddress, sizeof(zeroAddress));
   zeroAddress.sin_len = sizeof(zeroAddress);
   zeroAddress.sin_family = AF_INET;
    
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
    
    if (SCNetworkReachabilityGetFlags(reachability, &flags)) {
        if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
            return YES;
        }
    }
    
    return NO;
}

+ (BOOL)isReachableViaWiFi {
    SCNetworkReachabilityFlags flags;
    BOOL result = NO;
    
    struct sockaddr_in zeroAddress;
   bzero(&zeroAddress, sizeof(zeroAddress));
   zeroAddress.sin_len = sizeof(zeroAddress);
   zeroAddress.sin_family = AF_INET;
    
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
    
    if (SCNetworkReachabilityGetFlags(reachability, &flags)) {
        BOOL isReachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
        BOOL needsConnection = (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0;
        BOOL isWiFi = (flags & kSCNetworkReachabilityFlagsIsWWAN) == 0;
        
        result = isReachable && !needsConnection && isWiFi;
    }
    NSLog(@"result:%d",result);
    return result;
}


+(BOOL)isReachable {
    // 获取 SCNetworkReachabilityRef 对象
    // 注意：这里直接从内存地址获取的方式在实际开发中是不安全的，
    // 应该通过正确的途径获取该对象，例如使用 SCNetworkReachabilityCreateWithAddress 或 SCNetworkReachabilityCreateWithName
    struct sockaddr_in zeroAddress;
   bzero(&zeroAddress, sizeof(zeroAddress));
   zeroAddress.sin_len = sizeof(zeroAddress);
   zeroAddress.sin_family = AF_INET;
    
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);

    // 获取网络状态标志
    SCNetworkReachabilityFlags flags;
    BOOL success = SCNetworkReachabilityGetFlags(reachability, &flags);
    if (!success) {
        return NO;
    }

    // 判断网络是否可达，并调用 ALReachability 的方法
    return [ZZHUtils isReachableWithFlags:flags];
}

+ (int)isReachableWithFlags:(int)arg2 {
    int result = 5;
    
    if ((result & ~arg2) != 0) {
        result = (result != 0) ? 1 : 5;
    }
    
    return result & ((arg2 & 2) >> 1);
}


+ (id)collectTimeZoneOffset {
    NSTimeZone *tz = [NSTimeZone localTimeZone];
    NSInteger sec = [tz secondsFromGMT];
    double hoursFromGMT = (double)sec / 3600.0;
    return @(hoursFromGMT);
}

//获取电池数据
+ (NSDictionary *)collectBatteryInfoIfNeeded {
    NSMutableDictionary *dictionary = [NSMutableDictionary new];
    UIDevice *device = [UIDevice currentDevice];
    BOOL originalBatteryMonitoringEnabled = device.isBatteryMonitoringEnabled;
    
    [device setBatteryMonitoringEnabled:YES];
    
    UIDeviceBatteryState batteryState = device.batteryState;
    NSNumber *batteryStateNumber = [NSNumber numberWithInteger:batteryState];
    [dictionary setObject:batteryStateNumber forKey:@"ict"];
    
    float batteryLevel = device.batteryLevel;
    int batteryPercentage = (int)(batteryLevel * 100);
    if (batteryPercentage > 0) {
        batteryPercentage = batteryPercentage ^ 0x10101010;
        NSNumber *batteryLevelNumber = @(batteryPercentage);
        [dictionary setObject:batteryLevelNumber forKey:@"icm"];
    }
    else {
        [dictionary setObject:@"-1" forKey:@"icm"];
    }
    
    [device setBatteryMonitoringEnabled:originalBatteryMonitoringEnabled];
    return dictionary;
}

+ (NSString *)collectModelRevision {
    struct utsname systemInfo;
    if (uname(&systemInfo) == 0) {
        NSString *modelString = [NSString stringWithUTF8String:systemInfo.machine];
        return modelString;
    }
    return nil;
}

+ (CGFloat)getScreenBrightness {
    return [UIScreen mainScreen].brightness;
}

+ (CBManagerState)bluetoothState {
    CBCentralManager *manager = [[CBCentralManager alloc] init];
    return  manager.state;
}

//设备型号
+ (NSDictionary *)collectModel {
    NSMutableDictionary *arg2 = [NSMutableDictionary new];
    UIDevice *device = [UIDevice currentDevice];
    NSString *model = [device model];
    [arg2 setObject:model forKeyedSubscript:@"model"];
    return arg2;
}

+ (NSString *)collectSubplatform {
    if (@available(iOS 14.0, *)) {
        if ([[NSProcessInfo processInfo] isiOSAppOnMac]) {
            return @"ios_on_mac";
        }
    }
    
    if (@available(iOS 13.0, *)) {
        if ([[NSProcessInfo processInfo] isMacCatalystApp]) {
            return @"mac_catalyst";
        }
    }
    
    return @"ios";
}


+ (id)collectOrientationLock {
    //    [[[[UIApplication sharedApplication] windows].firstObject windowScene] interfaceOrientation]
    return nil;
}

+ (NSMutableDictionary *)collectScreenDimensions{
    NSMutableDictionary *dict = [NSMutableDictionary new];
    UIScreen *mainScreen = [UIScreen mainScreen];
    CGRect bounds = [mainScreen bounds];
    CGRect nativeBounds = [mainScreen nativeBounds];
    CGFloat scale = [mainScreen scale];
    
    [dict setObject:@(bounds.size.width) forKeyedSubscript:@"dx"];
    [dict setObject:@(bounds.size.height) forKeyedSubscript:@"dy"];
    [dict setObject:@(nativeBounds.size.width) forKeyedSubscript:@"ndx"];
    [dict setObject:@(nativeBounds.size.height) forKeyedSubscript:@"ndy"];
    [dict setObject:@(scale) forKeyedSubscript:@"scale"];
    
    return dict;
}

+ (NSString *)systemVersion {
    UIDevice *device = [UIDevice currentDevice];
    NSString *version = [device systemVersion];
    return version;
}

+ (NSMutableDictionary *)collectLocale{
    
    NSMutableDictionary *arg2 = [NSMutableDictionary new];
    NSLocale *currentLocale = [NSLocale currentLocale];
    NSString *localeIdentifier = [currentLocale localeIdentifier];
    [arg2 setObject:localeIdentifier forKeyedSubscript:@"locale"];
    
    return arg2;
}

+ (NSNumber *)volume {
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    float outputVolume = [audioSession outputVolume];
    NSNumber *volumeNumber = [NSNumber numberWithFloat:outputVolume];
    return volumeNumber;
}

+ (void)appInfoFromBundle {
    NSBundle *bundle = [NSBundle mainBundle];
    
    NSArray *arr = [NSBundle allBundles];
    NSArray *frameworks = [NSBundle allFrameworks];
    NSURL *appStoreReceiptURL = [bundle appStoreReceiptURL];
    NSLog(@"the bundle:%@",bundle);
}

+ (BOOL)isVPNConnected {
    CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
    if (proxySettings == NULL) {
        return NO;
    }
    
    NSDictionary *settings = (__bridge_transfer NSDictionary *)proxySettings;
    NSDictionary *scoped = settings[@"__SCOPED__"];
    
    if (!scoped) {
        return NO;
    }
    
    NSArray *allKeys = [scoped allKeys];
    __block BOOL rr = NO;
    [allKeys enumerateObjectsUsingBlock:^(NSString *  _Nonnull interface, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([interface containsString:@"tun"] || [interface containsString:@"ppp"]) {
                rr = YES;
            } else {
                rr = [interface containsString:@"ipsec"];
            }
    }];
    
    return rr;
}



+ (SystemSoundID)muteSoundID {
    SystemSoundID _muteSoundID = 0;
    NSURL *soundURL = [[NSBundle mainBundle] URLForResource:@"mute" withExtension:@"aiff"];
    if (soundURL) {
        OSStatus status = AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundURL, &_muteSoundID);
        if (status == noErr) {
            // 设置声音为静音
            UInt32 yes = 1;
            AudioServicesSetProperty(kAudioServicesPropertyIsUISound,
                                     sizeof(_muteSoundID),
                                     &_muteSoundID,
                                     sizeof(yes),
                                     &yes);
        }
    }
    return _muteSoundID;
}




+ (NSString *)getIDFA {
    if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) {
        NSString *idfa = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
        return  idfa;
   } else {
       return @"";
   }
}

+ (NSString *)getIDFV {
    NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    return idfv;
}


#pragma mark - 获取网络运营商
+ (NSString *)getCarrierName{
    CTTelephonyNetworkInfo *telephonyInfo = [[CTTelephonyNetworkInfo alloc] init];
    CTCarrier *carrier = [telephonyInfo subscriberCellularProvider];
    NSString *currentCountry=[carrier carrierName];
    return currentCountry;
}




#pragma mark - 获取当前可用内存

/**
 获取当前可用内存

 @return 当前可用内存大小
 */
+(long long)getAvailableMemorySize
{
    vm_statistics_data_t vmStats;
    mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;
    kern_return_t kernReturn = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount);
    if (kernReturn != KERN_SUCCESS)
    {
        return NSNotFound;
    }
    return ((vm_page_size * vmStats.free_count + vm_page_size * vmStats.inactive_count));
}


#pragma mark - 获取磁盘容量

/**
 获取磁盘容量

 @return 磁盘容量
 */
+(long long)getTotalDiskSize{
    struct statfs buf;
    unsigned long long freeSpace = -1;
    if (statfs("/var", &buf) >= 0)
    {
        freeSpace = (unsigned long long)(buf.f_bsize * buf.f_blocks);
    }
    return freeSpace;
}

#pragma mark - 获取可用磁盘容量

/**
 获取可用磁盘容量

 @return 可用磁盘容量
 */
+(long long)getAvailableDiskSize{
    struct statfs buf;
    unsigned long long freeSpace = -1;
    if (statfs("/var", &buf) >= 0)
    {
        freeSpace = (unsigned long long)(buf.f_bsize * buf.f_bavail);
    }
    return freeSpace;
}

/// 获取本机DNS服务器
/// libresolv.tbd需要添加这个库
+ (NSArray *)outPutDNSServers {
    res_state res = malloc( sizeof(struct __res_state) );
    NSMutableArray *dnsArray = [NSMutableArray new];
    if (res_ninit(res) == 0) {
        for (int i = 0; i < res->nscount; i++ ) {
            NSString *s = [NSString stringWithUTF8String:inet_ntoa(res->nsaddr_list[i].sin_addr)];
            [dnsArray addObject:s];
        }
    }
    res_ndestroy(res);//！！改了这里
    free(res);
    return dnsArray.mutableCopy;
}

//是否有刘海屏
+ (BOOL)hasNotch {
    return [UIScreen mainScreen].nativeBounds.size.height != UIScreen.mainScreen.bounds.size.height;
}

//获取系统字号
+ (NSNumber *)getSystemFontSize {
    UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
    UIFontDescriptor *ctFont = font.fontDescriptor;
    NSNumber *fontSize = [ctFont objectForKey:@"NSFontSizeAttribute"];
    return fontSize;
}

//获取processInfo
+ (NSDictionary *)processInfo {
    NSProcessInfo *processInfo = [NSProcessInfo processInfo];
    return processInfo.environment;
}


#define IOS_CELLULAR    @"pdp_ip0"//有些分配的地址为en0 有些分配的en1
#define IOS_WIFI2       @"en2"
#define IOS_WIFI1       @"en1"
#define IOS_WIFI        @"en0"//
#define IOS_VPN       @"utun0"  vpn很少用到可以注释
#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

//获取所有相关IP信息
+ (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;
}

#warning -----暂时还未去成功获取
+ (NSDictionary *)wifiInfo {
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    NSLog(@"interfaces:%@",ifs);
    NSDictionary *info = nil;
    for (NSString *ifname in ifs) {
        info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifname);
        NSLog(@"wifiInfo %@ => %@",ifname,info);
    }
    return info;
}





#warning -----暂时还未去处理的伪代码
//- (NSDictionary *)collectAdServerInfo {
//    NSMutableDictionary *result = [NSMutableDictionary dictionary];
//    
//    // Server Cache
//    [result setObject:[self.sdk stringForKey:[ALSetting serverCache]] forKeyedSubscript:@"sc"];
//    
//    // Server Cache 2
//    [result setObject:[self.sdk stringForKey:[ALSetting serverCache2]] forKeyedSubscript:@"sc2"];
//    
//    // Server Cache 3
//    [result setObject:[self.sdk stringForKey:[ALSetting serverCache3]] forKeyedSubscript:@"sc3"];
//    
//    // Server Installed At
//    [result setObject:[self.sdk stringForKey:[ALSetting serverInstalledAt]] forKeyedSubscript:@"server_installed_at"];
//    
//    // Server Installed At V2
//    [result setObject:[self.sdk stringForKey:[ALSetting serverInstalledAtV2]] forKeyedSubscript:@"server_installed_at_v2"];
//    
//    // Persisted Data
//    NSString *persistedData = [[self.sdk userDefaults] stringForKey:[ALUserDefaultsKey persistedData]];
//    [result al_setStringValueIfValid:persistedData forKey:@"persisted_data"];
//    
//    return [result copy];
//}

#warning  ----还未检测到该方法的调用...
+ (NSString *)collectCurrentRadioAccessTechnologyIfNeeded{
    return @"";
}


#pragma mark ----检测是否越狱

+ (BOOL)isJailbroken {
    if ([self checkCommonJailbreakFiles] || [self checkSuspiciousApps] || [self canWriteToSystemDirectories]) {
        return YES;
    }
    
    return NO;
}

+ (BOOL)checkCommonJailbreakFiles {
    NSArray *jailbreakFiles = @[
        @"/Applications/Cydia.app",
        @"/Library/MobileSubstrate/MobileSubstrate.dylib",
        @"/bin/bash",
        @"/usr/sbin/sshd",
        @"/etc/apt",
        @"/private/var/lib/apt/"
    ];
    
    for (NSString *path in jailbreakFiles) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
            return YES;
        }
    }
    
    return NO;
}

+ (BOOL)checkSuspiciousApps {
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]) {
        return YES;
    }
    
    return NO;
}

+ (BOOL)canWriteToSystemDirectories {
    NSError *error;
    NSString *stringToWrite = @"Jailbreak Test";
    NSString *filePath = @"/private/jailbreakTest.txt";
    
    [stringToWrite writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
    
    if (error == nil) {
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
        return YES;
    }
    
    return NO;
}


@end
