//
//  RDSBabyBluetooth.m
//  RuiZhi
//
//  Created by Rayson on 2020/6/13.
//  Copyright © 2020 RDIOT. All rights reserved.
//

#import "RDSBabyBluetooth.h"

#import "NSString+convert.h"
#import "NSString+safe.h"

#import "RDSDeallocWatcher.h"
#import <objc/runtime.h>


#define Server_00FF        @"00FF"
#define Characteristic_FF01 @"FF01"

@interface RDSBabyBluetooth ()<CBCentralManagerDelegate,CBPeripheralDelegate>

@property (nonatomic, copy) NSString *deviceName;// 要连接的设备名

@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic,strong) CBCharacteristic *characteristic;
@property (nonatomic,strong) CBPeripheral *peripheral;

@property (nonatomic, strong) NSMapTable *blockTable;
@property (nonatomic, strong) NSMapTable *blockTableDisconnect;

@property (nonatomic, assign) BOOL isBinding;

@end
@implementation RDSBabyBluetooth

//DEF_SINGLETON(RDSBabyBluetooth)

// 初始化单例方法
+ (instancetype)sharedInstance
{
    static RDSBabyBluetooth *manager = nil;
    static dispatch_once_t oneToken;
    
    // dispatch_once 是线程安全的，能够做到在多线程的环境下Block中的代码只会被执行一次
    dispatch_once(&oneToken, ^{
        manager = [[RDSBabyBluetooth alloc] init];
        [manager p_setup];
    });
    return manager;
}

// 初始化
- (void)p_setup{
    
    //初始化 蓝牙库
    dispatch_queue_t queue = dispatch_queue_create("yxlj.com", DISPATCH_QUEUE_SERIAL);
    _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:@{@"CBCentralManagerOptionShowPowerAlertKey":@YES}];
    
    
    // 创建 NSMapTable 映射表
    self.blockTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory capacity:1];
    self.blockTableDisconnect = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory capacity:1];
    
}


- (void)rds_beginScan{
    
    if(_centralManager == nil){
        dispatch_queue_t queue = dispatch_queue_create("yxlj.com", DISPATCH_QUEUE_SERIAL);
        _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:queue options:@{@"CBCentralManagerOptionShowPowerAlertKey":@YES}];
    }
    else{
        [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:Server_00FF]] options:nil];
    }
}

- (void)rds_cancelScan{
    //停止扫描
    [self.centralManager stopScan];
}


- (void)rds_cancelConnection{
    //停止之前的连接
    if(self.centralManager && self.peripheral){
        [self.centralManager cancelPeripheralConnection:self.peripheral];
    }
    
    _isBinding = NO;
}

- (void)rds_refreshScanDeviceName:(NSString *)name{
    
    _isBinding = YES;
    self.deviceName = name;
    [self rds_refreshScan];
}

- (void)rds_refreshScan{
    
    DDLog(@"重新扫描设备");
    [self rds_beginScan];
    
}













#pragma mark - 写一个值
//写一个值
-(void)rds_writeValue:(NSData *)data{
    
    if (data == nil){
        DDLog(@"数据为空，写入失败");
        return;
    }
    if (self.peripheral == nil || self.characteristic == nil) {
        DDLog(@"代理为空，写入失败");
        return;
    }
    
    
    [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}

#pragma mark  解析设备回复
- (void)p_receiveData:(NSData *)data{
    
    RDS_WEAKSELF(weakSelf)
    [RDSDeviceCmd rds_receiveData:data finish:^(RDSDeviceReply *reply) {
        
        [weakSelf p_cmdResponce:reply];
    }];
    
    
}

#pragma mark - 蓝牙配置和操作


#pragma mark - CBCentralManagerDelegate

/** 手机蓝牙状态 */
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
    if (central.state == CBManagerStateUnknown) {
        NSLog(@"系统蓝牙当前状态不明确");
    }else if (central.state == CBManagerStateResetting){
        NSLog(@"系统蓝牙设备正在被重置");
    }else if (central.state == CBManagerStateUnsupported){
        NSLog(@"系统蓝牙设备不支持");
    }else if (central.state == CBManagerStateUnauthorized){
        NSLog(@"系统蓝未被授权");
    }else if (central.state == CBManagerStatePoweredOff){
        NSLog(@"系统蓝牙关闭了，请先打开蓝牙");
    }else if (central.state == CBManagerStatePoweredOn){
        NSLog(@"系统蓝牙开启");
        //扫描设备
        if(_isBinding){
            [_centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:Server_00FF]] options:nil];
        }
        
    }
}

/** 扫描结果 */
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
    NSLog(@"%@",advertisementData);
    // C049EFC91660 807D3AE5E5D4
    if ([peripheral.name hasPrefix:self.deviceName]) {
        self.peripheral = peripheral;
        //连接设备
        [central connectPeripheral:peripheral options:nil];
        //停止扫描
        [self.centralManager stopScan];
    }
}

/** 连接成功 */
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"连接成功");
    // 设置代理
    self.peripheral.delegate = self;
    // 根据UUID来寻找服务
    [self.peripheral discoverServices:@[[CBUUID UUIDWithString:Server_00FF]]];
}
/** 连接失败 */
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error{
    NSLog(@"连接失败");
    // 断开连接可以设置重新连接
    if(_isBinding){
        [central connectPeripheral:peripheral options:nil];
    }
}

/** 断开连接 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
    NSLog(@"断开连接");
    // 断开连接可以设置重新连接
    if(_isBinding){
        [central connectPeripheral:peripheral options:nil];
    }
}

#pragma mark - CBPeripheralDelegate

/** 发现服务 */
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
    CBService * __nullable findService = nil;
    // 遍历服务
    for (CBService *service in peripheral.services)  {
        NSLog(@"遍历服务 UUID:%@",service.UUID);
        if ([[service UUID] isEqual:[CBUUID UUIDWithString:Server_00FF]])  {
            findService = service;
        }
    }

    if (findService)
        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:Characteristic_FF01]] forService:findService];
}
    
/** 发现特征回调 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    
    // 遍历出所需要的特征
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"所有特征：%@", characteristic);
        // 从外设开发人员那里拿到不同特征的UUID，不同特征做不同事情，比如有读取数据的特征，也有写入数据的特征
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:Characteristic_FF01]]) {
            self.characteristic = characteristic;
            // 订阅通知
            [self.peripheral setNotifyValue:YES forCharacteristic:self.characteristic];
        }
    }

    
}

/** 订阅状态的改变 */
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"订阅失败");
        NSLog(@"%@",error);
    }
    if (characteristic.isNotifying) {
        NSLog(@"订阅成功");
        BLOCK_SAFE_RUN(_onConnectDeviceSuccessAndCanWrite);
    } else {
        NSLog(@"取消订阅");
    }
}

/** 接收到数据回调 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    // 拿到外设发送过来的数据
//    NSData *data = characteristic.value;
//    NSString *msg = [[ NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//    //所有控制符都会被替换成空字符。
//    NSString *jsonString =[msg stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
//    
//    NSLog(@"收到信息 %@",jsonString);
    
    [self p_receiveData:characteristic.value];
}

/** 写入数据回调 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
    NSLog(@"写入成功");
}


#pragma mark - block 一对多监听回调
- (void)addObserver:(id)observer callback:(RDSCmdTouchResponce)callback {
    // 这里要打破循环引用，因为关联代码中 watch 被 observer 持有，而 watch 中的 callback 去调用了 observer
    __weak typeof (observer) weakObserver = observer;
    RDSDeallocWatcher *watch = [[RDSDeallocWatcher alloc] initWithDeallocCallback:^{
        __strong typeof (observer) strongObserver = weakObserver;
        [self removeObserver:strongObserver];
    }];
    [self.blockTable setObject:callback forKey:observer];
    // 将 observer 与 watch 进行绑定关联，key 则使用 observer 的打印地址
    objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], watch, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)addObserver:(id)observer block:(RDSDisconnectDeviceBlock)block {
    // 这里要打破循环引用，因为关联代码中 watch 被 observer 持有，而 watch 中的 callback 去调用了 observer
    __weak typeof (observer) weakObserver = observer;
    RDSDeallocWatcher *watch = [[RDSDeallocWatcher alloc] initWithDeallocCallback:^{
        __strong typeof (observer) strongObserver = weakObserver;
        [self removeObserver:strongObserver];
    }];
    [self.blockTableDisconnect setObject:block forKey:observer];
    // 将 observer 与 watch 进行绑定关联，key 则使用 observer 的打印地址
    objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], watch, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


- (void)removeObserver:(id)observer {
    [self.blockTable removeObjectForKey:observer];
    objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    
    [self.blockTableDisconnect removeObjectForKey:observer];
    objc_setAssociatedObject(observer, [[NSString stringWithFormat:@"%p", &observer] UTF8String], nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}


- (void)p_cmdResponce:(RDSDeviceReply *)reply{
    
    // 回调
    [[[self.blockTable objectEnumerator] allObjects] enumerateObjectsUsingBlock:^(RDSCmdTouchResponce callback, NSUInteger idx, BOOL * _Nonnull stop) {
      callback(reply);
    }];
    
    
}

// block 一对多监听设备信息回调
- (void)rds_cmdResponceObserve:(id)observe responce:(RDSCmdTouchResponce)responceBlock{
    
    if (observe == nil) {
        return;
    }
    [self addObserver:observe callback:responceBlock];
}

- (void)rds_disconnectDeviceObserve:(id)observe block:(RDSDisconnectDeviceBlock)block{
    if (observe == nil) {
        return;
    }
    [self addObserver:observe block:block];
}



- (void)p_disconnectDevice{
    
    [[[self.blockTableDisconnect objectEnumerator] allObjects] enumerateObjectsUsingBlock:^(RDSDisconnectDeviceBlock block, NSUInteger idx, BOOL * _Nonnull stop) {
        block();
    }];
    
}









@end








