//
//  AAILivenessWrapViewController.h
//  AAILivenessDemo
//
//  Created by Advance.ai on 2019/3/2.
//  Copyright © 2019 Advance.ai. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <AAILivenessSDK/AAILivenessSDK.h>

#if __has_include(<AAILivenessUI/AAILivenessUtil.h>)
#import <AAILivenessUI/AAILivenessUtil.h>
#else
#import "AAILivenessUtil.h"
#endif

NS_ASSUME_NONNULL_BEGIN

/// This class is main page of liveness detection. It contains all the detection logic as well as the key UI parts.
///
/// You can implement custom UI and other logic by inheriting from this class.
__attribute__((visibility("default")))
@interface AAILivenessViewController : UIViewController
{
    UIButton *_backBtn;
    UILabel *_stateLabel;
    UIImageView *_stateImgView;
    //Voice
    UIButton *_voiceBtn;
    //Time label
    UILabel *_timeLabel;
    CGRect _roundViewFrame;
}

/// @since 1.2.3
@property(nonatomic, strong) AAILivenessWrapView *wrapView;

/// @since 3.0.5
@property(nonatomic, readonly) CGRect roundViewFrame;

/// @since 3.1.0
@property(nonatomic, readonly) CGRect ellipseFrame;

/// @since 1.2.3
@property(nonatomic) AAILivenessUtil *util;

/// @abstract Whether to display default HUD(loading view) in the network request. The default is YES.
/// @discussion
/// You can set this value to NO, then implement the `beginRequestBlk` and `endRequestBlk` to show your customized loading view
///
/// @since 1.2.3
@property(nonatomic) BOOL showHUD;

/// @abstract Whether to allow to play prompt audio, the default is NO.
///
/// @discussion
/// Default value has changed:
/// - v3.4.0 and above: NO (disabled by default)
/// - Before v3.4.0: YES (enabled by default)
///
/// @since 2.0.4
@property(nonatomic) BOOL playAudio;

/// @abstract Whether to display animation images, the default is YES. If you set NO, then animation images will be hidden.
/// @since 2.0.4
@property(nonatomic) BOOL showAnimationImg;

/// @abstract Whether to mark the action of tapping back button as "user_give_up". The default is NO. If you set YES, the `detectionFailureBlk`
/// will be called when the user taps the top left back button while liveneness detection is running.
@property(nonatomic) BOOL recordUserGiveUp;

/// @abstract Specify which language to use for the SDK. The default is nil, which means the SDK will use the system language.
///
/// @discussion
/// The languages currently supported by sdk are as follows:
/// @code
/// "en" "id"  "vi"  "zh-Hans"  "th"  "es"  "ms" "hi" "fil"
/// @endcode
///
/// @since 1.2.9
@property(nonatomic, copy, nullable) NSString *language;

/// @abstract The font used for text displayed in the liveness UI. Default is nil (system font).
/// @since 4.1.4
@property(nonatomic, strong, nullable) UIFont *font;

/// @abstract Specify AAILoadingHud brand color. Default is 0x5BC413.
/// @since 3.4.0
@property(nonatomic, strong) UIColor *hudBrandColor;

/// @abstract Liveness detection configuration items.
/// @since 4.0.0
@property(nonatomic, readonly) AAILivenessConfig *livenessConfig;

/// @abstract The latest liveness detection stage.
/// @since 4.0.0
@property(nonatomic, readonly) AAILivenessStage livenessStage;

/// @abstract Call when SDK begin send request.
/// @discussion If needed, you can implement this block to show your own loading view.
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^beginRequestBlk)(AAILivenessViewController *rawVC);

/// @abstract Call when SDK request complete.
/// @discussion If needed, you can implement this block to dismiss your own loading view.
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^endRequestBlk)(AAILivenessViewController *rawVC, NSDictionary * _Nullable errorInfo);

/// @abstract Indicates that the liveness stage is about to be changed.
///
/// @parameter rawVC: AAILivenessViewController.
/// @parameter livenessStage: The liveness stage that will be enter.
///
/// @parameter info: Additional information about the toDetectionType, it will contain "key" and "state" fields.
/// @since 4.0.0
@property(nonatomic, copy, nullable) void (^livenessStageChangedBlk)(AAILivenessViewController *rawVC, AAILivenessStage livenessStage, NSDictionary *info);

/// @abstract Call when liveness detection failed or timeout etc.
///
/// @discussion
///  rawVC: AAILivenessViewController.
///
///  failureResult: Detailed information about the failure.
///
/// @since 4.0.0
@property(nonatomic, copy, nullable) void (^detectionFailureBlk)(AAILivenessViewController *rawVC, AAILivenessFailureResult *failureResult);

/// @abstract Call when liveness detection success and complete.
/// @since 4.0.0
@property(nonatomic, copy, nullable) void (^detectionSuccessBlk)(AAILivenessViewController *rawVC, AAILivenessSuccessResult *successResult);

/// @abstract The remaing detection time of the current liveness stage, this block is called about once per second.
/// @since 4.0.0
@property(nonatomic, copy, nullable) void (^detectionRemainingTimeBlk)(AAILivenessViewController *rawVC, AAILivenessStage livenessStage, NSTimeInterval remainingTime);

/// @abstract Called after the `AAILivenessWrapView` is created.
/// @param wrapView AAILivenessWrapView
///
/// @discussion You can override this method to customize wrapView UI.
/// For more details, see the comment code in the implementation part of this method.
///
/// @since 1.2.3
- (void)livenessWrapViewDidLoad:(AAILivenessWrapView *)wrapView;

/// @abstract Call before layout the  `AAILivenessWrapView`.
/// @param wrapView AAILivenessWrapView
///
/// @discussion You can override this method to configure the wrapView's to display liveness view in full screen.
/// For more details, see the comment code in the implementation part of this method.
///
/// @since 1.2.3
- (void)livenessWrapViewWillLayout:(AAILivenessWrapView *)wrapView;

/// @abstract Call after the   `_backButton`, `_stateLabe`, `_stateImgView`, `_voiceBtn`, `_timeLabel` are created.
/// @discussion You can override this method to make some additional settings for these UI controls, or add your UI controls.
/// @since 1.2.3
- (void)loadAdditionalUI;

/// @abstract Call after the additional UI controls layout. Default this method will laytout the  `_backButton`, `_stateLabe`l, `_stateImgView`, `_voiceBtn`, `_timeLabel`.
///
/// @discussion You can override this method to make some additional frame settings for these UI controls.
/// @since 1.2.3
- (void)layoutAdditionalUI;

/// @abstract This method will be called when the audio is about to be played. Default this method will auto play audio.
/// @param audioName Audio name
///
/// @discussion You can override this method to play custom audio, or disable play audio.
/// As long as the `[super willPlayAudio:]` method is not called, the audio will not be played.
///
/// @since 1.2.3
- (void)willPlayAudio:(NSString *)audioName;

/// @abstract This method will be called when the `_stateLabel` needs to be updated.
/// @param state Local language string of the key
/// @param key The state key
/// @discussion You can override this method to make some additional settings for `_stateLabel`, such as frame, content, etc.
///
/// In other words, this method is always called when the UI needs to update, and can essentially serve as a replacement for the unavailable  property `frameDetectedBlk`."
///
/// All available key as follows:
/// @code
/// "no_face"
/// "move_further"
/// "move_closer"
/// "move_center"
/// "frontal"
/// "stay_still"
/// "pls_blink"
/// "pls_turn_head"
/// "pls_open_mouth"
/// "face_occ"
/// "pls_open_eye"
/// "low_light"
/// "high_light"
/// "face_move_left"
/// "face_move_right"
/// "face_move_upper"
/// "face_move_down"
/// "pls_blink"
/// "warn_muti_face"
///
/// "fail_reason_timeout"
/// "fail_reason_muti_face"
/// "fail_reason_facemiss_pos_yaw"
/// "fail_reason_facemiss_blink_mouth"
/// "fail_reason_much_action"
/// "no_camera_permission"
/// "device_not_support"
/// "detection_success"
/// @endcode
///
/// @since 1.2.3
- (void)updateStateLabel:(NSString * _Nullable)state key:(NSString * _Nullable)key;

/// @abstract This method will be called when the liveness view and other UI controls need to be reset.
/// @discussion You can override this method to reset your own UI cotrols.
/// @since 1.2.3
- (void)resetViewState;

/// @abstract When liveness detection failed, you can call this method to retry it.
/// @warning Calling this method while liveness detection is running is prohibited. The SDK will ignore any such calls.
/// @since 1.2.3
- (void)restartDetection;

/// @abstract Stop the liveness detection.
/// @since 4.0.0
- (void)stopDetection;

/// @abstract This method will be called when the back button is clicked.
/// @discussion You can override this method to perform your own back actions.
/// @since 1.2.3
- (void)tapBackBtnAction;

/// @abstract This method will be called when the voice button is clicked.
/// @discussion You can override this method to perform your own actions.
/// @since 1.2.3
- (void)tapVoiceBtnAction:(UIButton *)btn;

/// @abstract This method will be called when the image of  `_stateImgView` needs to be updated.
/// @param fromLivenessStage Current liveness stage
/// @param toLivenessStage Next liveness stage
///
/// @discussion You can override this method to show your own image or other configurations about `_stateImgView`.
/// @since 4.0.0
- (void)updateImgWhenStageChange:(AAILivenessStage)fromLivenessStage to:(AAILivenessStage)toLivenessStage;

/// @abstract This method will be called when the ellipse(avatar area) frame changs.
///
/// @discussion
/// You can override this method to perform your own actions. For example, you can
/// ajust your UI controls' position according to the ellipse frame.
///
/// @param context Ellipse transition context
/// @since 3.1.2
- (void)ellipseWillTransition:(AAIEllipseTrasitionContext * _Nonnull)context;

/// @abstract This method will be called before the detection starts.
/// @discussion You can override this method to perform your own actions.
/// @note This method is called on camera queue. You should avoid performing time-consuming operations.
/// @since 3.4.0
- (void)beforeStartDetection;

/// @abstract This method will be called when the origin frame is received.
/// @discussion You can override this method to perform your own actions.
/// @note This method is called on camera queue. You should avoid performing time-consuming operations.
/// @since 3.4.0
- (void)didReceivedOriginFrame:(AAILivenessFrame *)originFrame;

/// @abstract This method will be called when detection stopped(detection success case).
/// @discussion You can override this method to perform your own actions.
/// @note This method is called on main thread.
/// @since 4.0.0
- (void)detectionDidStopWithSuccess:(AAILivenessSuccessResult *)successResult;

/// @abstract This method will be called when detection stopped(detection failure case).
/// @discussion You can override this method to perform your own actions.
/// @note This method is called on main thread.
/// @since 4.0.0
- (void)detectionDidStopWithFailure:(AAILivenessFailureResult *)failureResult;

@end


/*=========================================================================+
 |                               !!!WARNING                                |
 |NOTE THE FOLLOWING PROPERTIES AND METHODS NO LONGER AVAILABLE FROM V4.0.0|
 +=========================================================================*/


@interface AAILivenessViewController(DeprecatedOrUnavailable)

/// @abstract Detects if the phone is in the vertical orientation during the preparation phase. The default is NO.
/// @since 1.2.3
@property(nonatomic) BOOL detectPhonePortraitDirection AAI_LD_UNAVAILABLE_FROM(4.0.0);

/// @abstract The list of detection actions, reference to AAIDetectionType, note that the `AAIDetectionTypeNone` will be ignored in this array.
/// By default, the order of the three actions is random.
///
/// @discussion
/// You can configure this value like this:
/// @code
/// vc.detectionActions = @[@(AAIDetectionTypeMouth), @(AAIDetectionTypePosYaw), @(AAIDetectionTypeBlink)];
/// @endcode
///
/// @since 1.2.3
@property(nonatomic, copy, nullable) NSArray<NSNumber *> *detectionActions AAI_LD_UNAVAILABLE_FROM(4.0.0);

/// @abstract Set the timeout for prepare stage, default is 50s.
///
/// @discussion
/// This value refers to the time from when the sdk page is displayed to when the motion detection is ready.
/// For example, after the sdk page is presented, if the user does not hold the phone upright or put the face in the detection area,
/// and continues in this state for a certain period of time, then the `detectionFailureBlk` will be called,
/// and the value of the "key" in errorInfo is "fail_reason_prepare_timeout".
///
/// @since 1.2.9
@property(nonatomic) NSInteger prepareTimeoutInterval AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `livenessConfig.silentTimeout` instead");

/// @abstract Set the timeout for action detection, default is 10s.
///
/// @discussion
/// Note that this value represents the timeout for an action,
/// not the total timeout for all actions.
///
/// @since 1.2.8
@property(nonatomic) NSInteger actionTimeoutInterval AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `livenessConfig.actionTimeout` instead");

/// @abstract Set the detection timeout duration of 3D mode(near/distant mode), default is 50s.
///
/// @discussion
/// If the detection cannot pass within this time duration,  the `detectionFailureBlk` will be called,
/// and the value of the "key" in errorInfo is "fail_reason_prepare_timeout".
///
/// @note This time duration does not include network request duration.
/// @since 3.0.4
@property(nonatomic) NSInteger timeoutDurationOf3DMode AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `livenessConfig.distantNearTimeout` instead");

/// @abstract When the SDK starts, the SDK will check the camera permissions, and if the camera permission is denied, this block will be called.
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^cameraPermissionDeniedBlk)(AAILivenessViewController *rawVC) AAI_LD_UNAVAILABLE_FROM(4.0.0);

/// @abstract Indicates that the liveness detection is ready and the first action(detectionType) is about to be detected.
///
/// - parameter rawVC AAILivenessViewController
/// - parameter detectionType The first action type that will be detected.
/// - parameter info Additional information about the detectionType, it will contain "key" and "state" fields.
///
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^detectionReadyBlk)(AAILivenessViewController *rawVC, AAIDetectionType detectionType, NSDictionary *info) AAI_LD_UNAVAILABLE_FROM(4.0.0);

/// @abstract Called whenever a new video frame is detected.
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^frameDetectedBlk)(AAILivenessViewController *rawVC, AAILivenessStage livenessStage, AAIActionStatus status, AAIDetectionResult result, NSDictionary *info) AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `- (void)updateStateLabel:(NSString * _Nullable)state key:(NSString * _Nullable)key` instead");

/// @abstract Indicates that the liveness detection type is about to be changed.
///
/// - parameter rawVC AAILivenessViewController.
/// - parameter toDetectionType The action type that will be detected.
/// - parameter info Additional information about the toDetectionType, it will contain "key" and "state" fields.
///
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^detectionTypeChangedBlk)(AAILivenessViewController *rawVC, AAIDetectionType toDetectionType, NSDictionary *info) AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `livenessStageChangedBlk` instead");

/// @abstract Call when liveness detection failed or timeout.
///
/// @discussion
/// - parameter rawVC AAILivenessViewController.
///
/// - parameter info Detailed information about the failure. If it is a network request failure, it will contain "message", "code", "transactionId" fields.
///  If it is other failure reason, it will contain "key" and "state" fields, you can distinguish the different failure types by the "key" field.
///  e.g. When detection timeout, the value of the "key" will be "fail_reason_timeout".
///
/// @code
/// /*
/// This `errorInfo` value currently contains only two cases:
/// One is the case of a failed network request, for which the following three fields are available.
/// "message"
/// "code"
/// "transactionId"
///
/// The other is the case of failed action detection or action detection timeout, which corresponds to the following fields.
/// "key"
/// "state"
///
/// The values of the `key` are as follows:
/// "fail_reason_prepare_timeout"
/// "fail_reason_timeout"
/// "fail_reason_muti_face"
/// "fail_reason_facemiss_blink_mouth"
/// "fail_reason_facemiss_pos_yaw"
/// "fail_reason_much_action"
/// "user_give_up"    // This value only be available when `recordUserGiveUp` is enabled.
///
/// Generally there is no need to focus on this, just let the client to retry once detectionFailedBlk is called.
/// */
/// @endcode
///
/// @since 1.2.3
@property(nonatomic, copy, nullable) void (^detectionFailedBlk)(AAILivenessViewController *rawVC, NSDictionary *errorInfo) AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `detectionFailureBlk` instead");

/// @abstract This method will be called when the image of  `_stateImgView` needs to be updated.
/// @param detectionType Current detection type
///
/// @discussion You can override this method to show your own image or other configurations about `_stateImgView`.
/// @since 1.2.3
- (void)showImgWithType:(AAIDetectionType)detectionType AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `updateImgWhenStageChange:to` instead");

/// @abstract This method will be called when detection stopped(detection complete or failed).
/// @discussion You can override this method to perform your own actions.
/// @note This method is called on main thread.
/// @since 3.4.0
- (void)didStopDetection AAI_LD_UNAVAILABLE_FROM(4.0.0, "Using `detectionDidStopWithFailure:` or  `detectionDidStopWithSuccess:` instead");

@end
NS_ASSUME_NONNULL_END
