//
//  FYSDK.m
//  HelloDemo
//
//  Created by 初程程 on 2019/3/22.
//  Copyright © 2019年 Facebook. All rights reserved.
//

#import "FYSDK.h"
#import "DiscoveryType.h"
#import "IMSLifeClient.h"
#import "RNSendToJsReact.h"
#import <IMSApiClient/IMSApiClient.h>

#define JK_IS_STR_NIL(objStr) (![objStr isKindOfClass:[NSString class]] || objStr == nil || [objStr length] <= 0)
@interface FYSDK()<ILKAddDeviceNotifier>
@end
@implementation FYSDK
+ (FYSDK *)sharedInstance{
    static FYSDK *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[FYSDK alloc] init];
    });
    return manager;
}
- (instancetype)init
{
    self = [super init];
    if (self) {
      
    }
    return self;
}
//启动设备发现
- (void)startScanLocalDevice:(void(^)(NSDictionary *deviceDict ,NSError *err))block filterParams:(NSDictionary *)filterParams unclaimed:(BOOL)unclaimed {
// 由于阿里的人说 startDiscoveryWithFilter 只是用于蓝牙设备，虽然存疑，但再加上
// AliLiving.m 中 startScanLocalDevice 函数里的注释，以及实际运行结果对比没啥
// 差别，所以这里直接代之以 startDiscovery
//    [kLKLocalDeviceMgr startDiscoveryWithFilter:filterParams discoverBlcok:^(NSArray *devices, NSError *err) {
    [kLKLocalDeviceMgr startDiscovery:^(NSArray *devices, NSError *err) {
        if (devices && devices.count > 0) {
            for (IMLCandDeviceModel *device in devices) {

// // Despite unclaimed is true or false for startDiscoveryWithFilter:filterParams with device of `ble_awss start`
// find 1 combo device broadcast with subType = 0x03
//{
//    cipherRandomStr = 00000000000000000000000000000000;
//    cipherType = 0;
//    devType = "ble_subtype_3";
//    deviceEncrypt = 1;
//    deviceIsReset = 1;
//    enableGlobalCloudToken = 0;
//    ignoreLocationPermisionCheck = 0;
//    isBinded = 0;
//    linkType = 0;
//    mac = "BC:DF:20:71:74:04";
//    productId = 6266732;
//    regionNode = 0;
//    softApNoNeedSwitchBackRouter = 0;
//}

// // Despite unclaimed is true or false for startDiscoveryWithFilter:filterParams
// find 1 wifi or ethernet online device
//{
//    cipherRandomStr = 00000000000000000000000000000000;
//    cipherType = 0;
//    devType = 0; // actually is @"0"
//    deviceIsReset = 0;
//    deviceName = testDeviceName7;
//    enableGlobalCloudToken = 0;
//    ignoreLocationPermisionCheck = 0;
//    isBinded = 0;
//    linkType = 0;
//    mac = "BC:DF:70:71:74:04";
//    productKey = a17hN4W4777;
//    regionNode = 0;
//    softApNoNeedSwitchBackRouter = 0;
//    token = CBDD6A0DA3BF0DBE9D99FEB48E6C2D79;
//}
                NSLog(@"AliLiving %@", device);

                if (unclaimed) {
                    if (!device.productKey) {
                        [self provisionDevicePidtoPk:block device:device];
                    }
                } else {
                    if (device.productKey) {
                        NSMutableDictionary *deviceDict = [NSMutableDictionary dictionaryWithDictionary:@{
                            @"macAddress":device.mac,
                            @"meshUUID":[NSNumber numberWithInteger:[DiscoveryType getType:device.devType]],
                            @"productUUID":device.productKey,
                        }];
                        if (!JK_IS_STR_NIL(device.token)) {
                            [deviceDict setObject:device.token forKey:@"token"];
                        }

                        block(deviceDict, nil);
                    }
                }
            }
        }
    }];
}

// productId 换 productKey
- (void)provisionDevicePidtoPk:(void(^)(NSDictionary *deviceDict ,NSError *err))block device:(IMLCandDeviceModel *)device {
    NSLog(@"AliLiving provisionDevicePidtoPk");

    [[IMSLifeClient sharedClient] queryProductKeyWithId:device.productId completionHandler:^(NSDictionary *data, NSError *error) {
        if (error) {
            NSLog(@"AliLiving provisionDevicePidtoPk error: %@", error);
            return;
        } else {
            NSString *productKey = data[@"productKey"];
            NSLog(@"AliLiving provisionDevicePidtoPk productKey %@", productKey);

            NSMutableDictionary *deviceDict = [NSMutableDictionary dictionaryWithDictionary:@{
                @"macAddress":device.mac,
                @"meshUUID":[NSNumber numberWithInteger:[DiscoveryType getType:device.devType]],
                @"productUUID":productKey,
            }];
            if (!JK_IS_STR_NIL(device.token)) {
                [deviceDict setObject:device.token forKey:@"token"];
            }

            block(deviceDict, nil);
        }
    }];
}

//停止设备发现
- (void)stopScanLocalDevice{
    [kLKLocalDeviceMgr stopDiscovery];
}



// 蓝牙设备上线
// breezeSubLogin 入参是 pk 和mac 通过这两个值来上线蓝牙设备，上线完后回调里面会返回 deviceName 。
// 上线后，可在生活物联网平台设备详情页面中看到状态变成了在线，
// 以及 IP 地址（实际上是自动将手机创建为一个网关，这个手机的 IP 地址），
// 如果此时退出 APP ，则设备详情页面中就会看到状态变成了离线。
// 此处上线只是用于蓝牙设备的激活，并不能用于设备跟云端交换数据。
// 在 userBindByTimeWindow 绑定结束后，蓝牙设备就会下线掉。
// 绑定后随时可以解绑。解绑跟其他配网方式的设备一样，因为
// 执行恢复出厂设置任务 https://help.aliyun.com/document_detail/177871.html
// 只是在云端解绑用户跟设备的绑定关系，不会对设备做恢复出厂设置的事情。
// 设备恢复出厂设置，需要引导用户去按物理键进行。
- (void)breezeSubDevLogin:(NSString *)productKey
               macAddress:(NSString *)macAddress
                     ssid:(NSString *)ssid
                 password:(NSString *)password
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject {
    NSDictionary *params = @{
        @"status":@"LoginingBluetoothDevice",
    };
    [self.mRnModule sendEventWithName:kDeviceStatusUpdatingMesh body:params];

    [IMSSubDeviceService subDeviceAuthenLogin:@{@"productKey":productKey ?: @"", @"deviceName":macAddress ?: @""} resultBlock:^(NSDictionary * _Nullable object, NSError * _Nullable error) {
        if (error) {
            NSLog(@"AliLiving subDeviceAuthenLogin failure: %@", error.localizedDescription);
            reject(@"", @"breezeSubDevLogin failure", error);
        } else {
            NSLog(@"AliLiving subDeviceAuthenLogin: %@", object);
            NSString *deviceName = object[@"deviceName"];
            NSLog(@"AliLiving bluetooth device login pk->%@ dn->%@", productKey, deviceName);
            NSLog(@"AliLiving comboDeviceStartBindBle");
            [[IMSLifeClient sharedClient] bindBTDeviceWithProductKey:productKey deviceName:deviceName completionHandler:^(NSDictionary *data, NSError *error) {
                if (error) {
                    NSLog(@"AliLiving userBindByTimeWindow error: %@", error);
                    reject(@"", @"userBindByTimeWindow error", error);
                } else {
                    NSString *iotId = data[@"iotId"];
                    self.iotId = iotId;

                    // 绑定成功后通知sdk，deviceName是ble的MAC地址，不是真实的deviceName
                    NSDictionary *deviceInfo = @{
                        @"deviceName": macAddress ?: @"",
                        @"productKey": productKey ?: @"",
                        @"iotId": iotId ?: @"",
                    };
                    NSLog(@"AliLiving userBindByTimeWindow %@", deviceInfo);
                    NSDictionary *params = @{
                        @"status":@"WritingBluetoothToken",
                    };
                    [self.mRnModule sendEventWithName:kDeviceStatusUpdatingMesh body:params];
                    [IMSSubDeviceService notifySubDeviceBinded:YES deviceInfo:deviceInfo completeBlock:^(BOOL succeeded, NSError * _Nullable error){
                        NSLog(@"AliLiving notifySubDeviceBinded isSuccess->%d", succeeded);
                            if (succeeded) {
                                // if wifiConnect don't wait this isSuccess,
                                // sometimes will cause
                                //     <E> err at 0x0013, code 0x0008
                                //     <E> err at 0x0016, code 0x0009
                                // in firmware printf
                                [self wifiConnect:macAddress iotId:iotId ssid:ssid password:password resolver:resolve rejecter:reject];
                            } else {
                                reject(@"", @"IMSSubDeviceService notifySubDeviceBinded failure", error);
                            }
                        }
                    ];
                }
            }];
        }
    }];
}

//添加配网设备
- (void)wifiConnect:(NSString *)mac
              iotId:(NSString *)iotId
               ssid:(NSString *)ssid
           password:(NSString *)password
           resolver:(RCTPromiseResolveBlock)resolve
           rejecter:(RCTPromiseRejectBlock)reject {
    NSLog(@"AliLiving wifiConnect comboDeviceStartDistributionNetwork mac->%@", mac);
    NSLog(@"AliLiving wifiConnect startAddDevice");

    IMLCandDeviceModel *deviceInfo = [self buildDeviceInfo:mac];

    NSInteger errorIndex = [kLkAddDevBiz setDevice:deviceInfo];

    if (errorIndex == 0) {
//        [kLkAddDevBiz setAliProvisionMode:ForceAliLinkTypeBLE];

//        [self notifyWiFiStatus:iotId status:DeviceWifiStatus_Setting];
        [kLkAddDevBiz startAddDevice:self];
    }else{
        NSError *error = [NSError errorWithDomain:@"kSetDeviceError" code:511 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"error code %ld",(long)errorIndex]}];
        NSLog(@"AliLiving wifiConnect setDevice error: %@", error);
        reject(@"", @"wifiConnect setDevice error", error);

        [kLkAddDevBiz stopAddDevice];

//        self.provisionedResultBlock(@{}, error);
    }
}

- (IMLCandDeviceModel *)buildDeviceInfo:(NSString *)mac {
    IMLCandDeviceModel *deviceInfo = [[IMLCandDeviceModel alloc] init];
    deviceInfo.devType = @"ble_subtype_3";
    deviceInfo.mac = mac;

    // 设备热点配网：ForceAliLinkTypeSoftAP
    // 蓝牙辅助配网：ForceAliLinkTypeBLE
    // 二维码配网：ForceAliLinkTypeQR
    // 手机热点配网：ForceAliLinkTypePhoneAP
    // 一键配网：ForceAliLinkTypeBroadcast
    // 零配：ForceAliLinkTypeZeroAP
    deviceInfo.linkType = ForceAliLinkTypeBLE;

    // deviceInfo.id= "xxx";// 设备热点的 ID ，在发现热点设备返回到 APP 时会携带这个字段

    // 如果当前 APP 需要全球使用，且涉及到切换账号的数据中心，配网 SDK 可以按照以下设置传递数据中心的信息
    // 平台返回服务器 ID 的值如下
    // 0：上海
    // 1：新加坡
    // 3：美国
    // 4：德国
    // 阿里的人说，IoTSmart.getShortRegionId() 这个是由账号的注册地来决定的。
    // 也就是说，注册的时候选择国家是有意义的，但是登录的时候选择国家就没有意义了。
//    LKRegionNode shortRegionId = [[[IMSIotSmart sharedInstance] shortRegionId] intValue];
    NSString *shortRegionId = [[ALBBOpenAccountSDK sharedInstance] getRegionModel].shortRegionId;
    NSLog(@"AliLiving shortRegionId: %@", shortRegionId);

    deviceInfo.enableGlobalCloudToken = YES;
    deviceInfo.regionNode = LKRegionNodeCustomID;
    deviceInfo.customRegionID = shortRegionId ?: @"0";

    return deviceInfo;
}

- (void)notifyWiFiStatus:(NSString *)iotId
                  status:(DeviceWifiStatusType)status{
    IMSThing *thing = [kIMSThingManager buildThing:iotId];
    [[thing getThingActions] setWifiStatus:status responseHandler:^(IMSThingActionsResponse * _Nullable response) {
    }];
}

// （可选）当您需要判断设备是否为Combo设备以及wifistate状态时，可调用以下代码查看
//IMSThing *thing = [kIMSThingManager buildThing:iotId];
//[[thing getThingActions] getDeviceNetTypesSupported:^(IMSThingActionsResponse * _Nullable response) {
//        NSInteger netTypes = [(NSNumber *)response.dataObject integerValue];
//    DeviceWifiStatusType type = [[thing getThingActions] getWifiStatus];
//         // DeviceWifiStatus_Init表示初始化Wi-Fi
//         // DeviceWifiStatus_Setting表示初始化设置中
//         // DeviceWifiStatus_NotSupport表示不支持设置Wi-Fi
//         // DeviceWifiStatus_NotSet表示Wi-Fi设置失败
//         // DeviceWifiStatus_Set表示Wi-Fi设置成功
//    BOOL isComboDevice = (netTypes & NET_WIFI) && (netTypes & NET_BT) && (type != DeviceWifiStatus_NotSupport);
//         // 返回YES表示Combo设备，返回NO表示非Combo的其他设备
//}];

//添加配网的wifi名和wifi密码
- (void)toggleProvisionWithName:(NSString *)wifiName
                   wifiPassword:(NSString *)wifiPassword
                        timeout:(int)timeout{
  NSLog(@"AliLiving toggleProvision s->%@ p->%@ timeout: %d", wifiName, wifiPassword, timeout);
  [kLkAddDevBiz toggleProvision:wifiName pwd:wifiPassword timeout:timeout];
}

- (void)stopToogle{
    NSLog(@"AliLiving stopToogle stopAddDevice");
    [kLkAddDevBiz stopAddDevice];
}

- (void)stopAddDevice{
    NSLog(@"AliLiving stopAddDevice stopAddDevice");
    [kLkAddDevBiz stopAddDevice];
}
//获取设备token
- (void)getDeviceTokenWithProductKey:(NSString *)productKey
                          deviceName:(NSString *)deviceName
                             timeout:(NSUInteger)timeout
                               block:(void(^)(NSString *token))block{
    [[IMLLocalDeviceMgr sharedMgr] getDeviceToken:productKey deviceName:deviceName timeout:timeout resultBlock:^(NSString *token, BOOL boolSuccess) {
        if (boolSuccess) {
            block(token);
        }else{
            block(@"");
        }
    }];
}



//配网流程代理回调
#pragma mark - ILKAddDeviceNotifier
//预配网结果回调
- (void)notifyPrecheck:(BOOL)success withError:(NSError *)err
{
  self.preCheckBlock(success, err);
}
//配网准备
- (void)notifyProvisionPrepare:(LKPUserGuideCode)guideCode{
  self.provisionPrepareBlock(guideCode);
}
//配网中。。。
-(void)notifyProvisioning
{
  self.provisioningBlock();
}
//阶段性配网状态回调
//- (void)notifyProvisionStatus:(LKProvisonStatus)provisionStatus boolSuccess:(BOOL)boolSuccess;
//{
//  self.provisionStatusBlock(@{@"provisionStatus":@(provisionStatus),@"boolSuccess":@(boolSuccess)});
//}
/**
 手机热点配网模式相关回调
 通知上层UI：提示用户关闭手机热点并切回以前的wifi
 */
- (void)notifyProvisioningNotice {
    self.provisionStatusPhoneApBlock();
}


/**
 设备热点配网模式相关回调
 通知上层UI：热点配网相关回调提示
 只有需要提示用户需要手动连接设备热点或者恢复Wi-Fi连接的场景才会回调

 @param status 状态码：
        1=应该切换到设备热点；
        2=已经切换到设备热点
        3=已发送数据。dic里面会有"token"、"service"等信息,可能支持的service: @"ErrCode",@"OfflineOTA",@"OfflineLog",@"BatchEnrollee"
        4=应该切换回路由器 5=已经切换回路由器
 */
- (void)notifyProvisioningNoticeForSoftAp:(int) status withInfo:(NSDictionary *) dic {
    self.provisionStatusSoftApBlock(status, dic);
}


/**
 蓝牙辅助配网模式相关回调
 通知上层UI：蓝牙辅助配网相关回掉提示

 @param status 状态码：
 1=还未找到蓝牙设备，请检查设备是否初始化；
 2=已经找到蓝牙设备
 3=设备已经成功接收到ssid,password信息；
 @param dic 状态信息：
 status=2，dic 可能为：{@"devType":@"ble_subtype_3",@"mac":@"xxxxxxx"} 代表支持先绑定控制后配置WiFi信息的蓝牙Combo设备，可先执行绑定控制，之后调用continueProvision接口继续配网流程。
 */
- (void)notifyProvisioningNoticeForBleConfig:(int) status withInfo:(NSDictionary *) dic {
    self.provisionStatusBleConfigBlock(status, dic);
}

/**
 视频类二维码配网模式相关回调
 注意：返回二维码时已开启监听设备是否已配网成功的通告，并开始计时，UI端应提示用户尽快扫码，
 如果在指定时间配网超时了，重新调用开始配网流程并刷新二维码。

 @param qrcode 需要UI展现的二维码内容
 */
- (void)notifyProvisioningNoticeForQR:(NSString *) qrcode {
    self.provisionStatusQRBlock(qrcode);
}


//配网结果回调
- (void)notifyProvisionResult:(IMLCandDeviceModel *)candDeviceModel withProvisionError:(NSError *)provisionError
{
    self.provisionedResultBlock(candDeviceModel, provisionError);
}

@end
