//
//  NXCallManagerProtocol.h
//  NXCall
//
//  Created by 徐家骏 on 2019/4/29.
//  Copyright © 2019 徐家骏. All rights reserved.
//

#ifndef NXCallManagerProtocol_h
#define NXCallManagerProtocol_h

#import <AVFoundation/AVFoundation.h>
#import <UserNotifications/UserNotifications.h>
#import <NXSdwan/NXSdwan.h>
#import "NXGlobalDefs.h"
#import "NXCallOption.h"
#import "NXVideoFrameContext.h"
#import "NXDataFrameContext.h"
#import "NXCallInfo.h"
#import "NXCallStatus.h"
#import "NXCallResultInfo.h"

NS_ASSUME_NONNULL_BEGIN

extern NSString *const NXCallStateChangeNotification;

extern NSString *const NXCallStateIdle;
extern NSString *const NXCallStateCalling;
extern NSString *const NXCallStateCalled;
extern NSString *const NXCallStateIncall;

extern NSString *const NXCallEventInvite;
extern NSString *const NXCallEventAccept;
extern NSString *const NXCallEventReject;
extern NSString *const NXCallEventCancel;
extern NSString *const NXCallEventEnd;
extern NSString *const NXCallEventInviteMember;
extern NSString *const NXCallEventKickMember;

extern NSString *const NXCallEventRecvInvite;
extern NSString *const NXCallEventRecvSidCreated;
extern NSString *const NXCallEventRecvRing;
extern NSString *const NXCallEventRecvServerRing;
extern NSString *const NXCallEventRecvAccept;
extern NSString *const NXCallEventRecvReject;
extern NSString *const NXCallEventRecvCancel;
extern NSString *const NXCallEventRecvBusy;
extern NSString *const NXCallEventRecvEnd;
extern NSString *const NXCallEventRecvMemberState;

extern NSString *const NXCallEventTimeoutCalling;
extern NSString *const NXCallEventTimeoutCalled;

extern NSString *const NXCallEventCallkitActiveAudio;
extern NSString *const NXCallEventCallkitDeactiveAudio;

//多语的key
extern NSString *const NXLocalizedKey_NotificationActionAccept;
extern NSString *const NXLocalizedKey_NotificationActionReject;
extern NSString *const NXLocalizedKey_NotificationMediaAudio;
extern NSString *const NXLocalizedKey_NotificationMediaVideo;
extern NSString *const NXLocalizedKey_NotificationGroupMediaAudio;
extern NSString *const NXLocalizedKey_NotificationGroupMediaVideo;


NS_ASSUME_NONNULL_END

/**
 *  通话Block
 *  @param error  通话结果, 如果成功error为nil
 */
typedef void(^NXCallHandler)(NSError * __nullable error);

typedef NS_ENUM(NSInteger, NXSuggestedPlaySound) {
    NXSuggestedPlaySoundStopPlaying           = 0,
    NXSuggestedPlaySoundPeerIncall            = 1,
    NXSuggestedPlaySoundPeerTimeout           = 2,
    NXSuggestedPlaySoundPeerOffline           = 3,
    NXSuggestedPlaySoundPeerBusy              = 4,
    NXSuggestedPlaySoundCalling               = 5,
    NXSuggestedPlaySoundCalled                = 6,
};

typedef NS_ENUM(NSInteger, NXRecordType) {
    NXRecordTypeAudio,
    NXRecordTypeVideo,
    NXRecordTypeScreen,
};

@protocol NXCallDelegateWillDeprecate <NSObject>
@optional
/**
 @discussion 通话结束
 @param sessionId session id
 */
- (void)onCallEnd:(int64_t)sessionId reasonCode:(NSInteger)code;

@end

@protocol NXCallManagerDelegate <NSObject, NXCallDelegateWillDeprecate>

@optional
- (void)onInvite:(int64_t)sessionId
              to:(int64_t)callee;

- (void)onCancel:(int64_t)sessionId
              to:(int64_t)callee;

- (void)onCallingTimeout:(int64_t)sessionId
                     to:(int64_t)callee;


- (void)onReceiveRing:(int64_t)sessionId
                 from:(int64_t)callee;
/**
 *  @discussion 主叫收到被叫接听
 *
 *  @param sessionId   session id
 *  @param callee      被叫帐号, 多方呼叫时为 NXSessionManagerUserId
 */
- (void)onReceiveAccepted:(int64_t)sessionId
                     from:(int64_t)callee;

/**
 *  @discussion 主叫收到被叫拒绝
 *
 *  @param sessionId   session id
 *  @param callee      被叫帐号
 */
- (void)onReceiveRejected:(int64_t)sessionId
                     from:(int64_t)callee
               reasonCode:(NSInteger)code;


/**
 *  @discussion 主叫收到被叫忙
 *  @param sessionId   session id
 *  @param callee      被叫帐号
 */
- (void)onReceiveBusy:(int64_t)sessionId
                 from:(int64_t)callee;


///**
// *  @discussion 主叫收到被叫挂断
// *
// *  @param sessionId   session id
// *  @param callee      被叫帐号
// */
//- (void)onReceiveHanguped:(int64_t)sessionId
//                     from:(int64_t)callee;


/**
 *  @discussion 被叫收到呼叫（振铃）
 *  @param sessionId  session id
 *  @param caller     主叫帐号
 *  @param mediaType  呼叫类型
 */
- (void)onReceiveInvite:(int64_t)sessionId
                   from:(int64_t)caller
              mediaType:(NXCallMediaType)mediaType
                  title:(nullable NSString *)title
                    msg:(NSString *)msg;

- (void)onReceiveCancel:(int64_t)sessionId
                   from:(int64_t)caller;

- (void)onAccept:(int64_t)sessionId
              of:(int64_t)caller;

- (void)onReject:(int64_t)sessionId
              of:(int64_t)caller;

- (void)onCalledTimeout:(int64_t)sessionId
                     of:(int64_t)caller;


/**
 @discussion 通话建立
 @param sessionId session id
 */
- (void)onCallEstablished:(int64_t)sessionId;

/// 通话结束
/// @param info 状态信息
- (void)onCallEnd:(nonnull NXCallResultInfo *)info;

- (void)onMemberStateChange:(nonnull NSDictionary *)memberState;

- (void)onMemberJoin:(NSOrderedSet *_Nonnull)members sessionId:(int64_t)sessionId;

- (void)onMemberLeave:(NSOrderedSet *_Nonnull)members sessionId:(int64_t)sessionId;


- (void)onRemoteVideoFrameReady:(nonnull CVImageBufferRef)image withContext:(nonnull NXVideoFrameContext *)frameContext from:(int64_t)from;

- (void)onLocalVideoFrameReady:(nonnull CVImageBufferRef)image withContext:(nonnull NXVideoFrameContext *)frameContext;

- (void)onReceiveCustomSignal:(nonnull NXCustomSignal *)signal;

- (void)onCallStatus:(nullable NXCallStatus *)status;


- (void)onAudioVolume:(int)volume from:(int64_t)from;

- (void)onAudioVolumeOfSelf:(int)volume;

- (void)onAudioRecorderData:(nonnull NSData *)data;

//- (void)onDataReceived:(nonnull NSData *)data withContext:(nonnull NXDataFrameContext *)frameContext from:(int64_t)from;

- (void)onAppCommandReceived:(nonnull NSData *)command from:(int64_t)from;

- (void)onAppDataReceived:(nonnull NSData *)data from:(int64_t)from;

/**
 如果实现此方法，则会传递SDK内部信令出来，
 @param type 信令类型
 @param body 信令内容，透传即可
 */
- (void)onSendSignal:(NSString *)signal;

/**
 报告通话中的事件，比如对方被系统电话打断，本地或者对方网络不佳，通话中有掉线等
 */
- (void)onCallEvent:(NXCallEvent)event from:(int64_t)from;

- (nullable NSString *)nameOfCallerWithUid:(int64_t)uid;

- (nullable NSString *)phoneNumberOfCallerWithUid:(int64_t)uid;

- (nullable NSString *)localizedStringOfKey:(nullable NSString *)key;

- (void)suggestPlaySound:(NXSuggestedPlaySound)sound;

- (struct NXNetworkInfo)networkInfo:(int64_t)uid;;

//TODO:还有一些场景需要考虑：1）掉线，2）单向视频或者单向音频，3）自动接听，4）对方被打断的情况，4）单帐号多设备，6）网络质量提示
- (void)onBeginRecordForSessionID:(int64_t)sessionID;

@end

@protocol NXCallSetting <NSObject>
    ///当前使用的摄像头，默认前置。
@property (nonatomic) NXVideoCameraPosition cameraPosition;
    ///视频的最大码率，单位bps，通话中可设置, 默认8000000。
@property (nonatomic) uint32_t videoMaxBitrate;
    ///上行带宽上限，单位kb。
@property uint32_t bandwidthUpperLimit;
    ///帧率, 根据设备类型，默认支持最大30帧。
@property int32_t videoFramerate;
    ///最高的视频质量，视频质量会根据带宽自适应, 默认1080P。
@property (nonatomic, readonly) NXCallPreset callPreset;
    ///音频质量 0～10， 默认3.
@property int audioQuality;
    ///音频采样率，单位bit，默认48000，通话启动前设置有效。
@property int audioSampleRate;
    ///扬声器开关, 音频通话默认关闭，视频通话默认开启。
@property (nonatomic, getter = isSpeakerOn) BOOL speaker;
    ///静音开关，默认关闭。
@property (nonatomic, getter = isMuted) BOOL mute;
    ///混音器输出静音开关，默认关闭。
@property (nonatomic, getter = isMixerMuted) BOOL muteMixer;
    ///摄像头开关，视频通话授权后默认开启，音频通话无效。
@property (nonatomic, getter = isCameraOn) BOOL cameraOn;
    ///降噪等级 0～3, 默认3。
@property (nonatomic) int denoiseLevel;
    ///麦克风输入增益调整，-20db ～ 20db,，默认0。
@property (nonatomic) int inputGainDB;
    ///最大混音人数数量 1~20, 默认8。
@property (nonatomic) int maxAudioMixingNumber;
    ///h264编码开关
@property (nonatomic, getter = isH264Preferred) BOOL videoCodecH264Preferred;
    ///p2p流量最大占比 0～100 默认100，0表示不用p2p，100表示最大p2p流量
@property (nonatomic) int p2pEnabledRatio;
    ///callkit是否启用。
@property (nonatomic, getter = isCallkitActive, readonly) BOOL callkitActive;
    ///绿幕开关
@property (nonatomic, getter = isGreenCurtainOn) BOOL greenCurtain;
    ///替代消回声方法是否启用
@property (nonatomic, getter = isAlterAECEnabled) BOOL alterAECEnabled;
/// 允许图像跟随设备方向旋转。默认YES。
@property (nonatomic, getter = isAutoRotate) BOOL autoRotate;

    //- (void) setCallkitEnabled:(BOOL)enabled;
    //- (void) setCallkitBannedCountries:(NSArray *)bannedCountries;
    //- (BOOL) callkitEnabled;

//=============只提供设置接口=============
    ///设置音频偏好模式，1.低延迟模式。2.高音质模式。
- (void) setAudioPreferredMode:(NXCallAudioPreferredMode) preferredMode;
    ///设置视频偏好模式，1.流畅模式。2.清晰度模式。
- (void) setVideoPreferredMode:(NXCallVideoPreferredMode) preferredMode;
    ///设置视频背景，默认没有
- (void) setGreenCurtainBackground:(UIImage *_Nonnull)image;
    ///设置callkit的图标，默认没有。
- (void) setCallkitIcon:(nullable NSData *)icon;
    ///设置callkit铃声路径，默认没有。
- (void) setCallkitRingtone:(nullable NSString *)ringtone;
    ///更新callkit来电显示名称。
- (void) updateCallkitCallerName:(nullable NSString *)name;
    ///更新国际化字符串，需要实现 代理<NXCallManagerDelegate>方法：localizedStringOfKey。
- (void) updateLocalizedString;
    ///设置视频分辨率，返回设置结果
- (BOOL)resetCallPreset:(NXCallPreset)callPreset;

@end

@protocol NXCallManager <NSObject, NXCallManagerDelegate, NXCallSetting, NXCallDelegateWillDeprecate>

/**
 *  主叫发起1-1通话
 *
 *  @param callee       被叫帐号
 *  @param mediaType    呼叫媒体类型
 *  @param option       开始通话附带的选项, 可以为空
 *  @param completion   发起此次动作是否成功结果回调。通话业务逻辑的处理结果，在NXCallManagerDelegate的回调中。
 */
- (void)callOneOne:(int64_t)callee
         mediaType:(NXCallMediaType)mediaType
               msg:(nullable NSString *)msg
        completion:(nullable NXCallHandler)completion;

/**
 *  主叫发起多方通话
 *
 *  @param callees       被叫帐号列表, 可以是nil，包含0-多个被叫帐号
 *  @param mediaType     呼叫媒体类型
 *  @param option        开始通话附带的选项, 可以为空
 *  @param completion    发起此次动作是否成功结果回调。通话业务逻辑的处理结果，在NXCallManagerDelegate的回调中。
 */
- (void)callMultiparty:(nullable NSArray *)callees
             mediaType:(NXCallMediaType)mediaType
                   msg:(nullable NSString *)msg
            completion:(nullable NXCallHandler)completion;

/**
 *  加入多方通话，根据他人分享的Session Id加入
 *
 *  @param sessionId    已建立的多方通话Session Id
 *  @param completion   发起此次动作是否成功结果回调。通话业务逻辑的处理结果，在NXCallManagerDelegate的回调中。
 */
- (void)joinMultiparty:(int64_t) sessionId
             mediaType:(NXCallMediaType)mediaType
                   msg:(nullable NSString *)msg
            completion:(nullable NXCallHandler)completion;


/**
 *  邀请加入多方通话，由通话中的成员发起
 *
 *  @param callees       被叫帐号列表, 可以是nil，包含0-多个被叫帐号
 *  @param sessionId     已建立的多方通话Session Id
 *  @param completion    发起此次动作是否成功结果回调。通话业务逻辑的处理结果，在NXCallManagerDelegate的回调中。
 */
- (void)inviteMultiparty:(nullable NSArray *)callees
               sessionId:(int64_t)sessionId
                     msg:(nullable NSString *)msg
              completion:(nullable NXCallHandler)completion;

/**
 *  踢出多方通话，由通话中的成员发起
 *
 *  @param callees       被叫帐号列表, 可以是nil，包含0-多个被叫帐号
 *  @param sessionId     已建立的多方通话Session Id
 *  @param completion    发起此次动作是否成功结果回调。通话业务逻辑的处理结果，在NXCallManagerDelegate的回调中。
 */
- (void)kickMultiparty:(nullable NSArray *)callees
             sessionId:(int64_t)sessionId
            completion:(nullable NXCallHandler)completion;

/**
 使用外部传递的SDK信令，发起通话
 @param signalBody 从另一端发来的json信令
 @return 执行信令是否成功
 */
- (BOOL)makeCallFromSignalBody:(NSString *)signalBody;

///接听当前呼叫
- (void)accept;

///拒绝当前呼叫
- (void)reject;

///取消当前发起的呼出通话
- (void)cancel;

///挂断当前通话
- (void)hangup;

///挂断当前通话并结束当前会话，服务器会挂断所有其他成员
- (void)hangupAndCloseSession;

//- (void)sendData:(nonnull NSData *)data withContext:(nonnull NXDataFrameContext *)frameContext;

- (void)sendAppCommand:(nonnull NSData *)command; // NS_DEPRECATED_IOS(1,1, "use - (void)sendAppData:(nonnull NSData *)data;");
///发送自定义数据到通话中所有人
- (void)sendAppData:(nonnull NSData *)data;

///发给单个接收人
- (void)sendAppCommand:(nonnull NSData *)command to:(int64_t)uid; // NS_DEPRECATED_IOS(1,1, "use - (void)sendAppData:(nonnull NSData *)data to:(int64_t)uid;");
///发送自定义数据到通话中指定的人
- (void)sendAppData:(nonnull NSData *)data to:(int64_t)uid;

- (void)sendCustomSignal:(nonnull NXCustomSignal *)signal;

- (void)requestRecord:(NXRecordType)recordType;

///设置需要混音的成员集合，成员为NSNumber形式的uid，list为nil时无控制，所有成员的音频都接受。非nil时，只有集合中的uid的成员的音频会被混音播放。
///用于分组讨论情况下，可以控制一个人听哪些人的音频。如果要控制一个人仅对部分人说话，那么需要发信令让所有的人自己设置这个list来决定是否听。
- (void)setAudioAcceptList:(nullable NSSet *)list;

#pragma mark - ================外部传入音频数据================
/**
 external audio的接口
 生命周期：createExternalAudioTrack创建
         destroyExternalAudioTrack销毁
 */
/**
 创建audio track id
 @param audioFormat 传入音频流对应的format
 */
- (int)createExternalAudioTrack:(AudioStreamBasicDescription)audioFormat;

/**
 销毁audio track id。
 @param trackId 要销毁的track id。
 @return -10001: id 不存在；0:销毁成功。
 */
- (int)destroyExternalAudioTrack:(int)trackId;

/**
 传入混音数据。
 @param data 传入的音频数据，长度需要大于5，并能被2整除。
 @param trackId 有效的track id。
 @return -10001: id 不存在；-10002: 长度小于4；-10003:长度不能被2整除；0: 成功。
 */
- (int)mixExternalAudio:(nonnull NSData *)data trackId:(int)trackId;

/**
 设置传入音频的音量。
 @param volume 设置音量, 0~2, 默认1。
 @param trackId 有效的track id。
 @return -10001: id 不存在；0: 成功。
 */
- (int)setExternalAudioVolume:(float)volume trackId:(int)trackId;


///SDK输出音频数据流 回调接口 - (void)onAudioRecorderData:(nonnull NSData *)data;
- (void)setAudioRecorderEnabled:(BOOL)enabled;

///TODO: 这个要再优化一下
- (nullable NXCallInfo *)currentCallInfo;

- (nullable NSString *)currentCallState;

#pragma mark - 处理APNS事件
-(BOOL)userNotificationCenter:(UNUserNotificationCenter *_Nullable)center
      willPresentNotification:(UNNotification *_Nullable)notification
        withCompletionHandler:(void (^_Nullable)(UNNotificationPresentationOptions options))completionHandler;
-(BOOL)userNotificationCenter:(UNUserNotificationCenter *_Nullable)center
didReceiveNotificationResponse:(UNNotificationResponse *_Nullable)response
        withCompletionHandler:(void(^_Nullable)(void))completionHandler;

#pragma mark - callManagerDeleagte
///添加通话委托
- (void)addDelegate:(nonnull id<NXCallManagerDelegate>)delegate;
///移除通话委托
- (void)removeDelegate:(nonnull id<NXCallManagerDelegate>)delegate;


#pragma mark - Security
//传输层加密，客户端到服务器的流量将加密传输
- (void)setTransportationEncryptionEnabled:(BOOL) enabled;
- (BOOL)isTransportationEncryptionEnabled;

//端到端加密，由app自己负责进行密钥管理和分发，客户端到客户端之间媒体内容的加密传输，服务器无法获知内容。开启端到端加密将自动关闭传输层加密


#pragma mark - Low end device
- (void)setForceLowEnd:(BOOL) forced;

#pragma mark - Test local network, for privilege grant
- (void) testLocalNetwork;

//用于视频画面的水平、垂直镜像及透视变换
#pragma mark - perspective transform
//在进入视频变换的情况下，最好把本地自己前置摄像头的水平镜像取消
- (BOOL)isMirrorHorizontalUid:(int64_t)uid;
- (BOOL)isMirrorVerticalUid:(int64_t)uid;
- (void)setMirrorHorizontal:(BOOL)mirrored uid:(int64_t)uid;
- (void)setMirrorVertical:(BOOL)mirrored uid:(int64_t)uid;

//这里坐标都是归一化坐标，x，y的取值范围都是[0.0, 1.0]。坐标(x0,y0)左上角，(x1,y1)右上角，(x2,y2)左下角，（x3,y3)右下角
//app在进入透视模式的时候先setBaseRect，也可以不设，不设的话getBaseRect时就是缺省值(0.25, 0.25), (0.75, 0.25), (0.25, 0.75), (0.75, 0.75)
//getBaseRect的返回值YES表示是自动检测出来的梯形，NO返回用setBaseRect设定的值，或者如果没有设定过，那么就是缺省值
//app调用getBaseRect，取得坐标后，按此坐标在UIView上画绿框。如果autoDetction enabled，且自动检测到了梯形，坐标会是检测到的坐标，且返回BOOL为YES。否则返回原设置值或者缺省值，返回BOOL为NO
//如果返回YES，表示自动检测到了梯形，这时app可以显示自动调整按钮，点按钮那么会把自动识别的梯形，自动拉升到UIView接近铺满的矩形。
//也可以选择不点自动调整按钮，直接在绿框的角点拖动拉伸，拖动时随时设置setPerspectiveRect，每次调用setPerspectiveRect都会实时计算并展示拉伸效果。
//过程中的数据sdk都会自动同步到其他成员，如果有成员变动也会自动同步。
//最后可以选择确认，或者取消。确认的话调confirmVideoTransform, 取消时调cancelVideoTransform
//每次进入透视调整模式，都先调用resetVideoTransform复位。不能在上次变换过的基础上再次叠加变换
//在透视变换的中途，比如把某个绿点拖了一下，然后进行了镜像，这个时候，app要自己把绿框镜像翻转，并且要重新计算送给sdk的四个顶点，还是要按照翻转后的，0左上，1右上，2左下，3右下的顺序送。这个可以通过调用sdk的getPerspectiveRect来实现。
- (BOOL)getBaseRect:(CGFloat *)x0 :(CGFloat *)y0 :(CGFloat *)x1 :(CGFloat *)y1 :(CGFloat *)x2 :(CGFloat *)y2 :(CGFloat *)x3 :(CGFloat *)y3 uid:(int64_t)uid autoDetection:(BOOL)enabled;
- (void)setBaseRect:(CGFloat)x0 :(CGFloat)y0 :(CGFloat)x1 :(CGFloat)y1 :(CGFloat)x2 :(CGFloat)y2 :(CGFloat)x3 :(CGFloat)y3 uid:(int64_t)uid;
- (void)getPerspectiveRect:(CGFloat *)x0 :(CGFloat *)y0 :(CGFloat *)x1 :(CGFloat *)y1 :(CGFloat *)x2 :(CGFloat *)y2 :(CGFloat *)x3 :(CGFloat *)y3 uid:(int64_t)uid;
- (void)setPerspectiveRect:(CGFloat)x0 :(CGFloat)y0 :(CGFloat)x1 :(CGFloat)y1 :(CGFloat)x2 :(CGFloat)y2 :(CGFloat)x3 :(CGFloat)y3 uid:(int64_t)uid;
- (void)autoPerspectiveTransform:(int64_t)uid;
- (void)confirmVideoTransformUid:(int64_t)uid;
- (void)cancelVideoTransformUid:(int64_t)uid;
- (void)resetVideoTransformUid:(int64_t)uid;

@end


#endif /* NXCallManagerProtocol_h */
