/*
 *	| |    | |  \ \  / /  | |    | |   / _______|
 *	| |____| |   \ \/ /   | |____| |  / /
 *	| |____| |    \  /    | |____| |  | |   _____
 * 	| |    | |    /  \    | |    | |  | |  |____ |
 *  | |    | |   / /\ \   | |    | |  \ \______| |
 *  | |    | |  /_/  \_\  | |    | |   \_________|
 *
 * Copyright (c) 2011 ~ 2015 Shenzhen HXHG. All rights reserved.
 */

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <JMessage/JMSGConstants.h>
#import <JMessage/JMSGConversation.h>

@class JMSGAbstractContent;
@class JMSGUser;
@protocol JMSGTargetProtocol;
@class JMSGOptionalContent;

/*!
 * 消息
 *
 * 本类 JMSGMessage 是 JMessage SDK 里的消息实体。
 * 收到的消息、发送的消息、获取历史消息,其中的消息类,都是这个 JMSGMessage。
 *
 * 以下分别描述消息相关主要使用场景。
 *
 * #### 获取历史消息
 *
 * 先基于聊天对象ID与会话类型,拿到会话对象,然后调用会话对象里的 [JMSGConversation allMessages:] 获取到某会话的全部历史消息列表。
 *
 * #### 展示一条消息
 *
 * 发送者、接收者等基本属性都有相应的属性。消息内容则在一个 content 对象里,访问时先通过 contentType 拿到内容类型,
 * 然后把 content 转型为相应的具体内容类型,再进一步可拿到具体的信息。
 *
 *    ```
 *    JMSGTextContent *textContent = (JMSGTextContent *)message.content;
 *    NSString *msgText = textContent.text;
 *    ```
 *
 * #### 接收消息
 *
 * 参考 JMessageDelegate 里的说明.
 *
 * #### 发送消息
 *
 * 参考 JMessageDelegate 里的说明.
 *
 */
@interface JMSGMessage : NSObject <NSCopying>

JMSG_ASSUME_NONNULL_BEGIN

///----------------------------------------------------
/// @name Class APIs 类方法 - 创建与发送消息
///----------------------------------------------------

/*!
 * @abstract 创建单聊消息(快捷接口)
 *
 * @param content 消息内容对象
 * @param username 单聊用户 username
 *
 * #### 注意:
 *
 * 1、单独调用此接口创建消息,SDK 不会本地保存消息,再调用发送接口时才会保存;
 *
 * 2、如果上层希望创建消息时就本地化保存,请使用 [JMSGConversation createMessageWithContent:]
 */
+ (JMSGMessage *)createSingleMessageWithContent:(JMSGAbstractContent *)content
                                       username:(NSString *)username;

/*!
 * @abstract 创建群聊消息
 *
 * @param content 消息内容对象
 * @param groupId 群聊ID
 *
 * #### 注意:
 *
 * 1、单独调用此接口创建消息,SDK 不会本地保存消息,再调用发送接口时才会保存;
 *
 * 2、如果上层希望创建消息时就本地化保存,请使用 [JMSGConversation createMessageWithContent:]
 */
+ (JMSGMessage *)createGroupMessageWithContent:(JMSGAbstractContent *)content
                                       groupId:(NSString *)groupId;

/*!
 * @abstract 创建聊天室消息
 *
 * @param content 消息内容对象
 * @param roomId  聊天室ID
 *
 * @discussion 不关心会话时的直接创建聊天消息的接口。一般建议使用 JMSGConversation -> createMessageWithContent:
 */
+ (JMSGMessage *)createChatRoomMessageWithContent:(JMSGAbstractContent *)content
                                       chatRoomId:(NSString *)roomId;

/*!
 * @abstract 创建@人的群聊消息
 *
 * @param content 消息内容对象
 * @param groupId 群聊ID
 * @param at_list @对象的数组
 *
 * #### 注意:
 *
 * 1、单独调用此接口创建消息,SDK 不会本地保存消息,再调用发送接口时才会保存;
 *
 * 2、如果上层希望创建消息时就本地化保存,请使用 [JMSGConversation createMessageWithContent:]
 */
+ (JMSGMessage *)createGroupMessageWithContent:(JMSGAbstractContent *)content
                                       groupId:(NSString *)groupId
                                       at_list:(NSArray<__kindof JMSGUser *> *)at_list;

/*!
 * @abstract 创建@所有人的群聊消息
 *
 * @param content 消息内容对象
 * @param groupId 群聊ID
 *
 * #### 注意:
 *
 * 1、单独调用此接口创建消息,SDK 不会本地保存消息,再调用发送接口时才会保存;
 *
 * 2、如果上层希望创建消息时就本地化保存,请使用 [JMSGConversation createMessageWithContent:]
 */
+ (JMSGMessage *)createGroupAtAllMessageWithContent:(JMSGAbstractContent *)content
                                            groupId:(NSString *)groupId;

/*!
 * @abstract 发送消息(已经创建好的)
 *
 * @param message 消息对象。
 *
 * @discussion 此接口与 createMessage:: 相关接口配合使用,创建好后使用此接口发送。
 */
+ (void)sendMessage:(JMSGMessage *)message;

/*!
 * @abstract 发送消息(附带可选功能,如:控制离线消息存储、自定义通知栏内容、消息已读回执等)
 *
 * @param message           通过消息创建类接口,创建好的消息对象
 * @param optionalContent   可选功能,具体请查看 JMSGOptionalContent 类
 *
 * @discussion 可选功能里可以设置离线消息存储、自定义通知栏内容、消息已读回执等,具体请查看 JMSGOptionalContent 类。
 *
 */
+ (void)sendMessage:(JMSGMessage *)message optionalContent:(JMSGOptionalContent *)optionalContent;

/*!
 * @abstract 发送单聊文本消息
 *
 * @param text 文本内容
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleTextMessage:(NSString *)text
                       toUser:(NSString *)username;

/*!
 * @abstract 发送跨应用单聊文本消息
 *
 * @param text 文本内容
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleTextMessage:(NSString *)text
                       toUser:(NSString *)username
                       appKey:(NSString *)userAppKey;

/*!
 * @abstract 发送单聊图片消息
 *
 * @param imageData 图片数据
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleImageMessage:(NSData *)imageData
                        toUser:(NSString *)username;

/*!
 * @abstract 发送跨应用单聊图片消息
 *
 * @param imageData 图片数据
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleImageMessage:(NSData *)imageData
                        toUser:(NSString *)username
                        appKey:(NSString *)userAppKey;

/*!
 * @abstract 发送单聊语音消息
 *
 * @param voiceData 语音数据
 * @param duration 语音时长
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleVoiceMessage:(NSData *)voiceData
                 voiceDuration:(NSNumber *)duration
                        toUser:(NSString *)username;

/*!
 * @abstract 发送跨应用单聊语音消息
 *
 * @param voiceData 语音数据
 * @param duration 语音时长
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleVoiceMessage:(NSData *)voiceData
                 voiceDuration:(NSNumber *)duration
                        toUser:(NSString *)username
                        appKey:(NSString *)userAppKey;

/*!
 * @abstract 发送单聊文件消息
 *
 * @param fileData 文件数据数据
 * @param fileName 文件名
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleFileMessage:(NSData *)fileData
                     fileName:(NSString *)fileName
                        toUser:(NSString *)username;

/*!
 * @abstract 发送跨应用单聊文件消息
 *
 * @param fileData 文件数据数据
 * @param fileName 文件名
 * @param username 单聊对象 username
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleFileMessage:(NSData *)fileData
                     fileName:(NSString *)fileName
                        toUser:(NSString *)username
                        appKey:(NSString *)userAppKey;

/*!
 * @abstract 发送单聊地理位置消息
 * @param latitude 纬度
 * @param longitude 经度
 * @param scale 缩放比例
 * @param address 详细地址
 * @param username 单聊对象
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleLocationMessage:(NSNumber *)latitude
                  longitude:(NSNumber *)longitude
                      scale:(NSNumber *)scale
                    address:(NSString *)address
                     toUser:(NSString *)username;

/*!
 * @abstract 发送跨应用单聊地理位置消息
 * @param latitude 纬度
 * @param longitude 经度
 * @param scale 缩放比例
 * @param address 详细地址
 * @param username 单聊对象
 * @param userAppKey 单聊对象的appKey
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendSingleLocationMessage:(NSNumber *)latitude
                        longitude:(NSNumber *)longitude
                            scale:(NSNumber *)scale
                          address:(NSString *)address
                           toUser:(NSString *)username
                           appKey:(NSString *)userAppKey;

/*!
 * @abstract 发送群聊文本消息
 *
 * @param text 文本内容
 * @param groupId 群聊目标群组ID
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendGroupTextMessage:(NSString *)text
                     toGroup:(NSString *)groupId;

/*!
 * @abstract 发送群聊图片消息
 *
 * @param imageData 图片数据
 * @param groupId 群聊目标群组ID
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendGroupImageMessage:(NSData *)imageData
                      toGroup:(NSString *)groupId;

/*!
 * @abstract 发送群聊语音消息
 *
 * @param voiceData 语音数据
 * @param duration 语音时长
 * @param groupId 群聊目标群组ID
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendGroupVoiceMessage:(NSData *)voiceData
                voiceDuration:(NSNumber *)duration
                      toGroup:(NSString *)groupId;

/*!
 * @abstract 发送群聊文件消息
 *
 * @param fileData 文件数据
 * @param fileName 文件名
 * @param groupId 群聊目标群组ID
 *
 * @discussion 快捷方法,不需要先创建消息而直接发送。
 */
+ (void)sendGroupFileMessage:(NSData *)fileData
                fileName:(NSString *)fileName
                      toGroup:(NSString *)groupId;

/*!
 * @abstract 发送群聊地理位置消息
 * @param latitude 纬度
 * @param longitude 经度
 * @param scale 缩放比例
 * @param address 详细地址
* @param groupId 群聊目标群组ID
 */
+ (void)sendGroupLocationMessage:(NSNumber *)latitude
                        longitude:(NSNumber *)longitude
                            scale:(NSNumber *)scale
                          address:(NSString *)address
                           toGroup:(NSString *)groupId;
/*!
 * @abstract 消息撤回
 *
 * @param message 需要撤回的消息
 * @param handler 结果回调
 *
 * - resultObject 撤回后的消息
 * - error        错误信息
 *
 * @discussion 注意:SDK可撤回3分钟内的消息
 */
+ (void)retractMessage:(JMSGMessage *)message completionHandler:(JMSGCompletionHandler)handler;

/*!
 * @abstract 消息转发
 *
 * @param message         需要转发的消息
 * @param target          目标 target,只能为 JMSGUser 或 JMSGGroup
 * @param optionalContent 可选功能,具体请查看 JMSGOptionalContent 类
 *
 * @discussion 注意:只能转发消息状态为 SendSucceed 和 ReceiveSucceed 的消息。
 */
+ (void)forwardMessage:(JMSGMessage *)message
                target:(id)target
       optionalContent:(JMSGOptionalContent *JMSG_NULLABLE)optionalContent;


///----------------------------------------------------
/// @name Message basic fields 消息基本属性
///----------------------------------------------------


/*!
 * 消息ID:这个ID是本地存数据库生成的ID,不是服务器端下发时的ID。
 */
@property(nonatomic, strong, readonly) NSString *msgId;

/*!
 * @abstract 服务器端下发的消息ID.
 * @discussion 一般用于与服务器端跟踪消息.
 */
@property(nonatomic, strong, readonly) NSString * JMSG_NULLABLE serverMessageId;

/*!
 * @abstract 消息发送目标
 *
 * @discussion 与 [fromUser] 属性相对应. 根据消息方向不同:
 *
 * - 收到的消息,target 就是我自己。
 * - 发送的消息,target 是我的聊天对象。
 *      单聊是对方用户;
 *      群聊是聊天群组, 也与当前会话的目标一致 [JMSGConversation target]
 */
@property(nonatomic, strong, readonly) id target;

/*!
 * @abstract 消息发送目标应用
 *
 * @discussion 这是为了支持跨应用聊天, 而新增的字段.
 *
 * 单聊时目标是 username. 当该用户为默认 appKey 时, 则不填此字段.
 * 群聊时目标是 groupId, 不填写此字段.
 *
 * @since 2.1.0
 */
@property(nonatomic, strong, readonly) NSString *targetAppKey;

/*!
 * @abstract 消息来源用户 Appkey
 *
 * @discussion 这是为了支持跨应用聊天, 而新增的字段.
 *
 * 不管群聊还是单聊, from_id 都是发送消息的 username. 当该用户是默认 appKey 时, 则不填写此字段.
 *
 * @since 2.1.0
 */
@property(nonatomic, strong, readonly) NSString *fromAppKey;

/*!
 * @abstract 消息来源用户
 *
 * @discussion 与 [target] 属性相对应. 根据消息方向不同:
 *
 * - 收到的消息, fromUser 是发出消息的对方.
 *      单聊是聊天对象, 也与当前会话目标用户一致 [JMSGConversation target],
 *      群聊是该条消息的发送用户.
 * - 发出的消息: fromUser 是我自己.
 */
@property(nonatomic, strong, readonly) JMSGUser *fromUser;

/*!
 * @abstract 消息来源类型
 * @discussion 默认的用户之间互发消息,其值是 "user"。如果是 App 管理员下发的消息,是 "admin"
 */
@property(nonatomic, strong, readonly) NSString *fromType;

/*!
 * @abstract 消息的内容类型
 */
@property(nonatomic, assign, readonly) JMSGContentType contentType;

/*!
 * @abstract 消息内容对象
 * @discussion 使用时应通过 contentType 先获取到具体的消息类型,然后转型到相应的具体类。
 */
@property(nonatomic, strong, readonly) JMSGAbstractContent * JMSG_NULLABLE content;

/*!
 * @abstract 消息发出的时间戳
 * @discussion 这是服务器端下发消息时的真实时间戳,单位为毫秒
 */
@property(nonatomic, strong, readonly) NSNumber *timestamp;

/*!
 * @abstract 消息中的fromName
 * @discussion 消息的发送方展示名称
 */
@property(nonatomic, strong, readonly) NSString *fromName;


///----------------------------------------------------
/// @name Message addOn fields 消息附加属性
///----------------------------------------------------

/*!
 * @abstract 聊天类型。当前支持的类型:单聊,群聊
 */
@property(nonatomic, assign, readonly) JMSGConversationType targetType;

/*!
 * @abstract 消息状态
 * @discussion 一条发出的消息,或者收到的消息,有多个状态会下。具体定义参考 JMSGMessageStatus 的定义。
 */
@property(nonatomic, assign, readonly) JMSGMessageStatus status;

/*!
 * @abstract 当前的消息是不是收到的。
 *
 * @discussion 是收到的,则是别人发给我的。UI 上一般展示在左侧。
 * 如果不是收到侧的,则是发送侧的,是我对外发送的。
 *
 * 主要是在聊天界面展示消息列表时,需要使用此方法,来确认展示消息的方式与位置。
 * 展示时需要发送方消息,不管是收到侧还是发送侧,都可以使用 fromUser 对象。
 */
@property(nonatomic, assign, readonly) BOOL isReceived;

/*!
 * @abstract 消息标志
 *
 * @discussion 这是一个用于表示消息状态的标识字段, App 可自由使用, SDK 不做变更.
 * 默认值为 0, App 有需要时可更新此状态.
 *
 * 使用场景:
 *
 * 1. 语音消息有一个未听标志. 默认 0 表示未读, 已读时 App 更新为 1 或者其他.
 * 2. 某些 App 需要对一条消息做送达, 已读标志, 可借用这个字段.
 */
@property(nonatomic, strong, readonly) NSNumber *flag;

/*!
 * @abstract 是否已读(只针对接收的消息)
 *
 * @discussion 该属性与实例方法 [-(void)setMessageHaveRead:] 是对应的。
 *
 * 注意:只有发送方调用 [+sendMessage:optionalContent:] 方法设置 message 需要已读回执,此属性才有意义。
 */
@property(nonatomic, assign, readonly) BOOL isHaveRead;

///----------------------------------------------------
/// @name Instance APIs 实例方法
///----------------------------------------------------

/*!
 * @abstract 默认的 init 方法不可用
 *
 * @discussion 如果已经得到 JMSGConversation 实例, 则可用以下方法来创建对象:
 *
 * - conversation -> createMessageWithContent:
 * - conversation -> createMessageAsyncWithImageContent::
 *
 * 或者不创建 JMSGMessage 实例也可以直接发送消息. 请参考 JMSGConversation 里相关方法.
 *
 * 如果你的 App 不依赖 JMSGConversation 实例, 也可以直接调用 JMSGMessage 里的类方法
 * 来创建 JMSGMessage 实例:
 *
 * - JMSGMessage -> createSingleMessageWithContent:
 * - JMSGMessage -> createGroupMessageWithContent:
 *
 * 或者直接也可以调用 JMSGMessage 类方法发消息而不必创建 JMSGMessage 对象.
 */
- (instancetype)init NS_UNAVAILABLE;

/*!
 * @abstract 是否是@自己的消息(只针对群消息,单聊消息无@功能)
 */
- (BOOL)isAtMe;

/*!
 * @abstract 是否是@所有人的消息(只针对群消息,单聊消息无@功能)
 */
- (BOOL)isAtAll;

/*!
 * @abstract 获取消息体中所有@对象(只针对群消息,单聊消息无@功能)
 *
 * @param handler 结果回调。回调参数:
 *
 * - resultObject 类型为 NSArray,数组里成员的类型为 JMSGUser
 * 注意:如果该消息为@所有人消息时,resultObject 返回nil,可以通过 isAtAll 接口来判断是否是@所有人的消息
 * - error 错误信息
 *
 * 如果 error 为 nil, 表示获取成功
 * 如果 error 不为 nil,表示获取失败
 *
 * @discussion 从服务器获取,返回消息的所有@对象。
 */
- (void)getAt_List:(JMSGCompletionHandler)handler;

/*!
 * @abstract 设置为已读
 *
 * @param handler 回调
 *
 * - resultObject 返回对应的 message,不过成功失败都会返回 message 对象
 * - error        不为 nil 表示操作失败
 *
 * @discussion 注意: 只针对消息接收方有效
 * 
 * 这是一个异步接口;
 *
 * 1、接收方:设置消息为已读状态后,isHaveRead 属性也会被设置为 YES,
 *
 * 2、发送方:会收到消息已读状态变更事件,SDK 会更新消息的未读人数。
 *
 * 注意:只有发送方调用 [+sendMessage:optionalContent:] 方法设置 message 需要已读回执,此方法才有效。
 */
- (void)setMessageHaveRead:(JMSGCompletionHandler)handler;

/*!
 * @abstract 消息未读人数
 *
 * @discussion 只针对消息发送方有效
 *
 * 注意:只有发送方调用 [+sendMessage:optionalContent:] 方法设置 message 需要已读回执,此方法才有意义。
 */
- (NSInteger)getMessageUnreadCount;

/*!
 * @abstract 已读未读用户列表
 *
 * @param handler 结果回调。回调参数:
 *
 * - unreadUsers  未读用户列表
 * - readsUsers   读用户列表
 * - error        不为nil表示出错
 *
 * @discussion 只针对消息发送方有效
 *
 * 注意:只有发送方调用 [+sendMessage:optionalContent:] 方法设置 message 需要已读回执,此方法才有意义。
 */
- (void)messageReadDetailHandler:(void(^)(NSArray *JMSG_NULLABLE readUsers, NSArray *JMSG_NULLABLE unreadUsers, NSError *JMSG_NULLABLE error))handler;

/*!
 * @abstract 设置消息的 fromName(即:通知栏的展示名称)
 *
 * @param fromName 本条消息在接收方通知栏的展示名称
 *
 * @discussion fromName填充在发出的消息体里,对方收到该消息通知时,在通知栏显示的消息发送人名称就是该字段的值.
 *
 */
- (void)setFromName:(NSString * JMSG_NULLABLE)fromName;

/*!
 * @abstract 更新 message 中的extra
 *
 * @param value   待更新的value,不能为null,类型只能为 NSNumber 和 NSString
 * @param key     待更新value对应的key,不能为null
 *
 * @discussion 如果 message 中没有该 key 对应的 extra 值,则会插入该新值
 */
- (BOOL)updateMessageExtraValue:(id)value forKey:(NSString *)key;

/*!
 * @abstract 更新消息标志
 *
 * @param flag 为 nil 时表示设置为 0.
 *
 * @discussion 参考 flag property 的说明.
 */
- (void)updateFlag:(NSNumber * JMSG_NULLABLE)flag;

/*!
 * @abstract 消息对象转换为 JSON 字符串的表示。
 *
 * @discussion 遵循 Message JSON 协议的定义。
 */
- (NSString *)toJsonString;

/*!
 * @abstract 对象比较
 *
 * @param message 待比较的消息对象
 */
- (BOOL)isEqualToMessage:(JMSGMessage * JMSG_NULLABLE)message;


JMSG_ASSUME_NONNULL_END

@end