#import "ExtSdkToJson.h"

#import <AgoraChat/AgoraChatClient.h>
#import <AgoraChat/AgoraChatOptions+PrivateDeploy.h>

@implementation AgoraChatroom (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"roomId"] = self.chatroomId;
    ret[@"roomName"] = self.subject;
    ret[@"description"] = self.description;
    ret[@"owner"] = self.owner;
    ret[@"maxUsers"] = @(self.maxOccupantsCount);
    ret[@"memberCount"] = @(self.occupantsCount);
    ret[@"adminList"] = self.adminList;
    ret[@"memberList"] = self.memberList;
    ret[@"blockList"] = self.blacklist;
    ret[@"muteList"] = self.muteList;
    ret[@"muteKVList"] = self.muteMembers;
    ret[@"isAllMemberMuted"] = @(self.isMuteAllMembers);
    ret[@"announcement"] = self.announcement;
    ret[@"permissionType"] = @([self premissionTypeToInt:self.permissionType]);
    ret[@"isInWhitelist"] = @(self.isInWhitelist);
    ret[@"createTimestamp"] = @(self.createTimestamp);
    ret[@"muteExpireTimestamp"] = @(self.muteExpireTimestamp);

    return ret;
}

- (int)premissionTypeToInt:(AgoraChatroomPermissionType)type {
    int ret = -1;
    switch (type) {
    case AgoraChatroomPermissionTypeNone: {
        ret = -1;
    } break;
    case AgoraChatroomPermissionTypeMember: {
        ret = 0;
    } break;
    case AgoraChatroomPermissionTypeAdmin: {
        ret = 1;
    } break;
    case AgoraChatroomPermissionTypeOwner: {
        ret = 2;
    } break;
    default:
        break;
    }
    return ret;
}
@end

@implementation AgoraChatConversation (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"convId"] = self.conversationId;
    ret[@"convType"] = @([self.class typeToInt:self.type]);
    ret[@"isChatThread"] = @(self.isChatThread);
    ret[@"isPinned"] = @(self.isPinned);
    ret[@"pinnedTime"] = @(self.pinnedTime);
    ret[@"ext"] = self.ext;
    ret[@"marks"] = self.marks;
    ret[@"remindType"] = @(self.disturbType);
    return ret;
}

+ (int)typeToInt:(AgoraChatConversationType)aType {
    int ret = 0;
    switch (aType) {
    case AgoraChatConversationTypeChat:
        ret = 0;
        break;
    case AgoraChatConversationTypeGroupChat:
        ret = 1;
        break;
    case AgoraChatConversationTypeChatRoom:
        ret = 2;
        break;
    default:
        break;
    }
    return ret;
}

+ (AgoraChatConversationType)typeFromInt:(int)aType {
    AgoraChatConversationType ret = AgoraChatConversationTypeChat;
    switch (aType) {
    case 0:
        ret = AgoraChatConversationTypeChat;
        break;
    case 1:
        ret = AgoraChatConversationTypeGroupChat;
        break;
    case 2:
        ret = AgoraChatConversationTypeChatRoom;
        break;
    default:
        break;
    }
    return ret;
}

@end

@implementation AgoraChatCursorResult (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    NSMutableArray *dataList = [NSMutableArray array];

    for (id obj in self.list) {
        if ([obj respondsToSelector:@selector(toJsonObject)]) {
            [dataList addObject:[obj toJsonObject]];
        } else if ([obj isKindOfClass:[NSString class]]) {
            [dataList addObject:obj];
        }
    }

    data[@"list"] = dataList;
    data[@"cursor"] = self.cursor;

    return data;
}
@end

@implementation AgoraChatDeviceConfig (Json)
- (NSDictionary *)toJsonObject {
    return @{
        @"resource" : self.resource,
        @"deviceUUID" : self.deviceUUID,
        @"deviceName" : self.deviceName,
    };
}
@end

@implementation AgoraChatError (Json)
- (NSDictionary *)toJsonObject {
    return @{
        @"code" : @(self.code),
        @"description" : self.errorDescription,
    };
}
@end

@implementation AgoraChatGroup (Json)

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"groupId"] = self.groupId;
    ret[@"groupName"] = self.groupName;
    ret[@"description"] = self.description;
    ret[@"owner"] = self.owner;
    ret[@"announcement"] = self.announcement;
    ret[@"memberCount"] = @(self.occupantsCount);
    ret[@"memberList"] = self.memberList;
    ret[@"adminList"] = self.adminList;
    ret[@"blockList"] = self.blacklist;
    ret[@"muteList"] = self.muteList;
    ret[@"noticeEnable"] = @(self.isPushNotificationEnabled);
    ret[@"messageBlocked"] = @(self.isBlocked);
    ret[@"isAllMemberMuted"] = @(self.isMuteAllMembers);
    ret[@"permissionType"] =
        @([AgoraChatGroup premissionTypeToInt:self.permissionType]);

    if (self.settings != nil) {
        NSMutableDictionary *opt = [NSMutableDictionary dictionary];
        opt[@"maxCount"] = @(self.settings.maxUsers);
        opt[@"style"] = @(self.settings.style);
        opt[@"inviteNeedConfirm"] = @([self isMemberAllowToInvite]);
        opt[@"ext"] = self.settings.ext;
        opt[@"isDisabled"] = @(self.isDisabled);
        opt[@"isMemberOnly"] = @([self isMemberOnly]);
        ret[@"options"] = opt;
    }

    return ret;
}

- (BOOL)isMemberOnly {

    if (self.settings.style == AgoraChatGroupStylePrivateOnlyOwnerInvite ||
        self.settings.style == AgoraChatGroupStylePrivateMemberCanInvite ||
        self.settings.style == AgoraChatGroupStylePublicJoinNeedApproval) {
        return YES;
    }

    return NO;
}

- (BOOL)isMemberAllowToInvite {
    return self.settings.style == AgoraChatGroupStylePrivateMemberCanInvite;
}

+ (int)premissionTypeToInt:(AgoraChatGroupPermissionType)type {
    int ret = -1;
    switch (type) {
    case AgoraChatGroupPermissionTypeNone: {
        ret = -1;
    } break;
    case AgoraChatGroupPermissionTypeMember: {
        ret = 0;
    } break;
    case AgoraChatGroupPermissionTypeAdmin: {
        ret = 1;
    } break;
    case AgoraChatGroupPermissionTypeOwner: {
        ret = 2;
    } break;
    default:
        break;
    }
    return ret;
}

+ (AgoraChatGroupPermissionType)premissionTypeFromInt:(int)type {
    AgoraChatGroupPermissionType ret = AgoraChatGroupPermissionTypeMember;
    switch (type) {
    case -1: {
        ret = AgoraChatGroupPermissionTypeNone;
    } break;
    case 0: {
        ret = AgoraChatGroupPermissionTypeMember;
    } break;
    case 1: {
        ret = AgoraChatGroupPermissionTypeAdmin;
    } break;
    case 2: {
        ret = AgoraChatGroupPermissionTypeOwner;
    } break;
    default:
        break;
    }
    return ret;
}

@end

@implementation AgoraChatGroupOptions (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"maxCount"] = @(self.maxUsers);
    ret[@"ext"] = self.ext;
    ret[@"style"] = @([AgoraChatGroupOptions styleToInt:self.style]);
    ret[@"inviteNeedConfirm"] = @(self.IsInviteNeedConfirm);
    return ret;
}

+ (AgoraChatGroupOptions *)fromJsonObject:(NSDictionary *)dict {
    AgoraChatGroupOptions *options = [[AgoraChatGroupOptions alloc] init];
    options.maxUsers = [dict[@"maxCount"] intValue];
    options.ext = dict[@"ext"];
    options.IsInviteNeedConfirm = [dict[@"inviteNeedConfirm"] boolValue];
    options.style = [AgoraChatGroupOptions styleFromInt:[dict[@"style"] intValue]];
    return options;
}

+ (AgoraChatGroupStyle)styleFromInt:(int)style {
    AgoraChatGroupStyle ret = AgoraChatGroupStylePrivateOnlyOwnerInvite;
    switch (style) {
    case 0: {
        ret = AgoraChatGroupStylePrivateOnlyOwnerInvite;
    } break;
    case 1: {
        ret = AgoraChatGroupStylePrivateMemberCanInvite;
    } break;
    case 2: {
        ret = AgoraChatGroupStylePublicJoinNeedApproval;
    } break;
    case 3: {
        ret = AgoraChatGroupStylePublicOpenJoin;
    } break;
    default:
        break;
    }

    return ret;
}

+ (int)styleToInt:(AgoraChatGroupStyle)style {
    int ret = 0;
    switch (style) {
    case AgoraChatGroupStylePrivateOnlyOwnerInvite: {
        ret = 0;
    } break;
    case AgoraChatGroupStylePrivateMemberCanInvite: {
        ret = 1;
    } break;
    case AgoraChatGroupStylePublicJoinNeedApproval: {
        ret = 2;
    } break;
    case AgoraChatGroupStylePublicOpenJoin: {
        ret = 3;
    } break;
    default:
        break;
    }

    return ret;
}

@end

@implementation AgoraChatGroupSharedFile (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"fileId"] = self.fileId;
    data[@"name"] = self.fileName;
    data[@"owner"] = self.fileOwner;
    data[@"createTime"] = @(self.createdAt);
    data[@"fileSize"] = @(self.fileSize);
    return data;
}

@end

@implementation AgoraChatGroupMessageAck (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"msg_id"] = self.messageId;
    data[@"ack_id"] = self.readAckId;
    data[@"from"] = self.from;
    data[@"content"] = self.content;
    data[@"count"] = @(self.readCount);
    data[@"timestamp"] = @(self.timestamp);
    return data;
}
@end

@interface LocalFileHandler : NSObject

+ (NSString *)reset:(NSString *)localPath;

@end

@implementation LocalFileHandler

+ (NSString *)reset:(NSString *)localPath {
#if !TARGET_OS_SIMULATOR
    NSRange range = [localPath rangeOfString:@"/Library/"
                                     options:NSBackwardsSearch];
    NSRange range2 = [localPath rangeOfString:@"file://"
                                      options:NSAnchoredSearch];
    if (range.location == NSNotFound && range2.location != NSNotFound) {
        return [localPath
            stringByReplacingCharactersInRange:NSMakeRange(0, range2.length)
                                    withString:@""];

    } else {
        return localPath;
    }
#else
    return localPath;
#endif
}

@end

@implementation AgoraChatMessage (Json)

+ (AgoraChatMessage *)fromJsonObject:(NSDictionary *)aJson {
    AgoraChatMessageBody *body = [AgoraChatMessageBody fromJsonObject:aJson[@"body"]];
    if (!body) {
        return nil;
    }

    NSString *from = aJson[@"from"];
    if (from.length == 0) {
        from = AgoraChatClient.sharedClient.currentUsername;
    }

    NSString *to = aJson[@"to"];
    NSString *conversationId = aJson[@"conversationId"];

    AgoraChatMessage *msg =
        [[AgoraChatMessage alloc] initWithConversationID:conversationId
                                                 from:from
                                                   to:to
                                                 body:body
                                                  ext:nil];
    if (aJson[@"msgId"]) {
        msg.messageId = aJson[@"msgId"];
    }

    msg.direction = ({
        [aJson[@"direction"] isEqualToString:@"send"]
            ? AgoraChatMessageDirectionSend
            : AgoraChatMessageDirectionReceive;
    });

    msg.chatType =
        [AgoraChatMessage chatTypeFromInt:[aJson[@"chatType"] intValue]];
    msg.status = [msg statusFromInt:[aJson[@"status"] intValue]];
    msg.localTime = [aJson[@"localTime"] longLongValue];
    msg.timestamp = [aJson[@"serverTime"] longLongValue];
    msg.isReadAcked = [aJson[@"hasReadAck"] boolValue];
    msg.isDeliverAcked = [aJson[@"hasDeliverAck"] boolValue];
    msg.isRead = [aJson[@"hasRead"] boolValue];
    msg.isNeedGroupAck = [aJson[@"needGroupAck"] boolValue];
    // read only
    // msg.groupAckCount = [aJson[@"groupAckCount"] intValue]
    // msg.isContentReplaced = [aJson[@"isContentReplaced"] boolValue];
    msg.isChatThreadMessage = [aJson[@"isChatThread"] boolValue];
    msg.ext = aJson[@"attributes"];
    msg.priority =
        [AgoraChatMessage priorityFromInt:[aJson[@"priority"] intValue]];
    msg.deliverOnlineOnly = [aJson[@"deliverOnlineOnly"] boolValue];
    if (aJson[@"receiverList"]) {
        msg.receiverList = aJson[@"receiverList"];
    }
    return msg;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"from"] = self.from;
    ret[@"msgId"] = self.messageId;
    ret[@"to"] = self.to;
    ret[@"conversationId"] = self.conversationId;
    ret[@"hasRead"] = @(self.isRead);
    ret[@"hasDeliverAck"] = @(self.isDeliverAcked);
    ret[@"hasReadAck"] = @(self.isReadAcked);
    ret[@"needGroupAck"] = @(self.isNeedGroupAck);
    ret[@"serverTime"] = @(self.timestamp);
    ret[@"groupAckCount"] = @(self.groupAckCount);
    ret[@"attributes"] = self.ext ?: @{};
    ret[@"localTime"] = @(self.localTime);
    ret[@"status"] = @([self statusToInt:self.status]);
    ret[@"chatType"] = @([AgoraChatMessage chatTypeToInt:self.chatType]);
    ret[@"direction"] =
        self.direction == AgoraChatMessageDirectionSend ? @"send" : @"rec";
    ret[@"body"] = [self.body toJsonObject];
    ret[@"isChatThread"] = @(self.isChatThreadMessage);
    ret[@"isOnline"] = @(self.onlineState);
    ret[@"priority"] = @([AgoraChatMessage priorityToInt:self.priority]);
    ret[@"deliverOnlineOnly"] = @(self.deliverOnlineOnly);
    ret[@"receiverList"] = self.receiverList;
    ret[@"isBroadcast"] = @(self.broadcast);
    ret[@"isContentReplaced"] = @(self.isContentReplaced);

    return ret;
}

- (AgoraChatMessageStatus)statusFromInt:(int)aStatus {
    AgoraChatMessageStatus status = AgoraChatMessageStatusPending;
    switch (aStatus) {
    case 0: {
        status = AgoraChatMessageStatusPending;
    } break;
    case 1: {
        status = AgoraChatMessageStatusDelivering;
    } break;
    case 2: {
        status = AgoraChatMessageStatusSucceed;
    } break;
    case 3: {
        status = AgoraChatMessageStatusFailed;
    } break;
    }

    return status;
}

- (int)statusToInt:(AgoraChatMessageStatus)aStatus {
    int status = 0;
    switch (aStatus) {
    case AgoraChatMessageStatusPending: {
        status = 0;
    } break;
    case AgoraChatMessageStatusDelivering: {
        status = 1;
    } break;
    case AgoraChatMessageStatusSucceed: {
        status = 2;
    } break;
    case AgoraChatMessageStatusFailed: {
        status = 3;
    } break;
    }

    return status;
}

+ (AgoraChatType)chatTypeFromInt:(int)aType {
    AgoraChatType type = AgoraChatTypeChat;
    switch (aType) {
    case 0:
        type = AgoraChatTypeChat;
        break;
    case 1:
        type = AgoraChatTypeGroupChat;
        break;
    case 2:
        type = AgoraChatTypeChatRoom;
        break;
    }

    return type;
}

+ (int)chatTypeToInt:(AgoraChatType)aType {
    int type;
    switch (aType) {
    case AgoraChatTypeChat:
        type = 0;
        break;
    case AgoraChatTypeGroupChat:
        type = 1;
        break;
    case AgoraChatTypeChatRoom:
        type = 2;
        break;
    }
    return type;
}

+ (AgoraChatRoomMessagePriority)priorityFromInt:(int)priority {
    AgoraChatRoomMessagePriority ret = AgoraChatRoomMessagePriorityNormal;
    switch (priority) {
    case 0:
        ret = AgoraChatRoomMessagePriorityHigh;
        break;
    case 1:
        ret = AgoraChatRoomMessagePriorityNormal;
        break;
    case 2:
        ret = AgoraChatRoomMessagePriorityLow;
        break;
    }

    return ret;
}
+ (int)priorityToInt:(AgoraChatRoomMessagePriority)priority {
    int ret;
    switch (priority) {
    case AgoraChatRoomMessagePriorityHigh:
        ret = 0;
        break;
    case AgoraChatRoomMessagePriorityNormal:
        ret = 1;
        break;
    case AgoraChatRoomMessagePriorityLow:
        ret = 2;
        break;
    }
    return ret;
}

@end

@implementation AgoraChatMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)bodyJson {
    AgoraChatMessageBody *ret = nil;
    NSString *type = bodyJson[@"type"];
    if ([type isEqualToString:@"txt"]) {
        ret = [AgoraChatTextMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"img"]) {
        ret = [AgoraChatImageMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"loc"]) {
        ret = [AgoraChatLocationMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"video"]) {
        ret = [AgoraChatVideoMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"voice"]) {
        ret = [AgoraChatVoiceMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"file"]) {
        ret = [AgoraChatFileMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"cmd"]) {
        ret = [AgoraChatCmdMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"custom"]) {
        ret = [AgoraChatCustomMessageBody fromJsonObject:bodyJson];
    } else if ([type isEqualToString:@"combine"]) {
        ret = [AgoraChatCombineMessageBody fromJsonObject:bodyJson];
    }
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    NSString *type = [AgoraChatMessageBody toString:self.type];
    ret[@"type"] = type;
    if (self.operatorId && self.operatorId.length > 0) {
        ret[@"lastModifyOperatorId"] = self.operatorId;
        ret[@"lastModifyTime"] = @(self.operationTime);
        ret[@"modifyCount"] = @(self.operatorCount);
    }

    return ret;
}

+ (AgoraChatMessageBodyType)fromString:(NSString *)aStrType {

    AgoraChatMessageBodyType ret = AgoraChatMessageBodyTypeText;

    if ([aStrType isEqualToString:@"txt"]) {
        ret = AgoraChatMessageBodyTypeText;
    } else if ([aStrType isEqualToString:@"loc"]) {
        ret = AgoraChatMessageBodyTypeLocation;
    } else if ([aStrType isEqualToString:@"cmd"]) {
        ret = AgoraChatMessageBodyTypeCmd;
    } else if ([aStrType isEqualToString:@"custom"]) {
        ret = AgoraChatMessageBodyTypeCustom;
    } else if ([aStrType isEqualToString:@"file"]) {
        ret = AgoraChatMessageBodyTypeFile;
    } else if ([aStrType isEqualToString:@"img"]) {
        ret = AgoraChatMessageBodyTypeImage;
    } else if ([aStrType isEqualToString:@"video"]) {
        ret = AgoraChatMessageBodyTypeVideo;
    } else if ([aStrType isEqualToString:@"voice"]) {
        ret = AgoraChatMessageBodyTypeVoice;
    } else if ([aStrType isEqualToString:@"combine"]) {
        ret = AgoraChatMessageBodyTypeCombine;
    }
    return ret;
}

+ (NSString *)toString:(AgoraChatMessageBodyType)type {
    NSString *ret = @"txt";
    switch (type) {
    case AgoraChatMessageBodyTypeText:
        ret = @"txt";
        break;
    case AgoraChatMessageBodyTypeLocation:
        ret = @"loc";
        break;
    case AgoraChatMessageBodyTypeCmd:
        ret = @"cmd";
        break;
    case AgoraChatMessageBodyTypeCustom:
        ret = @"custom";
        break;
    case AgoraChatMessageBodyTypeFile:
        ret = @"file";
        break;
    case AgoraChatMessageBodyTypeImage:
        ret = @"img";
        break;
    case AgoraChatMessageBodyTypeVideo:
        ret = @"video";
        break;
    case AgoraChatMessageBodyTypeVoice:
        ret = @"voice";
        break;
    case AgoraChatMessageBodyTypeCombine:
        ret = @"combine";
        break;
    default:
        break;
    }

    return ret;
}

@end

#pragma mark - txt

@interface AgoraChatTextMessageBody (Json)
+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatTextMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    AgoraChatTextMessageBody *body =
        [[AgoraChatTextMessageBody alloc] initWithText:aJson[@"content"]];
    body.targetLanguages = aJson[@"targetLanguageCodes"];
    // 给底层的时候不需要设置
    return body;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"content"] = self.text;
    ret[@"targetLanguageCodes"] = self.targetLanguages;
    NSMutableDictionary *kv = [NSMutableDictionary dictionary];
    for (NSString *key in self.translations.allKeys) {
        [kv setValue:[self.translations valueForKey:key] forKey:key];
    }
    if (kv != nil) {
        ret[@"translations"] = kv;
    }
    return ret;
}

@end

#pragma mark - loc

@interface AgoraChatLocationMessageBody (Json)
+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatLocationMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    double latitude = [aJson[@"latitude"] doubleValue];
    double longitude = [aJson[@"longitude"] doubleValue];
    NSString *address = aJson[@"address"];
    NSString *buildingName = aJson[@"buildingName"];
    AgoraChatLocationMessageBody *ret =
        [[AgoraChatLocationMessageBody alloc] initWithLatitude:latitude
                                              longitude:longitude
                                                address:address
                                           buildingName:buildingName];
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"address"] = self.address;
    ret[@"latitude"] = @(self.latitude);
    ret[@"longitude"] = @(self.longitude);
    ret[@"buildingName"] = self.buildingName;
    return ret;
}

@end

#pragma mark - cmd

@interface AgoraChatCmdMessageBody (Json)
+ (AgoraChatCmdMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatCmdMessageBody (Json)

+ (AgoraChatCmdMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    AgoraChatCmdMessageBody *ret =
        [[AgoraChatCmdMessageBody alloc] initWithAction:aJson[@"action"]];
    //    ret.isDeliverOnlineOnly = [aJson[@"deliverOnlineOnly"] boolValue];
    ret.action = aJson[@"action"];
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"action"] = self.action;
    //    ret[@"deliverOnlineOnly"] = @(self.isDeliverOnlineOnly);
    return ret;
}

@end

#pragma mark - custom

@interface AgoraChatCustomMessageBody (Json)
+ (AgoraChatCustomMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatCustomMessageBody (Json)

+ (AgoraChatCustomMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    NSDictionary *dic = aJson[@"params"];
    if ([dic isKindOfClass:[NSNull class]]) {
        dic = nil;
    } else if ([dic isKindOfClass:[NSString class]]) {
        NSError *err = nil;
        NSData *jsonData =
            [(NSString *)dic dataUsingEncoding:NSUTF8StringEncoding];
        id obj = [NSJSONSerialization
            JSONObjectWithData:jsonData
                       options:NSJSONReadingMutableContainers
                         error:&err];
        if (err == nil && obj != nil) {
            dic = (NSDictionary *)obj;
        } else {
            dic = nil;
        }
    }

    AgoraChatCustomMessageBody *ret =
        [[AgoraChatCustomMessageBody alloc] initWithEvent:aJson[@"event"]
                                         customExt:dic];
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"event"] = self.event;
    ret[@"params"] = self.customExt;
    return ret;
}

@end

#pragma mark - combine

@interface AgoraChatCombineMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;

@end

@implementation AgoraChatCombineMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson {

    NSString *title = aJson[@"title"];
    NSString *summary = aJson[@"summary"];
    NSArray *messageIdList = aJson[@"messageIdList"];
    NSString *compatibleText = aJson[@"compatibleText"];
    NSString *localPath = aJson[@"localPath"];
    NSString *remotePath = aJson[@"remotePath"];
    NSString *secret = aJson[@"secret"];

    AgoraChatCombineMessageBody *ret =
        [[AgoraChatCombineMessageBody alloc] initWithTitle:title
                                            summary:summary
                                     compatibleText:compatibleText
                                      messageIdList:messageIdList];

    ret.remotePath = remotePath;
    ret.secretKey = secret;
    ret.localPath = localPath;
    return ret;
}
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"title"] = self.title;
    ret[@"summary"] = self.summary;
    ret[@"compatibleText"] = self.compatibleText;
    ret[@"localPath"] = self.localPath;
    ret[@"remotePath"] = self.remotePath;
    ret[@"secret"] = self.secretKey;
    // ret[@"messageIdList"] = self.messageIdList;
    return ret;
}

@end

#pragma mark - file

@interface AgoraChatFileMessageBody (Json)
+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatFileMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    NSString *path = aJson[@"localPath"];
    NSString *displayName = aJson[@"displayName"];
    AgoraChatFileMessageBody *ret = [[AgoraChatFileMessageBody alloc]
        initWithLocalPath:[LocalFileHandler reset:path]
              displayName:displayName];
    ret.secretKey = aJson[@"secret"];
    ret.remotePath = aJson[@"remotePath"];
    ret.fileLength = [aJson[@"fileSize"] longLongValue];
    ret.downloadStatus =
        [ret downloadStatusFromInt:[aJson[@"fileStatus"] intValue]];
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"localPath"] = self.localPath;
    ret[@"displayName"] = self.displayName;
    ret[@"secret"] = self.secretKey;
    ret[@"remotePath"] = self.remotePath;
    ret[@"fileSize"] = @(self.fileLength);
    ret[@"fileStatus"] = @([self downloadStatusToInt:self.downloadStatus]);
    return ret;
}

- (AgoraChatDownloadStatus)downloadStatusFromInt:(int)aStatus {
    AgoraChatDownloadStatus ret = AgoraChatDownloadStatusPending;
    switch (aStatus) {
    case 0:
        ret = AgoraChatDownloadStatusDownloading;
        break;
    case 1:
        ret = AgoraChatDownloadStatusSucceed;
        break;
    case 2:
        ret = AgoraChatDownloadStatusFailed;
        break;
    case 3:
        ret = AgoraChatDownloadStatusPending;
        break;
    default:
        break;
    }

    return ret;
}

- (int)downloadStatusToInt:(AgoraChatDownloadStatus)aStatus {
    int ret = 0;
    switch (aStatus) {
    case AgoraChatDownloadStatusDownloading:
        ret = 0;
        break;
    case AgoraChatDownloadStatusSucceed:
        ret = 1;
        break;
    case AgoraChatDownloadStatusFailed:
        ret = 2;
        break;
    case AgoraChatDownloadStatusPending:
        ret = 3;
        break;
    default:
        break;
    }
    return ret;
}

@end

#pragma mark - img

@interface AgoraChatImageMessageBody (Json)
+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatImageMessageBody (Json)

+ (AgoraChatMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    NSString *path = aJson[@"localPath"];
    NSString *displayName = aJson[@"displayName"];
    //    NSData *imageData = [NSData dataWithContentsOfFile:path];
    //    AgoraChatImageMessageBody *ret =
    //        [[AgoraChatImageMessageBody alloc] initWithData:imageData
    //                                     displayName:displayName];
    AgoraChatImageMessageBody *ret = [[AgoraChatImageMessageBody alloc]
        initWithLocalPath:[LocalFileHandler reset:path]
              displayName:displayName];

    ret.secretKey = aJson[@"secret"];
    ret.remotePath = aJson[@"remotePath"];
    ret.fileLength = [aJson[@"fileSize"] longLongValue];
    ret.downloadStatus =
        [ret downloadStatusFromInt:[aJson[@"fileStatus"] intValue]];
    ret.thumbnailLocalPath = aJson[@"thumbnailLocalPath"];
    ret.thumbnailRemotePath = aJson[@"thumbnailRemotePath"];
    ret.thumbnailSecretKey = aJson[@"thumbnailSecret"];
    ret.size =
        CGSizeMake([aJson[@"width"] floatValue], [aJson[@"height"] floatValue]);
    ret.thumbnailDownloadStatus =
        [ret downloadStatusFromInt:[aJson[@"thumbnailStatus"] intValue]];
    ret.compressionRatio = [aJson[@"sendOriginalImage"] boolValue] ? 1.0 : 0.6;
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"thumbnailLocalPath"] = self.thumbnailLocalPath;
    ret[@"thumbnailRemotePath"] = self.thumbnailRemotePath;
    ret[@"thumbnailSecret"] = self.thumbnailSecretKey;
    ret[@"thumbnailStatus"] =
        @([self downloadStatusToInt:self.thumbnailDownloadStatus]);
    ret[@"fileStatus"] = @([self downloadStatusToInt:self.downloadStatus]);
    ret[@"width"] = @(self.size.width);
    ret[@"height"] = @(self.size.height);
    ret[@"fileSize"] = @(self.fileLength);
    ret[@"remotePath"] = self.remotePath;
    ret[@"secret"] = self.secretKey;
    ret[@"displayName"] = self.displayName;
    ret[@"localPath"] = self.localPath;
    ret[@"sendOriginalImage"] = self.compressionRatio == 1.0 ? @(YES) : @(NO);
    return ret;
}
@end

#pragma mark - video

@interface AgoraChatVideoMessageBody (Json)
+ (AgoraChatVideoMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatVideoMessageBody (Json)
+ (AgoraChatVideoMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    NSString *path = aJson[@"localPath"];
    NSString *displayName = aJson[@"displayName"];
    AgoraChatVideoMessageBody *ret = [[AgoraChatVideoMessageBody alloc]
        initWithLocalPath:[LocalFileHandler reset:path]
              displayName:displayName];
    ret.duration = [aJson[@"duration"] intValue];
    ret.secretKey = aJson[@"secret"];
    ret.remotePath = aJson[@"remotePath"];
    ret.fileLength = [aJson[@"fileSize"] longLongValue];
    ret.thumbnailLocalPath = aJson[@"thumbnailLocalPath"];
    ret.thumbnailRemotePath = aJson[@"thumbnailRemotePath"];
    ret.thumbnailSecretKey = aJson[@"thumbnailSecret"];
    ret.thumbnailDownloadStatus =
        [ret downloadStatusFromInt:[aJson[@"thumbnailStatus"] intValue]];
    ret.thumbnailSize =
        CGSizeMake([aJson[@"width"] floatValue], [aJson[@"height"] floatValue]);
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"duration"] = @(self.duration);
    ret[@"thumbnailLocalPath"] = self.thumbnailLocalPath;
    ret[@"secret"] = self.secretKey;
    ret[@"remotePath"] = self.remotePath;
    ret[@"thumbnailRemotePath"] = self.thumbnailRemotePath;
    ret[@"thumbnailSecretKey"] = self.thumbnailSecretKey;
    ret[@"thumbnailStatus"] =
        @([self downloadStatusToInt:self.thumbnailDownloadStatus]);
    ret[@"width"] = @(self.thumbnailSize.width);
    ret[@"height"] = @(self.thumbnailSize.height);
    ret[@"fileSize"] = @(self.fileLength);
    ret[@"displayName"] = self.displayName;
    ret[@"duration"] = @(self.duration);
    return ret;
}
@end

#pragma mark - voice

@interface AgoraChatVoiceMessageBody (Json)
+ (AgoraChatVoiceMessageBody *)fromJsonObject:(NSDictionary *)aJson;
- (NSDictionary *)toJsonObject;
@end

@implementation AgoraChatVoiceMessageBody (Json)
+ (AgoraChatVoiceMessageBody *)fromJsonObject:(NSDictionary *)aJson {
    NSString *path = aJson[@"localPath"];
    NSString *displayName = aJson[@"displayName"];
    AgoraChatVoiceMessageBody *ret = [[AgoraChatVoiceMessageBody alloc]
        initWithLocalPath:[LocalFileHandler reset:path]
              displayName:displayName];
    ret.secretKey = aJson[@"secret"];
    ret.remotePath = aJson[@"remotePath"];
    ret.duration = [aJson[@"duration"] intValue];
    ret.downloadStatus =
        [ret downloadStatusFromInt:[aJson[@"fileStatus"] intValue]];
    return ret;
}

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[super toJsonObject] mutableCopy];
    ret[@"duration"] = @(self.duration);
    ret[@"displayName"] = self.displayName;
    ret[@"localPath"] = self.localPath;
    ret[@"fileSize"] = @(self.fileLength);
    ret[@"secret"] = self.secretKey;
    ret[@"remotePath"] = self.remotePath;
    ret[@"fileStatus"] = @([self downloadStatusToInt:self.downloadStatus]);
    ;
    return ret;
}

@end

@implementation AgoraChatOptions (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"appKey"] = self.appkey;
    data[@"autoLogin"] = @(self.isAutoLogin);
    data[@"debugModel"] = @(self.enableConsoleLog);
    data[@"requireAck"] = @(self.enableRequireReadAck);
    data[@"requireDeliveryAck"] = @(self.enableDeliveryAck);
    data[@"sortMessageByServerTime"] = @(self.sortMessageByServerTime);
    data[@"acceptInvitationAlways"] = @(self.autoAcceptFriendInvitation);
    data[@"autoAcceptGroupInvitation"] = @(self.autoAcceptGroupInvitation);
    data[@"deleteMessagesAsExitGroup"] = @(self.deleteMessagesOnLeaveGroup);
    data[@"deleteMessagesAsExitChatRoom"] =
        @(self.deleteMessagesOnLeaveChatroom);
    data[@"isAutoDownload"] = @(self.autoDownloadThumbnail);
    data[@"isChatRoomOwnerLeaveAllowed"] = @(self.canChatroomOwnerLeave);
    data[@"serverTransfer"] = @(self.isAutoTransferMessageAttachments);
    data[@"usingHttpsOnly"] = @(self.usingHttpsOnly);
    data[@"pushConfig"] = @{@"pushConfig" : @{@"deviceId" : self.apnsCertName}};
    data[@"enableDNSConfig"] = @(self.enableDnsConfig);
    data[@"imPort"] = @(self.chatPort);
    data[@"imServer"] = self.chatServer;
    data[@"restServer"] = self.restServer;
    data[@"dnsUrl"] = self.dnsURL;
    data[@"areaCode"] = @(self.area);
    data[@"enableEmptyConversation"] = @(self.loadEmptyConversations);
    data[@"customDeviceName"] = self.customDeviceName;
    data[@"customOSType"] = @(self.customOSType);
    data[@"useReplacedMessageContents"] = @(self.useReplacedMessageContents);
    data[@"enableTLS"] = @(self.enableTLSConnection);
    data[@"messagesReceiveCallbackIncludeSend"] =
        @(self.includeSendMessageInMessageListener);
    data[@"regardImportMessagesAsRead"] = @(self.regardImportMessagesAsRead);
    data[@"loginExtraInfo"] = self.loginExtensionInfo;
    data[@"workPathCopiable"] = @(self.workPathCopiable);
    data[@"appId"] = self.appId;

    return data;
}
+ (AreaCode)AreaCodeFromInt:(int)code {
    AreaCode ret = AreaCodeGLOB;
    switch (code) {
    case 1 << 0:
        ret = AreaCodeCN;
        break;
    case 1 << 1:
        ret = AreaCodeNA;
        break;
    case 1 << 2:
        ret = AreaCodeEU;
        break;
    case 1 << 3:
        ret = AreaCodeAS;
        break;
    case 1 << 4:
        ret = AreaCodeJP;
        break;
    case 1 << 5:
        ret = AreaCodeIN;
        break;
    default:
        ret = AreaCodeGLOB;
        break;
    }
    return ret;
}
+ (AgoraChatOptions *)fromJsonObject:(NSDictionary *)aJson {
    NSString *appKey = aJson[@"appKey"];
    NSString *appId = aJson[@"appId"];
    AgoraChatOptions *options;
    if (appKey != nil) {
        options = [AgoraChatOptions optionsWithAppkey:appKey];
    } else if (appId != nil) {
        options = [AgoraChatOptions optionsWithAppId:appId];
    } else {
        NSLog(@"AgoraChatOptions: fromJsonObject: appKey and appId is empty");
    }

    options.isAutoLogin = [aJson[@"autoLogin"] boolValue];
    options.enableConsoleLog = [aJson[@"debugModel"] boolValue];
    options.enableRequireReadAck = [aJson[@"requireAck"] boolValue];
    options.enableDeliveryAck = [aJson[@"requireDeliveryAck"] boolValue];
    options.sortMessageByServerTime =
        [aJson[@"sortMessageByServerTime"] boolValue];
    options.autoAcceptFriendInvitation =
        [aJson[@"acceptInvitationAlways"] boolValue];
    options.autoAcceptGroupInvitation =
        [aJson[@"autoAcceptGroupInvitation"] boolValue];
    options.deleteMessagesOnLeaveGroup =
        [aJson[@"deleteMessagesAsExitGroup"] boolValue];
    options.deleteMessagesOnLeaveChatroom =
        [aJson[@"deleteMessagesAsExitChatRoom"] boolValue];
    options.autoDownloadThumbnail = [aJson[@"isAutoDownload"] boolValue];
    options.canChatroomOwnerLeave =
        [aJson[@"isChatRoomOwnerLeaveAllowed"] boolValue];
    options.isAutoTransferMessageAttachments =
        [aJson[@"serverTransfer"] boolValue];
    options.usingHttpsOnly = [aJson[@"usingHttpsOnly"] boolValue];
    options.apnsCertName = aJson[@"pushConfig"][@"apnsCertName"];
    options.enableDnsConfig = [aJson[@"enableDNSConfig"] boolValue];
    options.chatPort = [aJson[@"imPort"] intValue];
    options.chatServer = aJson[@"imServer"];
    options.restServer = aJson[@"restServer"];
    options.dnsURL = aJson[@"dnsURL"];
    options.area = [AgoraChatOptions AreaCodeFromInt:[aJson[@"areaCode"] intValue]];
    options.loadEmptyConversations =
        [aJson[@"enableEmptyConversation"] boolValue];
    options.customDeviceName = aJson[@"customDeviceName"];
    if (aJson[@"customOSType"]) {
        options.customOSType = [aJson[@"customOSType"] intValue];
    }

    NSDictionary *pushConfig = aJson[@"pushConfig"];
    if (pushConfig != nil) {
        options.apnsCertName = pushConfig[@"deviceId"];
    }

    options.enableTLSConnection = [aJson[@"enableTLS"] boolValue];
    options.useReplacedMessageContents =
        [aJson[@"useReplacedMessageContents"] boolValue];
    options.includeSendMessageInMessageListener =
        [aJson[@"messagesReceiveCallbackIncludeSend"] boolValue];
    options.regardImportMessagesAsRead =
        [aJson[@"regardImportMessagesAsRead"] boolValue];

    options.loginExtensionInfo = aJson[@"loginExtraInfo"];
    options.workPathCopiable = aJson[@"workPathCopiable"];

    return options;
}
@end

@implementation AgoraChatPageResult (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    NSMutableArray *dataList = [NSMutableArray array];
    for (id<ExtSdkToJson> obj in self.list) {
        [dataList addObject:[obj toJsonObject]];
    }

    data[@"list"] = dataList;
    data[@"count"] = @(self.count);

    return data;
}
@end

@implementation AgoraChatPushOptions (Json)
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"pushStyle"] = @(self.displayStyle != AgoraChatPushDisplayStyleSimpleBanner);
    data[@"displayStyle"] = @(self.displayStyle);
    data[@"displayName"] = self.displayName;
    return data;
}

@end

@implementation AgoraChatPresence (Json)

- (nonnull NSDictionary *)toJsonObject {
    NSMutableDictionary *details = [NSMutableDictionary dictionary];
    for (AgoraChatPresenceStatusDetail *detail in self.statusDetails) {
        details[detail.device] = @(detail.status);
    }
    return @{
        @"publisher" : self.publisher,
        @"statusDetails" : details,
        @"statusDescription" : self.statusDescription,
        @"lastTime" : @(self.lastTime),
        @"expiryTime" : @(self.expirytime)
    };
}

@end

@implementation AgoraChatUserInfo (Json)

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"userId"] = self.userId;
    ret[@"nickName"] = self.nickname;
    ret[@"avatarUrl"] = self.avatarUrl;
    ret[@"mail"] = self.mail;
    ret[@"phone"] = self.phone;
    ret[@"gender"] = @(self.gender);
    ret[@"sign"] = self.sign;
    ret[@"birth"] = self.birth;
    ret[@"ext"] = self.ext;

    return ret;
}

+ (AgoraChatUserInfo *)fromJsonObject:(NSDictionary *)aJson {
    AgoraChatUserInfo *userInfo = AgoraChatUserInfo.new;
    userInfo.userId = aJson[@"userId"];
    userInfo.nickname = aJson[@"nickName"];
    userInfo.avatarUrl = aJson[@"avatarUrl"];
    userInfo.mail = aJson[@"mail"];
    userInfo.phone = aJson[@"phone"];
    userInfo.gender = [aJson[@"gender"] integerValue] ?: 0;
    userInfo.sign = aJson[@"sign"];
    userInfo.birth = aJson[@"birth"];
    userInfo.ext = aJson[@"ext"];
    return [userInfo copy];
}

@end

@implementation AgoraChatTranslateLanguage (Json)

- (nonnull NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"code"] = self.languageCode;
    ret[@"name"] = self.languageName;
    ret[@"nativeName"] = self.languageNativeName;
    return ret;
}

@end

@implementation NSArray (Json)
- (NSArray *)toJsonArray {
    NSMutableArray *ary = nil;
    for (id<ExtSdkToJson> item in self) {
        if (ary == nil) {
            ary = [NSMutableArray array];
        }
        [ary addObject:[item toJsonObject]];
    }
    return ary;
}
@end

@implementation AgoraChatMessageReactionOperation (Json)
- (NSDictionary *)toJsonObject {
    return @{
        @"userId" : self.userId,
        @"reaction" : self.reaction,
        @"operate" : @(self.operate),
    };
}
@end

@implementation AgoraChatMessageReaction (Json)

- (nonnull NSDictionary *)toJsonObject {
    return @{
        @"reaction" : self.reaction,
        @"count" : @(self.count),
        @"isAddedBySelf" : @(self.isAddedBySelf),
        @"userList" : self.userList,
    };
}

@end

@implementation AgoraChatMessageReactionChange (Json)

- (nonnull NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"conversationId"] = self.conversationId;
    ret[@"messageId"] = self.messageId;

    NSMutableArray *reactions = [NSMutableArray array];
    for (AgoraChatMessageReaction *reaction in self.reactions) {
        [reactions addObject:[reaction toJsonObject]];
    }
    ret[@"reactions"] = reactions;

    NSMutableArray *operations = [NSMutableArray array];
    for (AgoraChatMessageReactionOperation *reaction in self.operations) {
        [operations addObject:[reaction toJsonObject]];
    }
    ret[@"operations"] = operations;
    return ret;
}

@end

@implementation AgoraChatThread (Json)

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"threadId"] = self.threadId;
    ret[@"threadName"] = self.threadName;
    ret[@"owner"] = self.owner;
    ret[@"msgId"] = self.messageId;
    ret[@"parentId"] = self.parentId;
    ret[@"memberCount"] = @(self.membersCount);
    ret[@"msgCount"] = @(self.messageCount);
    ret[@"createAt"] = @(self.createAt);
    if (self.lastMessage && self.lastMessage.messageId.length > 0) {
        ret[@"lastMessage"] = [self.lastMessage toJsonObject];
    }
    return ret;
}

@end

@implementation AgoraChatThreadEvent (Json)

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"from"] = self.from;
    ret[@"type"] = @([self getIntOperation]);
    ret[@"thread"] = [self.chatThread toJsonObject];
    return ret;
}

- (int)getIntOperation {
    int ret = 0;
    switch (self.type) {
    case AgoraChatThreadOperationUnknown:
        ret = 0;
        break;
    case AgoraChatThreadOperationCreate:
        ret = 1;
        break;
    case AgoraChatThreadOperationUpdate:
        ret = 2;
        break;
    case AgoraChatThreadOperationDelete:
        ret = 3;
        break;
    case AgoraChatThreadOperationUpdate_msg:
        ret = 4;
        break;
    }

    return ret;
}

@end

@implementation AgoraChatSilentModeParam (Json)

+ (AgoraChatSilentModeParam *)fromJsonObject:(NSDictionary *)dict {
    AgoraChatSilentModeParamType paramType =
        [self paramTypeFromInt:[dict[@"paramType"] intValue]];
    AgoraChatSilentModeParam *param =
        [[AgoraChatSilentModeParam alloc] initWithParamType:paramType];
    NSDictionary *dictStartTime = dict[@"startTime"];
    NSDictionary *dictEndTime = dict[@"endTime"];
    int duration = [dict[@"duration"] intValue];

    AgoraChatPushRemindType remindType =
        [self remindTypeFromInt:[dict[@"remindType"] intValue]];

    param.remindType = remindType;
    param.silentModeStartTime = [AgoraChatSilentModeTime fromJsonObject:dictStartTime];
    param.silentModeEndTime = [AgoraChatSilentModeTime fromJsonObject:dictEndTime];
    param.silentModeDuration = duration;
    return param;
}
+ (AgoraChatSilentModeParamType)paramTypeFromInt:(int)iParamType {
    AgoraChatSilentModeParamType ret = AgoraChatSilentModeParamTypeRemindType;
    if (iParamType == 0) {
        ret = AgoraChatSilentModeParamTypeRemindType;
    } else if (iParamType == 1) {
        ret = AgoraChatSilentModeParamTypeDuration;
    } else if (iParamType == 2) {
        ret = AgoraChatSilentModeParamTypeInterval;
    }
    return ret;
}
+ (AgoraChatPushRemindType)remindTypeFromInt:(int)iRemindTime {
    AgoraChatPushRemindType ret = AgoraChatPushRemindTypeAll;
    if (iRemindTime == 0) {
        ret = AgoraChatPushRemindTypeAll;
    } else if (iRemindTime == 1) {
        ret = AgoraChatPushRemindTypeMentionOnly;
    } else if (iRemindTime == 2) {
        ret = AgoraChatPushRemindTypeNone;
    }
    return ret;
}
+ (int)remindTypeToInt:(AgoraChatPushRemindType)type {
    int ret = 0;
    switch (type) {
    case AgoraChatPushRemindTypeAll:
        ret = 0;
        break;
    case AgoraChatPushRemindTypeMentionOnly:
        ret = 1;
        break;
    case AgoraChatPushRemindTypeNone:
        ret = 2;
        break;
    }
    return ret;
}

@end

@implementation AgoraChatSilentModeResult (Json)

- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [[NSMutableDictionary alloc] init];
    ret[@"expireTimestamp"] = @(self.expireTimestamp);
    ret[@"startTime"] = [self.silentModeStartTime toJsonObject];
    ret[@"endTime"] = [self.silentModeEndTime toJsonObject];
    ret[@"remindType"] = @([AgoraChatSilentModeParam remindTypeToInt:self.remindType]);
    ret[@"conversationId"] = self.conversationID;
    ret[@"conversationType"] =
        @([AgoraChatConversation typeToInt:self.conversationType]);
    return ret;
}

@end

@implementation AgoraChatSilentModeTime (Json)

+ (AgoraChatSilentModeTime *)fromJsonObject:(NSDictionary *)dict {
    int hour = [dict[@"hour"] intValue];
    int minute = [dict[@"minute"] intValue];
    return [[AgoraChatSilentModeTime alloc] initWithHours:hour minutes:minute];
}
- (NSDictionary *)toJsonObject {
    return @{@"hour" : @(self.hours), @"minute" : @(self.minutes)};
}

@end

@implementation AgoraChatFetchServerMessagesOption (Json)
+ (AgoraChatFetchServerMessagesOption *)fromJsonObject:(NSDictionary *)dict {
    if (dict == nil) {
        return nil;
    }
    AgoraChatFetchServerMessagesOption *options =
        [[AgoraChatFetchServerMessagesOption alloc] init];
    options.direction = [dict[@"direction"] isEqual:@(0)]
                            ? AgoraChatMessageSearchDirectionUp
                            : AgoraChatMessageSearchDirectionDown;
    options.startTime = [dict[@"startTs"] longLongValue];
    options.endTime = [dict[@"endTs"] longLongValue];
    options.from = dict[@"from"];
    options.isSave = [dict[@"needSave"] boolValue];
    NSArray *types = dict[@"msgTypes"];
    NSMutableArray<NSNumber *> *list = [NSMutableArray new];
    if (types) {
        for (int i = 0; i < types.count; i++) {
            NSString *type = types[i];
            if ([type isEqualToString:@"txt"]) {
                [list addObject:@(AgoraChatMessageBodyTypeText)];
            } else if ([type isEqualToString:@"img"]) {
                [list addObject:@(AgoraChatMessageBodyTypeImage)];
            } else if ([type isEqualToString:@"loc"]) {
                [list addObject:@(AgoraChatMessageBodyTypeLocation)];
            } else if ([type isEqualToString:@"video"]) {
                [list addObject:@(AgoraChatMessageBodyTypeVideo)];
            } else if ([type isEqualToString:@"voice"]) {
                [list addObject:@(AgoraChatMessageBodyTypeVoice)];
            } else if ([type isEqualToString:@"file"]) {
                [list addObject:@(AgoraChatMessageBodyTypeFile)];
            } else if ([type isEqualToString:@"cmd"]) {
                [list addObject:@(AgoraChatMessageBodyTypeCmd)];
            } else if ([type isEqualToString:@"custom"]) {
                [list addObject:@(AgoraChatMessageBodyTypeCustom)];
            } else if ([type isEqualToString:@"combine"]) {
                [list addObject:@(AgoraChatMessageBodyTypeCombine)];
            }
        }
    }

    if (list.count > 0) {
        options.msgTypes = list;
    }

    return options;
}
- (NSDictionary *)toJsonObject {
    return nil;
}
@end

@implementation AgoraChatContact (Json)

- (nonnull NSDictionary *)toJsonObject {
    NSMutableDictionary *data = [NSMutableDictionary dictionary];
    data[@"userId"] = self.userId;
    data[@"remark"] = self.remark;
    return data;
}

+ (nonnull AgoraChatContact *)fromJsonObject:(nonnull NSDictionary *)dict {
    AgoraChatContact *contact = [[AgoraChatContact alloc] initWithUserId:dict[@"userId"]
                                                    remark:dict[@"remark"]];
    return contact;
}

@end

@implementation AgoraChatConversationFilter (Json)

+ (AgoraChatConversationFilter *)fromJsonObject:(NSDictionary *)dict {
    AgoraChatConversationFilter *filter = [[AgoraChatConversationFilter alloc] init];
    filter.mark = (AgoraChatMarkType)[dict[@"mark"] integerValue];
    filter.pageSize = [dict[@"pageSize"] intValue];
    return filter;
}

+ (NSString *)getCursor:(NSDictionary *)dict {
    return dict[@"cursor"];
}

+ (BOOL)getPinned:(NSDictionary *)dict {
    return [dict[@"pinned"] boolValue];
}

+ (BOOL)hasMark:(NSDictionary *)dict {
    return dict[@"mark"] != nil;
}

+ (NSInteger)pageSize:(NSDictionary *)dict {
    return [dict[@"pageSize"] intValue];
}

@end

@implementation AgoraChatMessagePinInfo (Json)
+ (AgoraChatMessagePinInfo *)fromJsonObject:(NSDictionary *)dict {
    AgoraChatMessagePinInfo *info = [[AgoraChatMessagePinInfo alloc] init];
    info.operatorId = dict[@"operatorId"];
    info.pinTime = [dict[@"pinTime"] integerValue];
    return info;
}
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary new];
    ret[@"operatorId"] = self.operatorId;
    ret[@"pinTime"] = @(self.pinTime);
    return ret;
}
@end

@implementation AgoraChatRecallMessageInfo (Json)
+ (AgoraChatRecallMessageInfo *)fromJsonObject:(NSDictionary *)dict {
    return nil;
}
- (NSDictionary *)toJsonObject {
    NSMutableDictionary *ret = [NSMutableDictionary dictionary];
    ret[@"recalledMessageId"] = self.recallMessageId;
    ret[@"recalledBy"] = self.recallBy;
    ret[@"recalledExt"] = self.ext;
    ret[@"recalledMessage"] = [self.recallMessage toJsonObject];
    ret[@"recalledConvId"] = self.conversationId;
    return ret;
}
@end
