//
//  AliyunCameraService.h
//  AliyunUtils
//
//  Created by 胡锦涛 on 2020/6/16.
//  Copyright © 2020 胡锦涛. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

#if defined(__cplusplus)
    #define ALIYUN_EXPORT extern "C"
#else
    #define ALIYUN_EXPORT extern
#endif


/// NSString keys that are used to specify configuration in AliyunCameraService initialization.
typedef NSString* const AliyunCameraInitKey;

/// NSString keys that are used to change configurations of AliyunCameraService;
typedef NSString* const AliyunCameraConfigKey;

/**
 Funtion type signature that is used for -takePicture: callbacks after a still image is captured.
 
 @param imageDataSampleBuffer The sample buffer of the still image. It is nil if any error occurred.
 @param error NSError that indicates any error during the capture process. It is nil if no error occurred.
 */
typedef void(^AliyunCameraTakePhotoCompletion)(CMSampleBufferRef _Nonnull imageDataSampleBuffer, NSError * _Nonnull error);

/// Initialization Key for specifying device position during initialization. Associated value must be a AVCaptureDevicePosition.
ALIYUN_EXPORT AliyunCameraInitKey _Nonnull AliyunCameraInitKeyCaptureDevicePosition;

/// Initialization Key for specifying quality preset during initialization. Associated value must be a AVCaptureSessionPreset.
ALIYUN_EXPORT AliyunCameraInitKey _Nonnull AliyunCameraInitKeySessionPreset;

/// Initialization Key for specifying video orientation during initialization. Associated value must be a AVCaptureVideoOrientation.
ALIYUN_EXPORT AliyunCameraInitKey _Nonnull AliyunCameraInitKeyVideoOutputOrientation;

/// Initialization Key for specifying video mode during initialization. Associated value must be a AliyunCameraMode
ALIYUN_EXPORT AliyunCameraInitKey _Nonnull AliyunCameraInitKeyMode;

/**
 Configuration Key for specifying focus point of interest. Associated value must be a CGPoint (converted to an NSString).
 
 Note that the associated value will be directly applied to camera without transformations, so regarding valid inputs please review focusPointOfInterest of AVCaptureDevice;
 */
ALIYUN_EXPORT AliyunCameraConfigKey _Nonnull AliyunCameraConfigKeyFocusPointOfInterest;

/// Error domain name for AliyunCameraService
ALIYUN_EXPORT NSString * _Nonnull const kAliyunCameraErrorDomain;


/// Error codes used by AliyunCameraService to indicate the type of errors occured.
typedef NS_ENUM(NSInteger, AliyunCameraErrorCode) {
    
    /// Error code indicates that an invalid preset string is given during initialization.
    AliyunCameraUnsupportedPreset,
    
    /// Error code indicates that no devices are available with the specified configuration.
    AliyunCameraNotAvailable,
    
    /// Error code indicates that the capture session has failed.
    AliyunCameraSessionFailure,
};

/// Modes that AliyunCameraService supports
typedef NS_ENUM(NSInteger, AliyunCameraMode) {
    
    /// Video
    AliyunCameraModeVideo,
    
    /// Still Image
    AliyunCameraModeStillImage
    
};


/**
 A structure that stores camera configuration for associated camera service
 */
typedef struct camera_configuration_t{
    /// Focus Point of Interest
    CGPoint focusPointOfInterest;
}camera_configuration;


/**
 Delegate to comply in order to receive camera output.
 */
@protocol AliyunCameraServiceDelegate <NSObject>

@optional
/**
 This function will be called by AliyunCameraService to pass capture device output to the delegate.
 
 Implement this optional function and call -setDelegate: if you have set AliyunCameraModeVideo and wish to receive output.

 @param captureOutput Output source that triggered this delegation.
 @param sampleBuffer A CMSampleBuffer output from said output source.
 @param connection The AVCaptureConnection related to the output.
 */
- (void)cameraControllerCaptureOutput:(AVCaptureOutput *_Nonnull)captureOutput
                didOutputSampleBuffer:(CMSampleBufferRef _Nonnull )sampleBuffer
                       fromConnection:(AVCaptureConnection *_Nonnull)connection;

@end

NS_ASSUME_NONNULL_BEGIN

@interface AliyunCameraService : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate>

/// Property that allows inspections the current configuration of AliyunCameraService
@property(nonatomic, assign, readonly)camera_configuration configuration;

/// Preview layer of the capture session. Add this layer as a sublayer to a view to inspect the device output.
@property(nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

#if DEBUG
@property(nonatomic, strong) AVSampleBufferDisplayLayer *mockDisplayLayer;
#endif


/**
 Do not use default NSObject initializer. Use initWithConfig:error: instead.
 */
- (instancetype)init NS_UNAVAILABLE;

/**
 Initialize the camera service.
 
 If AliyunCameraInitKeyCaptureDevicePosition is not specified in config, AVCaptureDevicePositionFront will be used as camera position.
 
 If AliyunCameraInitKeySessionPreset is not specified in config, AVCaptureSessionPresetHigh will be used as quality preset.
 
 If AliyunCameraInitKeyVideoOutputOrientation is not specified in config, output orientation will not be set. Video will be AVCaptureVideoOrientationLandscapeRight, and StillImage will be AVCaptureVideoOrientationPortrait.
 
 If AliyunCameraInitKeyMode is not specified in config, AliyunCameraModeVideo will be used as video output.

 @param config An NSDictionary that contains configuration keys and associated values to specify initialization requirements.
 @param errPtr An optional error receiver for initialization failures. Use nil if error information is not needed.
 @return If initialization is successful, a instance of AliyunCameraService is returned. Anything failture during initialization would cause this initialzer to return nil.
 */
- (instancetype)initWithConfig:(NSDictionary *)config error:(NSError **)errPtr;

/**
 Set delegate that AliyunCameraService can service

 @param delegate An instance of class that implements AliyunCameraServiceDelegate
 */
- (void)setDelegate:(id<AliyunCameraServiceDelegate>) delegate;

/**
 Change one configuration value in camera_configuration.

 @param key Configuration key
 @param value Properly formatted in-range value as a string
 @return YES if configuration was successfully changed. NO otherwise.
 */
- (BOOL)changeConfiguration:(NSString *)key
               desiredValue:(NSString *)value;

/**
 Start the capture session.
 
 The capture session needs to be started manually. This is a synchronous operation.
 */
- (void)startCamera;

/**
 Stop the capture session.
 
 This is a synchronous operation.
 */
- (void)stopCamera;

/**
 * 获取视频输出的分辨率尺寸
 * @return 视频输出的宽高尺寸
 */
- (CGSize)videoOutputSize;

/**
 Capture a still image asynchronously.
 
 @param completion Completion delegate when the capture process finishes. For required parameters see AliyunCameraTakePhotoCompletion.
 */
- (void)takePicture:(AliyunCameraTakePhotoCompletion)completion;

#if DEBUG
/**
 * 设置是否启用Mock模式
 * @param enabled YES表示启用Mock模式，NO表示关闭
 */
+ (void)setMockEnabled:(BOOL)enabled;

/**
 * 查询当前是否处于Mock模式
 * @return YES表示Mock模式已启用
 */
+ (BOOL)isMockEnabled;

/**
 * 获取当前Mock模式下的相机服务实例
 * @return 当前Mock实例，若未启用Mock则返回nil
 */
+ (nullable instancetype)currentMockInstance;

/**
 * 注入模拟的样本缓冲区数据用于测试
 * @param sampleBuffer 要注入的CMSampleBuffer数据
 */
- (void)injectSampleBuffer:(CMSampleBufferRef)sampleBuffer;
#endif

@end

NS_ASSUME_NONNULL_END
