//
//  RCPlayerView.m
//  Demo
//
//  Created by 余衡武 on 2024/10/6.
//

#import "RCPlayerView.h"
#import "UIKit/UIKit.h"
#import "RCTextField.h"
#import "RCDataBuffer.h"
#import "RCWSClient.h"
#import "RCWSOrder.h"
#import "RCTool.h"
#import "RCOperateManager.h"

#import <WebRTC/AMediaStream.h>
#import <WebRTC/RTCMacros.h>
#import <WebRTC/RTCIceCandidate.h>

#import <ifaddrs.h>
#import <arpa/inet.h>

#import <CoreMotion/CoreMotion.h>
#import <CoreLocation/CoreLocation.h>

#define SCREEN_W     [UIScreen mainScreen].bounds.size.width
#define SCREEN_H     [UIScreen mainScreen].bounds.size.height


@interface RCPlayerView () <MediaStreamClientEventsDelegate, MediaStreamClientEventsDelegateEx, RCTextFieldDelegate, UITextFieldDelegate, CLLocationManagerDelegate>

#pragma mark - 属性
@property (nonatomic, strong) AMediaStream *mediaStream;


#pragma mark - 音视频参数
/// 信令服务器地址l
@property (nonatomic, strong, readwrite) NSURL         *url;
/// 中转服务器地址
@property (nonatomic, strong, readwrite) NSString      *ice;
/// 卡序列号
@property (nonatomic, strong, readwrite) NSString      *sn;
/// 0正常拉流，1不拉流只用于数据传输
@property (nonatomic, assign, readwrite) NSInteger     direct;
/// 视频格式 ：1是264。 5是265
@property (nonatomic, assign, readwrite) NSInteger     fmt;
/// 编码方式 ：yes是硬编码。 no是软编码
@property (nonatomic, assign, readwrite) NSInteger     isHWEncode;
/// 视频分辨率 宽
@property (nonatomic, assign, readwrite) NSInteger     videoWidth;
/// 视频分辨率 高
@property (nonatomic, assign, readwrite) NSInteger     videoHeight;
/// 帧率
@property (nonatomic, assign, readwrite) NSInteger     fps;
/// 码率
@property (nonatomic, assign, readwrite) NSInteger     bitrate;
/// 卡分辨率 宽
@property (nonatomic, assign, readwrite) NSInteger     cardWidth;
/// 卡分辨率 高
@property (nonatomic, assign, readwrite) NSInteger     cardHeight;
/// DPI
@property (nonatomic, assign, readwrite) NSInteger     cardDensity;
/// 推流Token
@property (nonatomic, strong, readwrite) NSString      *token;

#pragma mark 已设置 未重连 未生效
/// 预设-编码方式 ：yes是硬编码。 no是软编码
@property (nonatomic, assign, readwrite) NSInteger     isHWEncodePre;
/// 预设-视频格式 ：1是264、 2是vp8、 5是265
@property (nonatomic, assign, readwrite) NSInteger     fmtPre;
/// 预设-帧率
@property (nonatomic, assign) NSInteger     fpsPre;
/// 预设-分辨率 宽
@property (nonatomic, assign) NSInteger     videoWidthPre;
/// 预设-分辨率 高
@property (nonatomic, assign) NSInteger     videoHeightPre;

/// 真机键盘 透明输入框
@property (nonatomic, strong) RCTextField              *textField;
/// 键盘输入方式  1、真机  2、云手机
@property (nonatomic, assign) int                      inputMode;

/// 实时数据流
@property (nonatomic, assign) CGFloat                  lastPacketsLost;
@property (nonatomic, assign) CGFloat                  lastPacketsReceived;
@property (nonatomic, assign) NSInteger                lastBytesReceived;
@property (nonatomic, assign) CGFloat                  lastReportTime;
@property (nonatomic, strong) UILabel                  *infoLabel;
@property (nonatomic, strong) UIView                   *bgView;

/// WS
@property (nonatomic, strong) RCWSClient                        *client;

#pragma mark - 感应器数据透传

@property (nonatomic, assign) BOOL   isSensor;

@property (nonatomic, strong) CMMotionManager   *motionManager;
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CMAltimeter       *altimeter;

/// 重力感应器/线性加速度/旋转矢量/方向传感器
@property (nonatomic, assign) BOOL   isGravity;
@property (nonatomic, assign) double gravityX;
@property (nonatomic, assign) double gravityY;
@property (nonatomic, assign) double gravityZ;
@property (nonatomic, assign) double lineX;
@property (nonatomic, assign) double lineY;
@property (nonatomic, assign) double lineZ;
@property (nonatomic, assign) double quaternionX;
@property (nonatomic, assign) double quaternionY;
@property (nonatomic, assign) double quaternionZ;
@property (nonatomic, assign) double yaw;
@property (nonatomic, assign) double pitch;
@property (nonatomic, assign) double roll;

/// 加速度计感应器
@property (nonatomic, assign) BOOL   isAccelerate;
@property (nonatomic, assign) double accelerateX;
@property (nonatomic, assign) double accelerateY;
@property (nonatomic, assign) double accelerateZ;

/// 磁力计感应器
@property (nonatomic, assign) BOOL   isMagnet;
@property (nonatomic, assign) double magnetX;
@property (nonatomic, assign) double magnetY;
@property (nonatomic, assign) double magnetZ;

/// 陀螺仪感应器
@property (nonatomic, assign) BOOL   isGyro;
@property (nonatomic, assign) double gyroX;
@property (nonatomic, assign) double gyroY;
@property (nonatomic, assign) double gyroZ;

/// GPS感应器
@property (nonatomic, assign) BOOL   isLocation;
@property (nonatomic, assign) double longitude;
@property (nonatomic, assign) double latitude;

/// 气压计感应器
@property (nonatomic, assign) BOOL   isPressure;
@property (nonatomic, assign) double pressure;

/// 距离感应器
@property (nonatomic, assign) BOOL   isDistance;


@end

@implementation RCPlayerView



#pragma mark - 初始化
/// 初始化
+(void)globalInitialization {
    [AMediaStream globalInitialization];
}

/// 反初始化
+(void)globalDeinitialization {
    [AMediaStream globalDeinitialization];
}


#pragma mark - 直接出图
- (instancetype)initForUrl:(NSURL *)url
                       ice:(NSString *)ice
                        sn:(NSString *)sn
                    direct:(NSInteger)direct
                       fmt:(NSInteger)fmt
                videoWidth:(NSInteger)videoWidth
               videoHeight:(NSInteger)videoHeight
                       fps:(NSInteger)fps
                   bitrate:(NSInteger)bitrate
                 cardWidth:(NSInteger)cardWidth
                cardHeight:(NSInteger)cardHeight
               cardDensity:(NSInteger)cardDensity
                     token:(NSString*)token
                  delegate:(id<RCPlayerViewDelegate>)delegate {
    if (self = [super initWithFrame:CGRectZero]) {
        [self initSensor]; // 初始化传感器
        
        // 1、创建AMediaStream
        self.mediaStream.webRTCPlayerView = [[WebRTCPlayerView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
        self.mediaStream.eventDelegate = self;
        self.mediaStream.eventDelegateEx = self;
        [self addSubview:self.mediaStream.webRTCPlayerView];
        [self setPrelinkEnable:NO];
        
        // 默认设置 拉流之前设置
        self.isHWEncode = YES; // 默认硬编码
        [self.mediaStream setVideoCodecHwAcceleration:self.isHWEncode];
        
        
        // 2、开始拉流
        [self.mediaStream start:url 
                            ice:ice
                             sn:sn
                         direct:direct
                            fmt:fmt
                     videoWidth:videoWidth
                    videoHeight:videoHeight
                            fps:fps
                        bitrate:bitrate
                      cardWidth:0
                     cardHeight:0
                    cardDensity:0
                          token:token];
        [self.mediaStream setShouldGetStats:YES];
        
        // 3、保存参数
        self.url = url;
        self.ice = ice;
        self.sn = sn;
        self.direct = direct;
        self.fmt = fmt;
        self.videoWidth = videoWidth;
        self.videoWidthPre = self.videoWidth;
        self.videoHeight = videoHeight;
        self.videoHeightPre = self.videoHeight;
        self.fps = fps;
        self.fpsPre = self.fps;
        self.bitrate = bitrate;
        self.cardWidth = 0;
        self.cardHeight = 0;
        self.cardDensity = 0;
        self.token = token;
        self.delegate = delegate;
        self.backgroundColor = [UIColor blackColor];
        self.isHWEncodePre = YES;
        self.fmtPre =  fmt;
        
        [self drawView];
        
        
        // 默认设置 拉流之后设置
        [self setAudioEnable:YES]; // 默认打开音频开关
    }
    return self;
}

#pragma mark - 预连接 后出图
/// start 之前调用是否启用预连接
- (void)setPrelinkEnable:(BOOL)enable {
    [self.mediaStream setPrelinkEnable:enable];
}

/// 初始化预连接对象
- (instancetype)initForUrlPreLinkFun {
    if (self = [super initWithFrame:CGRectZero]) {
        [self initSensor]; // 初始化传感器

        // 1、创建AMediaStream
        self.mediaStream.webRTCPlayerView = [[WebRTCPlayerView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
        self.mediaStream.eventDelegate = self;
        self.mediaStream.eventDelegateEx = self;
        [self addSubview:self.mediaStream.webRTCPlayerView];

        // 默认设置 拉流之前设置
        self.isHWEncode = YES; // 默认硬编码
        [self.mediaStream setVideoCodecHwAcceleration:self.isHWEncode];
        
        [self drawView];

    }
    return self;
}

- (void)startPreLinkForUrl:(NSURL *)url
                       ice:(NSString *)ice
                        sn:(NSString *)sn
                    direct:(NSInteger)direct
                       fmt:(NSInteger)fmt
                videoWidth:(NSInteger)videoWidth
               videoHeight:(NSInteger)videoHeight
                       fps:(NSInteger)fps
                   bitrate:(NSInteger)bitrate
                 cardWidth:(NSInteger)cardWidth
                cardHeight:(NSInteger)cardHeight
               cardDensity:(NSInteger)cardDensity
                     token:(NSString*)token {
    
    [self.mediaStream setPrelinkEnable:YES];
    
    // 2、开始拉流
    [self.mediaStream start:url
                        ice:ice
                         sn:sn
                     direct:direct
                        fmt:fmt
                 videoWidth:videoWidth
                videoHeight:videoHeight
                        fps:fps
                    bitrate:bitrate
                  cardWidth:0
                 cardHeight:0
                cardDensity:0
                      token:token];
    [self.mediaStream setShouldGetStats:YES];

    // 3、保存参数
    self.url = url;
    self.ice = ice;
    self.sn = sn;
    self.direct = direct;
    self.fmt = fmt;
    self.videoWidth = videoWidth;
    self.videoWidthPre = self.videoWidth;
    self.videoHeight = videoHeight;
    self.videoHeightPre = self.videoHeight;
    self.fps = fps;
    self.fpsPre = self.fps;
    self.bitrate = bitrate;
    self.cardWidth = 0;
    self.cardHeight = 0;
    self.cardDensity = 0;
    self.token = token;
    
    self.backgroundColor = [UIColor blackColor];
    self.isHWEncodePre = YES;
    self.fmtPre =  fmt;

    // 默认设置 拉流之后设置
    [self setAudioEnable:YES]; // 默认打开音频开关

}

/// 拉流预连接播放
- (BOOL)setPrelinkPlay {
    return [self.mediaStream setPrelinkPlay];
}

/// 断开预连接
- (void)setPrelinkDisconnect {
   [self.mediaStream disconnect];
}

- (void)enterBackground {
    [self.mediaStream enterBackground];
}

- (void)enterForeground {
    [self.mediaStream enterForeground];
}

#pragma mark - 生命周期
- (void)layoutSubviews {
    [super layoutSubviews];
    
    self.mediaStream.webRTCPlayerView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
    self.bgView.frame = CGRectMake(0, 0, 150, 130);
    self.infoLabel.frame = CGRectMake(10, 20, 150, 130);
}

- (void)drawView {
    self.isShowLogInfo = NO;
    self.isCallBackLogInfo = NO;
    
    RCTextField *textField = [[RCTextField alloc] init];
    self.textField = textField;
    textField.rcdelegate = self;
    textField.delegate = self;
    [textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
    textField.frame = CGRectMake(-50, -50, 50, 50); // 设置文本框的位置和大小
    textField.hidden = YES;
    [self addSubview:textField];
    [self sendSubviewToBack:textField];
    
    UIView *bgView = [[UIView alloc] init];
    self.bgView = bgView;
    bgView.hidden = YES; // 默认隐藏
    bgView.userInteractionEnabled = NO;
    bgView.backgroundColor = [RCTool hwColor:@"#000000" alpha:0.5];
    [self addSubview:bgView];
    
    UILabel *infoLabel = [[UILabel alloc] init];
    self.infoLabel = infoLabel;
    infoLabel.textColor = [UIColor whiteColor];
    infoLabel.font = [UIFont systemFontOfSize:14];
    infoLabel.numberOfLines = 0;
    infoLabel.textAlignment = NSTextAlignmentLeft;
    [bgView addSubview:infoLabel];
}

#pragma mark - 真机键盘 位置适配
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self];
    self.textField.center = location;
}

#pragma mark - 操作方法
/**
 1、重连后才会生效：视频格式-fmt、帧率-fps、编码方式-isHWEncode（重连生效处理-内部）
 2、重连后保持效果：画质、分辨率
 3、重连后默认效果：云机键盘-关闭、暂停/恢复连接-默认 （重连默认处理-外面）、音频开关-默认打开（重连默认处理-外面）
 */
/// 重新连接 - 拉流
- (void)reConnectForUrl:(NSURL *)url
                    ice:(NSString *)ice
                     sn:(NSString *)sn
                  token:(NSString*)token {
    self.url = url;
    self.ice = ice;
    self.sn = sn;
    self.token = token;
    
    // 默认设置 拉流之前设置
    self.isHWEncode = self.isHWEncodePre;
    [self.mediaStream setVideoCodecHwAcceleration:self.isHWEncode];
    self.fmt = self.fmtPre;
    
    // 重新拉流
    [self.mediaStream start:self.url
                        ice:self.ice
                         sn:self.sn
                     direct:self.direct
                        fmt:self.fmt
                 videoWidth:self.videoWidth
                videoHeight:self.videoHeight
                        fps:self.fps
                    bitrate:self.bitrate
                  cardWidth:0
                 cardHeight:0
                cardDensity:0
                      token:self.token];

}

/// 主动断开连接
- (void)disConnect {
    [self.mediaStream setShouldGetStats:NO];
    [self.mediaStream disconnect];
    self.mediaStream = nil;
    if (self.isSensor) { self.isSensor = NO;}
}

/// 暂停接收流
- (BOOL)pauseStream {
    return [self.mediaStream pauseStream];
}

/// 恢复接收流
- (BOOL)resumeStream {
    return [self.mediaStream resumeStream];
}

/// 音频开关
- (BOOL)setAudioEnable:(BOOL)enable {
    return [self.mediaStream setAudioEnable:enable];
}

/// 自动码率和手动码率 （自动码率传支持的最大bitrate）
- (BOOL)setAutoBitrateEnabled:(BOOL)enable bitrate:(NSNumber*)bitrate {
    return [self.mediaStream setAutoBitrateEnabled:enable bitrate:bitrate];
}

/// 设置渲染视频填充模式
- (void)setVideoContentMode:(UIViewContentMode)mode {
    [self.mediaStream.webRTCPlayerView setVideoContentMode:mode];
}

/// 设置是否回调流信息状态
- (void)setShouldGetStats:(BOOL)shouldGetStats {
    [self.mediaStream setShouldGetStats:shouldGetStats];
}

/// 设置输入方式  1、真机  2、云手机
- (void)setKeyboardMode:(int)mode {
    if (mode == 2) {
        self.inputMode = 2;
    } else {
        self.inputMode = 1;
    }
}

/// 订阅使用相机麦克风。拉流成功，datachannel 连接成功后才能调用
- (BOOL)subscribeVirtualMicrophoneCamera:(BOOL)microphone camera:(BOOL)camera userData:(NSString *)userData {
    return [self.mediaStream subscribeVirtualMicrophoneCamera:microphone camera:camera userData:userData];
}

/// 发送自定义消息。拉流成功，datachannel 连接成功后才能调用
- (BOOL)pushCustomMessages:(NSString *)pusher userData:(NSString *)userData {
    return [self.mediaStream pushCustomMessages:pusher userData:userData];
}

/// 设置自动码率调整区间。拉流成功，datachannel 连接成功后才能调用
- (BOOL)setAutoBitrateRange:(int)minBitrateKbps maxBitrateKbps:(int)maxBitrateKbps {
    return [self.mediaStream setAutoBitrateRange:minBitrateKbps maxBitrateKbps:maxBitrateKbps];
}

#pragma mark - 控制面板
/// 设置视频类型 1是H264。2是vp8  5是H265（需要重新拉流）
- (void)setVideoType:(int)fmt {
    // 重连后生效
    self.fmtPre = fmt;
}

// 拉流前调用设置软硬编码，YES硬编码，NO软编码，不调用默认硬编码（需要重新拉流）
-(void)setVideoCodecHwAcceleration:(BOOL)videoCodecHwAcceleration {
    // 重连后生效
    self.isHWEncodePre = videoCodecHwAcceleration;
}

/// 拉流过程中改变视频分辨率
- (BOOL)setVideoSize:(NSInteger)videoWidth videoHeight:(NSInteger)videoHeight {
    self.videoWidthPre = videoWidth;
    self.videoHeightPre = videoHeight;
    return [self.mediaStream setVideoSize:videoWidth videoHeight:videoHeight frameRate:0];
}

/// 拉流过程中更改卡分辨率
- (void)setCardSize:(NSInteger)cardWidth cardHeight:(NSInteger)cardHeight cardDensity:(NSInteger)cardDensity {
    [self.mediaStream setCardSize:cardWidth cardHeight:cardHeight cardDensity:cardDensity];
}

/// 拉流过程中改变HD
- (BOOL)setHDVaule:(int)value {
    NSNumber *rate = [NSNumber numberWithInt:value];
    BOOL isSuccess = [self.mediaStream setMaxBitrate:rate];
    if (isSuccess) { self.bitrate = value; }
//    NSLog(@"设置画质 val：%d res：%d", value, isSuccess);
    return isSuccess;
}

/// 设置帧率
- (BOOL)setMaxFps:(NSInteger)fps {
    self.fpsPre = fps;
    return [self.mediaStream setVideoSize:self.videoWidth videoHeight:self.videoHeight frameRate:fps];
}

/** key 指令类型 3:home 4:back  25:声音-键  24:声音+键  187:后台切换 **/
/// 调整音量 0-减 1-加
- (BOOL)setVoice:(NSInteger)type {
    int key = type == 0 ? 25 : 24;
    return [self.mediaStream sendKey:key];
}

/// Home键
- (BOOL)systemHomeBtnClick {
    return [self.mediaStream sendKey:3];
}

/// 返回键键
- (BOOL)systemBackBtnClick {
    return [self.mediaStream sendKey:4];
}

/// 菜单键
- (BOOL)systemMenuBtnClick {
//    [self.mediaStream sendKey:82];
    return [self.mediaStream sendKey:187];
}


#pragma mark - 键盘透传
/// 设置输入键盘类型： 1、设置真机输入法  2、设置云手机输入法 
- (void)setInputMode:(int)inputMode {
    _inputMode = inputMode;
    dispatch_async(dispatch_get_main_queue(), ^{
        if (inputMode == 2) { [self endEditing:YES]; }
    });
    
    [self setInputMethod:inputMode];
}

/// 1、设置真机输入法  2、设置云手机输入法
- (void)setInputMethod:(int)mode {
    NSString *order = [RCWSOrder setInputMethod:mode];
    [self sendData:order];
}

- (void)textFieldDeleteBackward:(RCTextField *)textField {
    [self setKeyoardInfo:@""]; // 删除
}

- (void)textFieldDidChange:(UITextField *)textField {
    UITextRange *markedTextRange = textField.markedTextRange;
    if (!markedTextRange && textField.text.length > 0) {
        NSLog(@"输入框: 已确认的输入内容: %@", textField.text);
        [self setKeyoardInfo:textField.text]; // 输入
        self.textField.text = @"";
    } else {
        NSLog(@"输入框: 存在未确认的输入内容");
    }
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [self.mediaStream sendKey:66];
    return YES;
}

/// 设置真机键盘输入内容 (自定义输入内容透传给云机输入框)
- (void)setKeyoardInfo:(NSString *)content {
    NSString *order = content.length == 0 ? [RCWSOrder setKeyoardInfo:@"" position:-1] : [RCWSOrder setKeyoardInfo:content position:0];
    [self sendData:order];
}

/// 自定义输入内容透传给云机剪贴板
- (void)setClipboardInfo:(NSString *)content {
    NSString *order = [RCWSOrder clipboardOrder:content];
    [self.client fl_send:order];
}

/// 自定义拉流 ws 发送指令
- (void)sendWSOrder:(NSString *)order {
    [self.client fl_send:order];
}

- (void)receiveDataChannel:(NSData *)data isBinary:(BOOL)isBinary {
    if (isBinary) { return; }
    NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
    NSLog(@"数据通道 %@", dataDict);
    
    if ([[dataDict allKeys] containsObject:@"type"]) {
        NSString *type = [NSString stringWithFormat:@"%@", dataDict[@"type"]];
        if ([type isEqualToString:@"keyboardFeedbackBean"]) { // 显示真机键盘输入法
            [self showKeyboard:YES];
            NSLog(@"显示真机键盘输入法");
        } else if ([type isEqualToString:@"KeyboardHidden"]) { // 隐藏真机键盘输入法
            [self showKeyboard:NO];
            NSLog(@"隐藏真机键盘输入法");
        } else if ([type isEqualToString:@"setVideoSizeRes"]) { // 分辨率设置回调
            BOOL isSuccess = [dataDict[@"succeed"] boolValue];
            NSLog(@"分辨率/帧率 设置：%d", isSuccess);
            if (isSuccess) {
                if (self.fps != self.fpsPre) {
                    self.fps = self.fpsPre;
                } else if (self.videoWidth != self.videoWidthPre || self.videoHeight != self.videoHeightPre) {
                    self.videoWidth = self.videoWidthPre;
                    self.videoHeight = self.videoHeightPre;
                }
            } else {
                if (self.fps != self.fpsPre) {
                    self.fpsPre = self.fps;
                } else if (self.videoWidth != self.videoWidthPre || self.videoHeight != self.videoHeightPre) {
                    self.videoWidthPre = self.videoWidth;
                    self.videoHeightPre = self.videoHeight;
                }
            }
        } else if ([type isEqualToString:@"InputMethod"]) { // 1、真机键盘 2、系统默认键盘
            int type = [dataDict[@"data"][@"type"] intValue];
            self.inputMode = type; // 默认真机键盘
        }
    } else if ([[dataDict allKeys] containsObject:@"action"]) {
        NSString *type = [NSString stringWithFormat:@"%@", dataDict[@"action"]];
        if ([type isEqualToString:@"auths"]) { // 真机键盘授权
            BOOL isSuccess = [dataDict[@"result"] boolValue];
            NSLog(@"真机键盘授权：%d", isSuccess);
        }
    }
}

- (void)showKeyboard:(BOOL)isShow {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (isShow) {
            [self.textField becomeFirstResponder];
        } else {
            [self.textField resignFirstResponder];
        }
    });
}


#pragma mark - 其他
/// 通过data channel发送字符串到云机
- (BOOL)sendData:(NSString*)strData {
    NSLog(@"sendData : %@", strData);
    return [self.mediaStream sendData:strData];
}
/// 通过data channel发送BYTE到云机
- (BOOL)sendBinary:(NSData*)data {
    return [self.mediaStream sendBinary:data];
}
/// 发送键盘事件到云机
- (BOOL)sendKey:(int)value {
    return [self.mediaStream sendKey:value];
}


#pragma mark - 信息上报
/// 出图后发送信息给推流段
- (void)sendInfo {

    NSString *version = [RCOperateManager shareInstance].sdkVersion;
    NSString *deviceID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    NSString *IP = [self getIPAddress];

    [RCPlayerView getIPWithCompletion:^(NSString *ip, NSError *error) {
        NSString *user_ip = ip;
        if (ip == nil || ip.length == 0) { user_ip = IP; }
        NSDictionary *dict = @{@"type": @"clientInfo",
                               @"sdk_version": version,
                               @"user_identity": deviceID,
                               @"user_ip": user_ip};
        NSString *json = [RCTool getJSONStringFrom:dict];
        NSLog(@"信息上报 成功 %@", json);
        [self sendData:json];
    }];
}

/// 获取外网IP
+ (void)getIPWithCompletion:(void (^)(NSString *ip, NSError *error))completion {
    NSArray<NSString *> *ipServiceURLs = @[
        @"https://www.ip.cn/api/index?ip=&type=0",            // 国内
        @"https://ip.taobao.com/outGetIpInfo?ip=myip&type=1", // 国内
        @"https://ipinfo.io/json",                            // 国外
    ];
    
    [self tryFetchIPFromServices:ipServiceURLs index:0 completion:completion];
}

+ (void)tryFetchIPFromServices:(NSArray<NSString *> *)services
                         index:(NSInteger)index
                    completion:(void (^)(NSString *ip, NSError *error))completion {
    if (index >= services.count) {
        if (completion) {
            NSError *error = [NSError errorWithDomain:@"IPFetcher"
                                                 code:-1001
                                             userInfo:@{NSLocalizedDescriptionKey: @"获取外网 IP 失败，所有服务均不可用"}];
            dispatch_async(dispatch_get_main_queue(), ^{
                completion(nil, error);
            });
        }
        return;
    }

    NSString *urlString = services[index];
    NSURL *url = [NSURL URLWithString:urlString];
    if (!url) {
        [self tryFetchIPFromServices:services index:index + 1 completion:completion];
        return;
    }

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.timeoutIntervalForRequest = 3.0;
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

    NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error || !data) {
            [self tryFetchIPFromServices:services index:index + 1 completion:completion];
            return;
        }

        NSString *ip = nil;
        NSError *jsonError = nil;
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];

        if (!jsonError && [json isKindOfClass:[NSDictionary class]]) {
            ip = json[@"ip"] ?: json[@"data"][@"ip"] ?: json[@"cip"] ?: json[@"origin"];
        }

        if (!ip) {
            NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSString *trimmed = [text stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
            if (trimmed.length > 0 && [trimmed componentsSeparatedByString:@"."].count == 4) {
                ip = trimmed;
            }
        }

        if (ip.length > 0) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (completion) completion(ip, nil);
            });
        } else {
            [self tryFetchIPFromServices:services index:index + 1 completion:completion];
        }
    }];

    [task resume];
}

//获取设备Ip地址
- (NSString *)getIPAddress {
    NSString *address = @"127.0.0.1"; // 默认值
    
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    
    // 获取接口地址列表
    if (getifaddrs(&interfaces) == 0) {
        temp_addr = interfaces;
        
        while (temp_addr != NULL) {
            // 检查是否为 IPv4 地址
            if (temp_addr->ifa_addr->sa_family == AF_INET) {
                // 获取接口名称
                NSString *interfaceName = [NSString stringWithUTF8String:temp_addr->ifa_name];
                
                // 判断是否是 Wi-Fi 或蜂窝网络接口
                if ([interfaceName isEqualToString:@"en0"]) { // en0 是 Wi-Fi 接口
                    struct sockaddr_in *addr_in = (struct sockaddr_in *)temp_addr->ifa_addr;
                    char *ipAddress = inet_ntoa(addr_in->sin_addr);
                    address = [NSString stringWithUTF8String:ipAddress];
                } else if ([interfaceName isEqualToString:@"pdp_ip0"]) { // pdp_ip0 是蜂窝网络接口
                    struct sockaddr_in *addr_in = (struct sockaddr_in *)temp_addr->ifa_addr;
                    char *ipAddress = inet_ntoa(addr_in->sin_addr);
                    address = [NSString stringWithUTF8String:ipAddress];
                }
            }
            
            temp_addr = temp_addr->ifa_next;
        }
    }
    
    // 释放内存
    freeifaddrs(interfaces);
    
    return address;
}


#pragma mark - 代理 MediaStreamClientEventsDelegate
- (void)dataChannelDidChangeFromPeerName:(NSString *)peerName State:(RTCDataChannelState)state {
    if ([_delegate respondsToSelector:@selector(dataChannelDidChangeFromPeerName:State:)]) {
        
        RCDataChannelState status = RCDataChannelStateConnecting;
        if (state == RTCDataChannelStateConnecting) {
            status = RCDataChannelStateConnecting;
        } else if (state == RTCDataChannelStateOpen) {
            status = RCDataChannelStateOpen;
//            self.inputMode = 1; // 默认真机键盘
        } else if (state == RTCDataChannelStateClosing) {
            status = RCDataChannelStateClosing;
        } else if (state == RTCDataChannelStateClosed) {
            status = RCDataChannelStateClosed;
        }
        
        [_delegate dataChannelDidChangeFromPeerName:peerName State:status];
    }
}

- (void)onChangeConnectionStateFromPeerName:(NSString *)peerName didChangeIceConnectionState:(RTCIceConnectionState)state {
    if ([_delegate respondsToSelector:@selector(onChangeConnectionStateFromPeerName:didChangeIceConnectionState:)]) {
        
        RCIceConnectionState status = RCIceConnectionStateNew;
        if (state == RTCIceConnectionStateNew) {
            status = RCIceConnectionStateNew;
        } else if (state == RTCIceConnectionStateChecking) {
            status = RCIceConnectionStateChecking;
        } else if (state == RTCIceConnectionStateConnected) {
            status = RCIceConnectionStateConnected;
            [self sendInfo];
        } else if (state == RTCIceConnectionStateCompleted) {
            status = RCIceConnectionStateCompleted;
        } else if (state == RTCIceConnectionStateFailed) {
            status = RCIceConnectionStateFailed;
        } else if (state == RTCIceConnectionStateDisconnected) {
            status = RCIceConnectionStateDisconnected;
        } else if (state == RTCIceConnectionStateClosed) {
            status = RCIceConnectionStateClosed;
        } else if (state == RTCIceConnectionStateCount) {
            status = RCIceConnectionStateCount;
        }
        
        [_delegate onChangeConnectionStateFromPeerName:peerName didChangeIceConnectionState:status];
        
        NSLog(@"onChangeConnectionStateFromPeerName: %zd", status);
    }
}


- (void)onFrameResolutionChangedFromPeerName:(NSString *)peerName videoWidth:(int)videoWidth videoHeight:(int)videoHeight rotation:(int)rotation {
    if ([_delegate respondsToSelector:@selector(onFrameResolutionChangedFromPeerName:videoWidth:videoHeight:rotation:)]) {
        [_delegate onFrameResolutionChangedFromPeerName:peerName videoWidth:videoWidth videoHeight:videoHeight rotation:rotation];
    }
    
    self.isRotation = rotation;
    NSLog(@"onFrameResolutionChangedFromPeerName");
}

- (void)onAuthResultFromPeerName:(NSString *)peerName code:(int)code descriptions:(NSString *)descriptions {
    if ([_delegate respondsToSelector:@selector(onAuthResultFromPeerName:code:descriptions:)]) {
        [_delegate onAuthResultFromPeerName:peerName code:code descriptions:descriptions];
    }
    
    NSLog(@"onAuthResultFromPeerName: %d", code);
}

- (void)onChannelDataFromPeerName:(NSString *)peerName buffer:(RTC_OBJC_TYPE(RTCDataBuffer) *)buffer {
    
    [self receiveDataChannel:buffer.data isBinary:buffer.isBinary];
    
    RCDataBuffer *dataBuffer = [[RCDataBuffer alloc] initWithData:buffer.data isBinary:buffer.isBinary];
    
    if ([_delegate respondsToSelector:@selector(onChannelDataFromPeerName:buffer:)]) {
        [_delegate onChannelDataFromPeerName:peerName buffer:dataBuffer];
    }
}

- (void)didGetStats:(NSString *)peerName stats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats {
    
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    NSDictionary *ditc = stats.statistics;
    for (NSString *key in [ditc allKeys]) {
        RTCStatistics *value = ditc[key];
        RCStatistics *item = [[RCStatistics alloc] initWithId:value.id timestamp:value.timestamp_us type:value.type values:[value.values copy]];
        if (item != nil && key != nil) { [params setObject:item forKey:key]; }
    }
    
    RCStatisticsReport *status = [[RCStatisticsReport alloc] initWithTimestamp:stats.timestamp_us statistics:params];
    
    if ([_delegate respondsToSelector:@selector(didGetStats:stats:)]) {
        [_delegate didGetStats:peerName stats:status];
    }
    
    // 实时数据流解析
    [self refreshRealInfo:stats];
}

/// 实时数据流解析
- (void)refreshRealInfo:(RTCStatisticsReport *)stats {
    dispatch_async(dispatch_get_main_queue(), ^{
        self.bgView.hidden = !self.isShowLogInfo;
        if (!self.isShowLogInfo && !self.isCallBackLogInfo) { return; }
    });
    
    // 数据流解析
    NSDictionary *dict = stats.statistics;
    NSArray *array = dict.allValues;
    NSMutableDictionary<NSString *, RTCStatistics *> *pairMap = [NSMutableDictionary dictionary];
    NSMutableDictionary<NSString *, RTCStatistics *> *localCandidateMap = [NSMutableDictionary dictionary];
    NSMutableDictionary<NSString *, RTCStatistics *> *remoteCandidateMap = [NSMutableDictionary dictionary];
    RTCStatistics *jsonTransport;
    RTCStatistics *inbound_video;
    RTCStatistics *inbound_audio;
    
    // 参数读取
    for (RTCStatistics *item in array) {
        if (item == nil || item.id == nil) { continue; }
        if ([item.type isEqualToString:@"transport"]) {
            jsonTransport = item;
        } else if ([item.type isEqualToString:@"candidate-pair"]) {
            pairMap[item.id] = item;
        } else if ([item.type isEqualToString:@"local-candidate"]) {
            localCandidateMap[item.id] = item;
        } else if ([item.type isEqualToString:@"remote-candidate"]) {
            remoteCandidateMap[item.id] = item;
        } else if ([item.type isEqualToString:@"inbound-rtp"]) {
            if (![[item.values allKeys] containsObject:@"kind"]) { return; }
            NSString *kind = [NSString stringWithFormat:@"%@", item.values[@"kind"]];
            if ([kind isEqualToString:@"video"]) {
                inbound_video = item;
            } else if ([kind isEqualToString:@"audio"]) {
                inbound_audio = item;
            }
        }
    }
    
    //本次报道状态的时间戳
    CGFloat timestamp = (CGFloat)jsonTransport.timestamp_us;
    //线性增长接收字节数
    NSString *bytesReceivedStr = [NSString stringWithFormat:@"%@", jsonTransport.values[@"bytesReceived"]];
    NSInteger bytesReceived = [bytesReceivedStr integerValue];
    //选择使用的pair
    NSString *selectedCandidatePairId = [NSString stringWithFormat:@"%@", jsonTransport.values[@"selectedCandidatePairId"]];

    //线性增长接收包数
    NSString *packetsReceivedStr = [NSString stringWithFormat:@"%@", jsonTransport.values[@"packetsReceived"]];
    CGFloat packetsReceived = [packetsReceivedStr floatValue];

    //网络往返RTT时间----界面展示的网络延迟
    CGFloat currentRoundTripTime = 0.0;
    //用于获取网络连接类型
    NSString *localCandidateId = @"";
    //用于获取网络连接类型
    NSString *remoteCandidateId = @"";
    //每秒帧数 ----界面展示的帧率
    NSInteger framesPerSecond = 0;
    //线性增长丢包数  音频+视频
    CGFloat packetsLost = 0.0;

    for (RTCStatistics *value in [pairMap allValues]) {
        if ([value.id isEqualToString:selectedCandidatePairId]) {
            NSString *currentRoundTripTimeStr = [NSString stringWithFormat:@"%@", value.values[@"currentRoundTripTime"]];
            currentRoundTripTime = [currentRoundTripTimeStr floatValue];
            currentRoundTripTime = currentRoundTripTime * 1000; // s 转 ms
            localCandidateId = [NSString stringWithFormat:@"%@", value.values[@"localCandidateId"]];
            remoteCandidateId = [NSString stringWithFormat:@"%@", value.values[@"remoteCandidateId"]];
        }
    }

    // 分辨率 帧率
    NSInteger frameWidth = 0;
    NSInteger frameHeight = 0;
    NSString *framesPerSecondStr = [NSString stringWithFormat:@"%@", inbound_video.values[@"framesPerSecond"]];
    framesPerSecond = [framesPerSecondStr integerValue];
    NSString *frameWidthStr = [NSString stringWithFormat:@"%@", inbound_video.values[@"frameWidth"]];
    frameWidth = [frameWidthStr integerValue];
    NSString *frameHeightStr = [NSString stringWithFormat:@"%@", inbound_video.values[@"frameHeight"]];
    frameHeight = [frameHeightStr integerValue];

    // 丢包率
    NSString *videoPacketsLostStr = [NSString stringWithFormat:@"%@", inbound_video.values[@"packetsLost"]];
    CGFloat videoPacketsLost = [videoPacketsLostStr floatValue];
    packetsLost += videoPacketsLost;
    NSString *audioPacketsLostStr = [NSString stringWithFormat:@"%@", inbound_audio.values[@"packetsLost"]];
    CGFloat audioPacketsLost = [audioPacketsLostStr floatValue];
    packetsLost += audioPacketsLost;

    CGFloat chu = (packetsReceived - self.lastPacketsReceived) + (packetsLost - self.lastPacketsLost);
    CGFloat lostRate = 0.0;
    if (chu != 0.0) { lostRate = (packetsLost - self.lastPacketsLost)*100 / chu; }
    self.lastPacketsLost = packetsLost;
    self.lastPacketsReceived = packetsReceived;
    
    // 解码时间
    NSString *totalDecodeTimeStr = [NSString stringWithFormat:@"%@", inbound_video.values[@"totalDecodeTime"]];
    CGFloat totalDecodeTime = [totalDecodeTimeStr floatValue];
    NSString *framesDecodedStr = [NSString stringWithFormat:@"%@", inbound_video.values[@"framesDecoded"]];
    NSInteger framesDecoded = [framesDecodedStr integerValue];
    CGFloat averageDecodeTime = 0.0;
    if (framesDecoded != 0) { averageDecodeTime = totalDecodeTime *10000 / framesDecoded; }

    // 码率（preReceive/1024 kb/s）
    CGFloat preReceive = 0;
    if (self.lastReportTime > 0) {
        NSInteger diffReceive = bytesReceived - self.lastBytesReceived;
        NSInteger diffTime = timestamp - self.lastReportTime;
        if (diffTime != 0) { preReceive = diffReceive * 1000000.0 / diffTime; }
        preReceive = preReceive/1024;
    }
    self.lastBytesReceived = bytesReceived;
    self.lastReportTime = timestamp;
    
//    NSLog(@"实时 网络延迟：%.fms", currentRoundTripTime);
//    NSLog(@"实时 解码时间：%.fms", averageDecodeTime);
//    NSLog(@"实时 码率：%.fkbps", preReceive);
//    NSLog(@"实时 帧率：%zd", framesPerSecond);
//    NSLog(@"实时 分辨率：%zd*%zd", frameWidth, frameHeight);
//    NSLog(@"实时 丢包率：%.f%%", lostRate);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        // 回调
        if (self.isCallBackLogInfo && [self->_delegate respondsToSelector:@selector(videoParamsCallBack:)]) {
            NSMutableDictionary *params = [NSMutableDictionary dictionary];
            params[@"currentRoundTripTime"] = [NSString stringWithFormat:@"%.fms", currentRoundTripTime];
            params[@"averageDecodeTime"] = [NSString stringWithFormat:@"%.fms", averageDecodeTime];
            params[@"preReceive"] = [NSString stringWithFormat:@"%.fkbps", preReceive];
            params[@"framesPerSecond"] = [NSString stringWithFormat:@"%zdfps", framesPerSecond];
            params[@"lostRate"] = [NSString stringWithFormat:@"%.f%%", lostRate];
            params[@"frameWidth"] = [NSString stringWithFormat:@"%zd", frameWidth];
            params[@"frameHeight"] = [NSString stringWithFormat:@"%zd", frameHeight];
            [self->_delegate videoParamsCallBack:params];
        }
        
        // 显示
        if (!self.isShowLogInfo) { return; }
        self.infoLabel.text = [NSString stringWithFormat:@" 网络延迟：%.fms \n 解码时间：%.fms \n 码率：%.fkbps \n 帧率：%zd \n 分辨率：%zd*%zd \n", currentRoundTripTime, averageDecodeTime, preReceive, framesPerSecond, frameWidth, frameHeight];
    });
}

-(void)onFirstFrameRendered:(NSString*)peerName {
    if ([_delegate respondsToSelector:@selector(onFirstFrameRendered:)]) {
        [_delegate onFirstFrameRendered:peerName];
    }
}

- (void)connectionChange:(NSString*)peerName
    didChangeLocalCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)local
            remoteCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)remote
             lastReceivedMs:(int)lastDataReceivedMs
            changeReason:(NSString *)reason {
    
    RCIceCandidate *localCandidate = [[RCIceCandidate alloc] initWithSdp:local.sdp sdpMLineIndex:local.sdpMLineIndex sdpMid:local.sdpMid];
    localCandidate.serverUrl = local.serverUrl;
    
    RCIceCandidate *remoteCandidate = [[RCIceCandidate alloc] initWithSdp:remote.sdp sdpMLineIndex:remote.sdpMLineIndex sdpMid:remote.sdpMid];
    remoteCandidate.serverUrl = remote.serverUrl;
    
    if ([_delegate respondsToSelector:@selector(connectionChange:didChangeLocalCandidate:remoteCandidate:lastReceivedMs:changeReason:)]) {
        [_delegate connectionChange:peerName didChangeLocalCandidate:localCandidate remoteCandidate:remoteCandidate lastReceivedMs:lastDataReceivedMs changeReason:reason];
    }
}

/// 麦克风和摄像头透传
- (void)virtualMicrophoneCameraOccupiedNotify:(BOOL)microphone camera:(BOOL)camera userData:(NSString *)userData {
    if ([_delegate respondsToSelector:@selector(virtualMicrophoneCameraOccupiedNotify:camera:userData:)]) {
        [_delegate virtualMicrophoneCameraOccupiedNotify:microphone camera:camera userData:userData];
    }
}

- (void)virtualMicrophoneStatusNotify:(BOOL)microphoneStatus {
    if ([_delegate respondsToSelector:@selector(virtualMicrophoneStatusNotify:)]) {
        [_delegate virtualMicrophoneStatusNotify:microphoneStatus];
    }
}

- (void)virtualCameraStatusNotify:(BOOL)cameraStatus {
    if ([_delegate respondsToSelector:@selector(virtualCameraStatusNotify:)]) {
        [_delegate virtualCameraStatusNotify:cameraStatus];
    }
}

/// SDK 监听自定义消息
- (void)customMsgNotify:(NSString *)pusher userData:(NSString *)userData {
    if ([_delegate respondsToSelector:@selector(customMsgNotify:userData:)]) {
        [_delegate customMsgNotify:pusher userData:userData];
    }
}

/// 服务器消息透传
- (void)serverMsgNotice:(NSString *)userData {
    if ([_delegate respondsToSelector:@selector(serverMsgNotice:)]) {
        [_delegate serverMsgNotice:userData];
    }
}


#pragma mark - 代理 MediaStreamClientEventsDelegateEx
- (void)dataChannelDidChangeFromPeerNameEx:(NSString*)peerName State:(RTCDataChannelState)state {
    if ([_delegateEx respondsToSelector:@selector(dataChannelDidChangeFromPeerNameEx:State:)]) {
        
        RCDataChannelState status = RCDataChannelStateConnecting;
        if (state == RTCDataChannelStateConnecting) {
            status = RCDataChannelStateConnecting;
        } else if (state == RTCDataChannelStateOpen) {
            status = RCDataChannelStateOpen;
//            self.inputMode = 1; // 默认真机键盘
        } else if (state == RTCDataChannelStateClosing) {
            status = RCDataChannelStateClosing;
        } else if (state == RTCDataChannelStateClosed) {
            status = RCDataChannelStateClosed;
        }
        
        [_delegateEx dataChannelDidChangeFromPeerNameEx:peerName State:status];
    }
}

- (void)onChangeConnectionStateFromPeerNameEx:(NSString*)peerName didChangeIceConnectionState:(RTCIceConnectionState)state {
    if ([_delegateEx respondsToSelector:@selector(onChangeConnectionStateFromPeerNameEx:didChangeIceConnectionState:)]) {
        
        RCIceConnectionState status = RCIceConnectionStateNew;
        if (state == RTCIceConnectionStateNew) {
            status = RCIceConnectionStateNew;
        } else if (state == RTCIceConnectionStateChecking) {
            status = RCIceConnectionStateChecking;
        } else if (state == RTCIceConnectionStateConnected) {
            status = RCIceConnectionStateConnected;
            [self sendInfo];
        } else if (state == RTCIceConnectionStateCompleted) {
            status = RCIceConnectionStateCompleted;
        } else if (state == RTCIceConnectionStateFailed) {
            status = RCIceConnectionStateFailed;
        } else if (state == RTCIceConnectionStateDisconnected) {
            status = RCIceConnectionStateDisconnected;
        } else if (state == RTCIceConnectionStateClosed) {
            status = RCIceConnectionStateClosed;
        } else if (state == RTCIceConnectionStateCount) {
            status = RCIceConnectionStateCount;
        }
        
        [_delegateEx onChangeConnectionStateFromPeerNameEx:peerName didChangeIceConnectionState:status];
    }
}

- (void)onAuthResultFromPeerNameEx:(NSString*)peerName code:(int)code descriptions:(NSString*)descriptions {
    if ([_delegateEx respondsToSelector:@selector(onAuthResultFromPeerNameEx:code:descriptions:)]) {
        [_delegateEx onAuthResultFromPeerNameEx:peerName code:code descriptions:descriptions];
    }
}

#pragma mark - Websocket 流程
/// 开启websocket
- (void)startWebsocketHost:(NSString *)host ip:(NSString *)ip token:(NSString *)token complete:(void (^)(BOOL))complete {
    if (self.client) { [self stopWebsocket]; }
    
    RCWSClient *client = [[RCWSClient alloc] init];
    self.client = client;
    client.overtime = 1;
    client.reconnectCount = INT_MAX;
    client.currentToken = [NSString stringWithFormat:@"Authorization=%@", token];
    client.closeCallBack = ^(NSInteger code, NSString *reason, BOOL wasClean) {
        if ([self->_delegate respondsToSelector:@selector(webSocketDidCloseCallBack)]) {
            [self->_delegate webSocketDidCloseCallBack];
        }
    };

    __weak typeof(client) weakClient = client;
    __weak typeof(self)   weakSelf = self;
    NSString *url = [RCWSOrder getUrlFrom:host ip:ip token:token];
    [client fl_open:url connect:^{
        NSLog(@"音视频 WS 连接成功 %@", url);
        complete(YES);
        [self queryStatus];
    } receive:^(id message, RCSocketReceiveType type) {
        
        // 心跳保活 - Ping-Pong机制
        if (type == RCSocketReceiveTypeForPing) {
            [weakClient fl_sendPong:nil];
        } else if (type == RCSocketReceiveTypeForPong) {
            [weakClient fl_sendPing:nil];
        } else if ([message isKindOfClass:[NSData class]]) { // 预览图解析

        } else if ([message isKindOfClass:[NSMutableString class]] || [message isKindOfClass:[NSString class]]) {  // 指令返回结果解析
            NSDictionary *dataDict = [self getDataDictFrom:(NSString *)message];
            NSLog(@"%@", dataDict);
            
            // WS消息解析
            NSString *type = [NSString stringWithFormat:@"%@", dataDict[@"type"]];
            int code = 0;
            if ([[dataDict allKeys] containsObject:@"code"]) { code = [dataDict[@"code"] intValue]; }
            
            if ([type isEqualToString:@"subscribe_topic"]) { // 订阅主题回调：屏幕亮度、消息透传
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeSubscribeTopic) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"unsubscribe_topic"]) { // 取消订阅主题：屏幕亮度、消息透传
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeUnsubscribeTopic) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"publish_topic"]) { // 发布主题消息回调：屏幕亮度、消息透传、剪贴板透传
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypePublishTopic) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"app_message_transmission"]) { // 消息透传 发送消息
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeSendAppMessage) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"sys_blow"]) { // 吹一吹
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeBlow) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"sys_shake"]) { // 摇一摇
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeShake) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"sys_screenshot"]) { // 截图
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeScreenShot) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"sys_get_bar_status"]) { // 导航栏状态
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeNavStatus) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"sys_get_root"]) { // 全局root状态
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeRootStatus) content:dataDict[@"content"] code:code];
                }
                
            } else if ([type isEqualToString:@"sys_root"]) { // 设置root状态
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeRootSet) content:dataDict[@"content"] code:code];
                }
                
            }  else if ([type isEqualToString:@"sys_get_stream_quality_config"]) { // 查询画质
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeStreamQuality) content:dataDict[@"content"] code:code];
                }
            } else if ([type isEqualToString:@"sys_get_screen_orientation"]) { // 查询屏幕方向
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeScreenOrientation) content:dataDict[@"content"] code:code];
                }
            } else if ([type isEqualToString:@"sys_get_resolution"]) { //  查询分辨率
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeSysResolution) content:dataDict[@"content"] code:code];
                }
            } else if ([type isEqualToString:@"app_get_hide_list"]) { //  查询隐藏应用列表
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeAppHideList) content:dataDict[@"content"] code:code];
                }
            } else if ([type isEqualToString:@"app_get_all_list"]) { //  查询所有应用列表
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeAppAllList) content:dataDict[@"content"] code:code];
                }
            } else if ([type isEqualToString:@"sys_set_stream_volume"]) { //  音量设置
                if ([weakSelf.delegate respondsToSelector:@selector(webSocketCallBack:content:code:)]) {
                    [weakSelf.delegate webSocketCallBack:(RCWSTypeVoiceSet) content:dataDict[@"content"] code:code];
                }
            }
        }
        
    } failure:^(NSError *error) {
        complete(NO);
        NSLog(@"%@", error.userInfo);
    }];
}

/// 关闭websocket
- (void)stopWebsocket {
    [self unsubscribeBrightness];          // 取消订阅屏幕亮度
    [self unsubscribeAppMessage];          // 取消订阅消息透传
    [self unsubscribeClipboard];           // 取消订阅剪贴板透传
    [self unsubscribeScreenOrientation];   // 取消订阅屏幕方向
    [self unsubscribeResolution];          // 取消订阅分辨率
    [self unsubscribeAppShortcut];         // 取消订阅创建快捷方式
    [self unsubscribeVolume];              // 取消订阅音量变化
    [self unsubscribeAppStart];            // 取消订阅应用启动
    [self unsubscribeGPSVariation];        // 取消订阅GPS变化
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.client fl_close:^(NSInteger code, NSString *reason, BOOL wasClean) {
            NSLog(@"音视频 WS 关闭成功 %@", self.client.urlString);
            self.client = nil;
        }];
    });
}

/// 查询Websocket连接状态
- (RCWebSocketStatus)queryWebsocketStatus {
    
    RCWebSocketStatus status = RCWebSocketStatusConnected;
    switch (self.client.fl_socketStatus) {
        case RCSocketStatusConnected:
            status = RCWebSocketStatusConnected;
            break;
        case RCSocketStatusReceived:
            status = RCWebSocketStatusReceived;
            break;
        case RCSocketStatusFailed:
            status = RCWebSocketStatusFailed;
            break;
        case RCSocketStatusClosedByServer:
            status = RCWebSocketStatusClosedByServer;
            break;
        case RCSocketStatusClosedByUser:
            status = RCWebSocketStatusClosedByUser;
            break;
    }
    return status;
}

/// 解析接收到的指令数据
- (NSDictionary *)getDataDictFrom:(NSString *)json {
    NSData *message = [json dataUsingEncoding:(NSUTF8StringEncoding)];
    NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:message options:NSJSONReadingMutableContainers error:nil];
    return dataDict;
}

- (void)queryStatus {
    [self subscribeBrightness];          // 订阅屏幕亮度
    [self subscribeAppMessage];          // 订阅消息透传
    [self subscribeClipboard];           // 订阅剪贴板透传
    [self subscribeScreenOrientation];   // 订阅屏幕方向
    [self subscribeResolution];          // 订阅分辨率
    [self subscribeAppShortcut];         // 订阅创建快捷方式
    [self subscribeVolume];              // 订阅音量变化
    [self subscribeAppStart];            // 订阅应用启动
    [self subscribeGPSVariation];        // 订阅GPS变化
    
    [self queryStreamQuality];           // 查询推流画质
    [self querySysResolution];           // 查询分辨率
    [self queryScreenOriention];         // 查询横竖屏状态
    
//    [self sendSensorData];             // 传感器数据透传
}

#pragma mark Websocket 操作
/// 订阅主题：屏幕亮度
- (void)subscribeBrightness {
    NSString *order = [RCWSOrder subscribeBrightness];
    [self.client fl_send:order];
}

/// 取消订阅主题：屏幕亮度
- (void)unsubscribeBrightness {
    NSString *order = [RCWSOrder unsubscribeBrightness];
    [self.client fl_send:order];
}

/// 订阅主题：剪贴板透传
- (void)subscribeClipboard {
    NSString *order = [RCWSOrder subscribeClipboard];
    [self.client fl_send:order];
}

/// 取消订阅主题：剪贴板透传
- (void)unsubscribeClipboard {
    NSString *order = [RCWSOrder unsubscribeClipboard];
    [self.client fl_send:order];
}

/// 订阅主题：屏幕方向
- (void)subscribeScreenOrientation {
    NSString *order = [RCWSOrder subscribeScreenOrientation];
    [self.client fl_send:order];
}

/// 取消订阅主题：屏幕方向
- (void)unsubscribeScreenOrientation {
    NSString *order = [RCWSOrder unsubscribeScreenOrientation];
    [self.client fl_send:order];
}

/// 订阅主题：分辨率变化
- (void)subscribeResolution {
    NSString *order = [RCWSOrder subscribeResolution];
    [self.client fl_send:order];
}

/// 取消订阅主题：分辨率变化
- (void)unsubscribeResolution {
    NSString *order = [RCWSOrder unsubscribeResolution];
    [self.client fl_send:order];
}

/// 订阅主题：创建快捷方式
- (void)subscribeAppShortcut {
    NSString *order = [RCWSOrder subscribeAppShortcut];
    [self.client fl_send:order];
}

/// 取消订阅主题：创建快捷方式
- (void)unsubscribeAppShortcut {
    NSString *order = [RCWSOrder unsubscribeAppShortcut];
    [self.client fl_send:order];
}

/// 订阅主题：应用启动
- (void)subscribeAppStart {
    NSString *order = [RCWSOrder subscribeAppStart];
    [self.client fl_send:order];
}

/// 取消订阅主题：应用启动
- (void)unsubscribeAppStart {
    NSString *order = [RCWSOrder unsubscribeAppStart];
    [self.client fl_send:order];
}

/// 订阅主题：GPS 变化
- (void)subscribeGPSVariation {
    NSString *order = [RCWSOrder unsubscribeGPSVariation];
    [self.client fl_send:order];
}

/// 取消订阅主题：GPS 变化
- (void)unsubscribeGPSVariation {
    NSString *order = [RCWSOrder unsubscribeGPSVariation];
    [self.client fl_send:order];
}

/// 订阅主题：音量变化
- (void)subscribeVolume {
    NSString *order = [RCWSOrder subscribeVolume];
    [self.client fl_send:order];
}

/// 取消订阅主题：音量变化
- (void)unsubscribeVolume {
    NSString *order = [RCWSOrder unsubscribeVolume];
    [self.client fl_send:order];
}

/// 音量设置 streamType:音量类型（0-5）percentage-音量百分比（0-100）
- (void)setVolumeWithStreamType:(uint)streamType percentage:(uint)percentage {
    NSString *order = [RCWSOrder setVolumeWithStreamType:streamType percentage:percentage];
    [self.client fl_send:order];
}

/// 订阅主题：消息透传
- (void)subscribeAppMessage {
    NSString *order = [RCWSOrder subscribeAppMessage];
    [self.client fl_send:order];
}

/// 取消订阅主题：消息透传
- (void)unsubscribeAppMessage {
    NSString *order = [RCWSOrder unsubscribeAppMessage];
    [self.client fl_send:order];
}

/// 消息透传 - 发送消息
- (void)appMsgTransmission:(NSString *)packageName message:(NSString *)message {
    NSString *order = [RCWSOrder appMsgTransmission:packageName message:message];
    [self.client fl_send:order];
}

/// 查询推流画质
- (void)queryStreamQuality {
    NSString *order = [RCWSOrder queryStreamQuality];
    [self.client fl_send:order];
}

/// 查询分辨率
- (void)querySysResolution {
    NSString *order = [RCWSOrder querySysResolution];
    [self.client fl_send:order];
}

/// 查询横竖屏状态
- (void)queryScreenOriention {
    NSString *order = [RCWSOrder queryScreenOriention];
    [self.client fl_send:order];
}

/// 查询隐藏应用列表
- (void)queryAppHideList {
    NSString *order = [RCWSOrder queryAppHideList];
    [self.client fl_send:order];
}

/// 查询所有应用列表
- (void)queryAppAllList {
    NSString *order = [RCWSOrder queryAppAllList];
    [self.client fl_send:order];
}

/// 显示应用
- (void)showAppPackageNames:(NSArray *)packageNames {
    NSString *order = [RCWSOrder showApps:packageNames];
    [self.client fl_send:order];
}

/// 隐藏应用
- (void)hideAppPackageNames:(NSArray *)packageNames {
    NSString *order = [RCWSOrder hideApps:packageNames];
    [self.client fl_send:order];
}

/// 打开应用
- (void)openAppPackageName:(NSString *)packageName {
    NSString *order = [RCWSOrder startApp:packageName];
    [self.client fl_send:order];
}

/// 截图（quality ： 1～100 ）
- (void)screenShot:(int)quality {
    NSString *order = [RCWSOrder screenShot:quality];
    [self.client fl_send:order];
}

/// 吹一吹 取值范围 5-10秒
- (void)sendBlowOrder:(int)time {
    NSString *order = [RCWSOrder blowMargin:time];
    [self.client fl_send:order];
}

/// 摇一摇 取值范围 3-10秒
- (void)sendShakeOrder:(int)time {
    NSString *order = [RCWSOrder shakeMargin:time];
    [self.client fl_send:order];
}

/// 隐藏导航栏
- (void)hideNavBar {
    NSString *order = [RCWSOrder hideNavBar];
    [self.client fl_send:order];
}

/// 显示导航栏
- (void)showNavBar {
    NSString *order = [RCWSOrder showNavBar];
    [self.client fl_send:order];
}

/// 查询导航栏状态 YES-显示 NO-隐藏
- (void)queryNavBarStatus {
    NSString *order = [RCWSOrder queryNavBar];
    [self.client fl_send:order];
}

/// 查询屏幕状态 NO-竖屏 YES-横屏
- (BOOL)queryScreenStatus {
    return self.isRotation; // 默认竖屏
}

/// 查询全局 root 状态
- (void)queryRootStatus {
    NSString *order = [RCWSOrder queryRootStatus];
    [self.client fl_send:order];
}

/// 设置全局 root 状态
- (void)rootSystemStatus:(BOOL)isOpen {
    int open = isOpen ? 1 : 0;
    NSString *order = [RCWSOrder rootSystemOrder:open];
    [self.client fl_send:order];
}

#pragma mark - 传感器数据透传
- (void)openSensorData:(BOOL)isOpen {
    self.isSensor = isOpen;
}

- (void)setIsSensor:(BOOL)isSensor {
    self.isGravity = isSensor;
    self.isAccelerate = isSensor;
    self.isMagnet = isSensor;
    self.isGyro = isSensor;
    self.isLocation = isSensor;
    self.isPressure = isSensor;
}

- (void)sendSensorData {
    [self startGravity];        // 重力感应器/线性加速度/旋转矢量/方向传感器
    [self startAccelerate];     // 加速度计感应器
    [self startMagnet];         // 磁力计感应器
    [self startGyro];           // 陀螺仪感应器
    [self startLocation];       // GPS感应器
    [self startPressure];       // 气压计感应器
}

- (void)initSensor {
    // 重力感应器/线性加速度/旋转矢量/方向传感器
    self.motionManager = [[CMMotionManager alloc] init];
    // GPS感应器
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    // 气压计感应器
    self.altimeter = [[CMAltimeter alloc] init];
}

#pragma mark - 重力感应器/线性加速度/旋转矢量/方向传感器
- (void)setIsGravity:(BOOL)isGravity {
    _isGravity = isGravity;
    
    if (isGravity) {
        [self startGravity];
    } else {
        [self stopGravity];
    }
}

/// 开始监听-重力感应器/线性加速度/旋转矢量/方向传感器
- (void)startGravity {
    if (!self.isGravity) { return; }
    if (self.client.fl_socketStatus != RCSocketStatusConnected &&
        self.client.fl_socketStatus != RCSocketStatusReceived) { return; }
    
    // 0、重力感应
    double threshold = 0.01; // 变化阈值
    self.gravityX = self.gravityY = self.gravityZ = 0.0;
    
    // 1、线性加速度
    double threshold1 = 0.01; // 变化阈值
    self.lineX = self.lineY = self.lineZ = 0.0;
    
    // 2、旋转矢量
    double threshold2 = 0.01; // 变化阈值
    self.quaternionX = self.quaternionY = self.quaternionZ = 0.0;
    
    // 3、方向传感器
    double threshold3 = 0.01; // 变化阈值
    self.yaw = self.pitch = self.roll = 0.0;
    
    if (!self.motionManager.isDeviceMotionAvailable) {
        if ([self.delegate respondsToSelector:@selector(toastMsg:)]) {
            [self.delegate toastMsg:@"重力感应器无法使用"];
        }
        return;
    }
    
    self.motionManager.deviceMotionUpdateInterval = 0.1; // 100ms 更新一次
    [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
                                         withHandler:^(CMDeviceMotion *motion, NSError *error) {
        if (error) {
            NSLog(@"Motion update error: %@", error.localizedDescription);
            return;
        }
        
        // 0、重力感应
        double x = motion.gravity.x;
        double y = motion.gravity.y;
        double z = motion.gravity.z;
        
        if (fabs(x - self.gravityX) > threshold ||
            fabs(y - self.gravityY) > threshold ||
            fabs(z - self.gravityZ) > threshold) {
            
            self.gravityX = x;
            self.gravityY = y;
            self.gravityZ = z;
            
            NSData *packet = [self packageDataWithX:x Y:y Z:z code:0x000D];
            [self.client fl_send:packet]; // 通过 WS 发送 Data
//            NSLog(@"传感器 重力 发送数据 x: %f y: %f z: %f", x, y, z);
            NSLog(@"传感器 重力 发送数据 Data: %@", packet);
        }
        
        // 1、线性加速度
        double x1 = motion.userAcceleration.x;
        double y1 = motion.userAcceleration.y;
        double z1 = motion.userAcceleration.z;
        
        if (fabs(x1 - self.lineX) > threshold1 ||
            fabs(y1 - self.lineY) > threshold1 ||
            fabs(z1 - self.lineZ) > threshold1) {
            
            self.lineX = x1;
            self.lineY = y1;
            self.lineZ = z1;
            
            NSData *packet1 = [self packageDataWithX:x1 Y:y1 Z:z1 code:0x0015];
            [self.client fl_send:packet1]; // 通过 WS 发送 Data
            NSLog(@"传感器 线性加速度 发送数据 x: %f y: %f z: %f", x, y, z);
            NSLog(@"传感器 线性加速度 发送数据 Data: %@", packet1);
        }
        
        // 2、 旋转矢量
        CMQuaternion quaternion = motion.attitude.quaternion;
        double x2 = quaternion.x;
        double y2 = quaternion.y;
        double z2 = quaternion.z;
        
        if (fabs(x2 - self.quaternionX) > threshold2 ||
            fabs(y2 - self.quaternionY) > threshold2 ||
            fabs(z2 - self.quaternionZ) > threshold2) {
            
            self.quaternionX = x2;
            self.quaternionY = y2;
            self.quaternionZ = z2;
            
            NSData *packet2 = [self packageDataWithX:x2 Y:y2 Z:z2 code:0x0016];
            [self.client fl_send:packet2]; // 通过 WS 发送 Data
            NSLog(@"传感器 旋转矢量 发送数据 x: %f y: %f z: %f", x, y, z);
            NSLog(@"传感器 旋转矢量 发送数据 Data: %@", packet2);
        }
        
        // 3、 方向传感器
        double yaw = motion.attitude.yaw; // 方位角
        double pitch = motion.attitude.pitch; // 俯仰角
        double roll = motion.attitude.roll; // 横滚角
        
        if (fabs(yaw - self.yaw) > threshold3 ||
            fabs(pitch - self.pitch) > threshold3 ||
            fabs(roll - self.roll) > threshold3) {
            
            self.yaw = yaw;
            self.pitch = pitch;
            self.roll = roll;
            
            NSData *packet3 = [self packageDataWithX:yaw Y:pitch Z:roll code:0x0010];
            [self.client fl_send:packet3]; // 通过 WS 发送 Data
            NSLog(@"传感器 方向 发送数据 yaw: %f pitch: %f roll: %f", yaw, pitch, roll);
            NSLog(@"传感器 方向 发送数据 Data: %@", packet3);
        }
        
    }];
}

/// 停止监听-重力感应器/线性加速度/旋转矢量/方向传感器
- (void)stopGravity {
    if (self.motionManager.isDeviceMotionActive) {
        [self.motionManager stopDeviceMotionUpdates];
    }
    NSLog(@"已停止重力感应监听");
}


#pragma mark - 加速度计感应器
- (void)setIsAccelerate:(BOOL)isAccelerate {
    _isAccelerate = isAccelerate;
    
    if (isAccelerate) {
        [self startAccelerate];
    } else {
        [self stopAccelerate];
    }
}

/// 开始监听- 加速度计感应器
- (void)startAccelerate {
    if (!self.isAccelerate) { return; }
    if (self.client.fl_socketStatus != RCSocketStatusConnected &&
        self.client.fl_socketStatus != RCSocketStatusReceived) { return; }
    double threshold = 0.02;
    self.accelerateX = self.accelerateY = self.accelerateZ = 0.0;
    
    if (!self.motionManager.isAccelerometerAvailable) {
        if ([self.delegate respondsToSelector:@selector(toastMsg:)]) {
            [self.delegate toastMsg:@"加速度计感应器无法使用"];
        }
        return;
    }
    
    self.motionManager.accelerometerUpdateInterval = 0.1; // 100ms 更新一次
    [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
                                             withHandler:^(CMAccelerometerData * _Nullable data, NSError * _Nullable error) {
        if (error) {
            NSLog(@"加速度计错误: %@", error.localizedDescription);
            return;
        }
        
        double x = data.acceleration.x;
        double y = data.acceleration.y;
        double z = data.acceleration.z;
        
        if (fabs(x - self.accelerateX) > threshold ||
            fabs(y - self.accelerateY) > threshold ||
            fabs(z - self.accelerateZ) > threshold) {
            
            self.accelerateX = x;
            self.accelerateY = y;
            self.accelerateZ = z;
            
            NSData *packet = [self packageDataWithX:x Y:y Z:z code:0x000E];
            [self.client fl_send:packet]; // 通过 WS 发送 Data
//            NSLog(@"传感器 加速度计 发送数据 x: %f y: %f z: %f", x, y, z);
            NSLog(@"传感器 加速度计 发送数据 Data: %@", packet);
        }
    }];
}

/// 停止监听-加速度计感应器
- (void)stopAccelerate {
    if (self.motionManager.isAccelerometerActive) {
        [self.motionManager stopAccelerometerUpdates];
        NSLog(@"已停止加速度感应");
    }
}


#pragma mark - 磁力计感应器
- (void)setIsMagnet:(BOOL)isMagnet {
    _isMagnet = isMagnet;
    
    if (isMagnet) {
        [self startMagnet];
    } else {
        [self stopMagnet];
    }
}

/// 开始监听-磁力计感应器
- (void)startMagnet {
    if (!self.isMagnet) { return; }
    if (self.client.fl_socketStatus != RCSocketStatusConnected &&
        self.client.fl_socketStatus != RCSocketStatusReceived) { return; }
    double threshold = 0.6; // 变化阈值
    self.magnetX = self.magnetY = self.magnetZ = 0.0;
    
    if (!self.motionManager.isMagnetometerAvailable) {
        if ([self.delegate respondsToSelector:@selector(toastMsg:)]) {
            [self.delegate toastMsg:@"磁力计感应器无法使用"];
        }
        return;
    }
    
    self.motionManager.magnetometerUpdateInterval = 0.1; // 100ms 更新一次
    [self.motionManager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue]
                                                 withHandler:^(CMMagnetometerData *magData, NSError *error) {
        if (error) {
            NSLog(@"Motion update error: %@", error.localizedDescription);
            return;
        }
        
        double x = magData.magneticField.x;
        double y = magData.magneticField.y;
        double z = magData.magneticField.z;
        
        if (fabs(x - self.magnetX) > threshold ||
            fabs(y - self.magnetY) > threshold ||
            fabs(z - self.magnetZ) > threshold) {
            
            self.magnetX = x;
            self.magnetY = y;
            self.magnetZ = z;
            
            NSData *packet = [self packageDataWithX:x Y:y Z:z code:0x000F];
            [self.client fl_send:packet]; // 通过 WS 发送 Data
//            NSLog(@"传感器 磁力计 发送数据 x: %f y: %f z: %f", x, y, z);
            NSLog(@"传感器 磁力计 发送数据 Data: %@", packet);
        }
    }];
}

/// 停止监听-磁力计感应器
- (void)stopMagnet {
    if (self.motionManager.isMagnetometerActive) {
        [self.motionManager stopMagnetometerUpdates];
    }
}


#pragma mark - 陀螺仪感应器
- (void)setIsGyro:(BOOL)isGyro {
    _isGyro = isGyro;
    
    if (isGyro) {
        [self startGyro];
    } else {
        [self stopGyro];
    }
}

/// 开始监听-陀螺仪感应器
- (void)startGyro {
    if (!self.isGyro) { return; }
    if (self.client.fl_socketStatus != RCSocketStatusConnected &&
        self.client.fl_socketStatus != RCSocketStatusReceived) { return; }
    double threshold = 0.1; // 变化阈值
    self.gyroX = self.gyroY = self.gyroZ = 0.0;
    
    if (!self.motionManager.isGyroAvailable) {
        if ([self.delegate respondsToSelector:@selector(toastMsg:)]) {
            [self.delegate toastMsg:@"陀螺仪感应器无法使用"];
        }
        return;
    }
    
    self.motionManager.gyroUpdateInterval = 0.1; // 100ms 更新一次
    [self.motionManager startGyroUpdatesToQueue:[NSOperationQueue mainQueue]
                                    withHandler:^(CMGyroData *gyroData, NSError *error) {
        if (error) {
            NSLog(@"Motion update error: %@", error.localizedDescription);
            return;
        }
        
        double x = gyroData.rotationRate.x;
        double y = gyroData.rotationRate.y;
        double z = gyroData.rotationRate.z;
        
        if (fabs(x - self.gyroX) > threshold ||
            fabs(y - self.gyroY) > threshold ||
            fabs(z - self.gyroZ) > threshold ||
            (self.gyroX == 0.0 && self.gyroZ == 0.0 && self.gyroZ == 0.0)) {
            
            self.gyroX = x;
            self.gyroY = y;
            self.gyroZ = z;
            
            NSData *packet = [self packageDataWithX:x Y:y Z:z code:0x0011];
            [self.client fl_send:packet]; // 通过 WS 发送 Data
//            NSLog(@"传感器 陀螺仪 发送数据 x: %f y: %f z: %f", x, y, z);
            NSLog(@"传感器 陀螺仪 发送数据 Data: %@", packet);
        }
    }];
}

/// 停止监听-陀螺仪感应器
- (void)stopGyro {
    if (self.motionManager.isGyroActive) {
        [self.motionManager stopGyroUpdates];
    }
}


#pragma  mark - GPS感应器
- (void)setIsLocation:(BOOL)isLocation {
    _isLocation = isLocation;
    
    if (isLocation) {
        [self startLocation];
    } else {
        [self stopLocation];
    }
}

/// 开始定位
- (void)startLocation {
    if (!self.isLocation) { return; }
    if (self.client.fl_socketStatus != RCSocketStatusConnected &&
        self.client.fl_socketStatus != RCSocketStatusReceived) { return; }
    self.longitude = 0.0;
    self.latitude  = 0.0;
    
    // 判断授权状态
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
    if (status == kCLAuthorizationStatusNotDetermined) {
        // 请求权限
        [self.locationManager requestWhenInUseAuthorization];
    } else if (status == kCLAuthorizationStatusDenied || status == kCLAuthorizationStatusRestricted) {
        if ([self.delegate respondsToSelector:@selector(toastMsg:)]) {
            [self.delegate toastMsg:@"GPS透传 需要使用您的位置信息 请在设置中授权定位权限"];
        }
    } else {
        // 已授权，开始定位
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        [self.locationManager startUpdatingLocation];
        NSLog(@"已开启定位");
    }
}

/// 停止定位
- (void)stopLocation {
    [self.locationManager stopUpdatingLocation];
    NSLog(@"已停止定位");
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    CLLocation *location = [locations lastObject];
    CLLocationCoordinate2D coordinate = location.coordinate;
    if (coordinate.longitude == self.longitude &&
        coordinate.latitude  == self.latitude) {
        return;
    }
    self.longitude = coordinate.longitude;
    self.latitude  = coordinate.latitude;
    
//    NSLog(@"传感器 self 经度: %f, 纬度: %f", self.longitude, self.latitude);
//    NSLog(@"传感器 经度: %f, 纬度: %f", coordinate.longitude, coordinate.latitude);
    NSString *longitude = [NSString stringWithFormat:@"%f", coordinate.longitude];
    NSString *latitude = [NSString stringWithFormat:@"%f", coordinate.latitude];

    NSString *order = [RCWSOrder virtualLocationOrder:longitude latitude:latitude];
    [self.client fl_send:order];
    NSLog(@"传感器 GPS 发送数据 Data: %@", order);
    
    // 停止定位以节省电量
    [self.locationManager stopUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    NSLog(@"定位失败: %@", error.localizedDescription);
}


#pragma mark - 光线感应器


#pragma mark - 气压计感应器
- (void)setIsPressure:(BOOL)isPressure {
    _isPressure = isPressure;
    
    if (isPressure) {
        [self startPressure];
    } else {
        [self stopPressure];
    }
}

/// 开始监听-气压计感应器
- (void)startPressure {
    if (!self.isPressure) { return; }
    if (self.client.fl_socketStatus != RCSocketStatusConnected &&
        self.client.fl_socketStatus != RCSocketStatusReceived) { return; }
    double threshold = 0.1; // 变化阈值
    self.pressure = 0.0;
    
    if (![CMAltimeter isRelativeAltitudeAvailable]) {
        if ([self.delegate respondsToSelector:@selector(toastMsg:)]) {
            [self.delegate toastMsg:@"气压计感应器无法使用"];
        }
        return;
    }
    
    [self.altimeter startRelativeAltitudeUpdatesToQueue:[NSOperationQueue mainQueue]
                                            withHandler:^(CMAltitudeData * _Nullable data, NSError * _Nullable error) {
        if (error) {
            NSLog(@"气压计错误: %@", error.localizedDescription);
            return;
        }
        
        double pressure = [data.pressure doubleValue]; // 单位: kPa
        pressure = pressure * 10.0; // 0.1 kPa ≈ 1 hPa
        
        if (fabs(pressure - self.pressure) >= threshold) {
            self.pressure = pressure;

            NSData *packet = [self packageDataWithValue:pressure code:0x0013];
            [self.client fl_send:packet]; // 通过 WS 发送 Data
//            NSLog(@"传感器 气压计 发送数据 v: %f", pressure);
            NSLog(@"传感器 气压计 发送数据 Data: %@", packet);
        }
    }];
}

/// 停止监听-气压计感应器
- (void)stopPressure {
    [self.altimeter stopRelativeAltitudeUpdates];
}


#pragma mark - 距离感应器
- (void)setIsDistance:(BOOL)isDistance {
    _isDistance = isDistance;
    
    if (isDistance) {
        [self startDistance];
    } else {
        [self stopDistance];
    }
}

- (void)startDistance {
    UIDevice *device = [UIDevice currentDevice];
    device.proximityMonitoringEnabled = YES;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityStateDidChange:) name:UIDeviceProximityStateDidChangeNotification object:nil];
}

- (void)stopDistance {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceProximityStateDidChangeNotification object:nil];
}

- (void)proximityStateDidChange:(NSNotification *)notification {
    BOOL isClose = [UIDevice currentDevice].proximityState;
    NSLog(@"传感器 距离 物体%@", isClose ? @"靠近" : @"远离");
}


#pragma mark - 数据包装
/// 三个参数
- (NSData *)packageDataWithX:(double)x Y:(double)y Z:(double)z code:(uint16_t)code {
    uint16_t type = CFSwapInt16HostToBig(code);
    uint32_t length = CFSwapInt32HostToBig(12); // 12 字节
    
    float fx = (float)x;
    float fy = (float)y;
    float fz = (float)z;
    
    uint32_t bx = CFSwapInt32HostToBig(*(uint32_t *)&fx);
    uint32_t by = CFSwapInt32HostToBig(*(uint32_t *)&fy);
    uint32_t bz = CFSwapInt32HostToBig(*(uint32_t *)&fz);
    
    NSMutableData *data = [NSMutableData data];
    [data appendBytes:&type length:sizeof(type)];
    [data appendBytes:&length length:sizeof(length)];
    [data appendBytes:&bx length:sizeof(bx)];
    [data appendBytes:&by length:sizeof(by)];
    [data appendBytes:&bz length:sizeof(bz)];
    
    return data;
}

/// 一个参数
- (NSData *)packageDataWithValue:(double)v code:(uint16_t)code {
    uint16_t type = CFSwapInt16HostToBig(code);
    uint32_t length = CFSwapInt32HostToBig(4); // 4 字节
    
    float fv = (float)v;
    uint32_t bv = CFSwapInt32HostToBig(*(uint32_t *)&fv);
    
    NSMutableData *data = [NSMutableData data];
    [data appendBytes:&type length:sizeof(type)];
    [data appendBytes:&length length:sizeof(length)];
    [data appendBytes:&bv length:sizeof(bv)];
    
    return data;
}


#pragma mark - 懒加载
- (AMediaStream *)mediaStream {
    if (!_mediaStream) {
        _mediaStream = [[RTC_OBJC_TYPE(AMediaStream) alloc] init];
        _mediaStream.eventDelegate = self;
        _mediaStream.eventDelegateEx = self;
    }
    return _mediaStream;
}

- (NSString *)name {
    return self.mediaStream.name;
}

- (BOOL)shouldGetStats {
    return self.mediaStream.shouldGetStats;
}


@end
