// Copyright © 2022 BytePlusRTC All rights reserved.
// SPDX-License-Identifier: MIT

#import "VertcLive.h"
#import "RTCHeader.h"
#import <TTSDKFramework/TTSDKFramework.h>
#import <TTSDKFramework/VeLivePusherDef.h>
#import "VeLivePushHelper.h"
#import "VertcHelper.h"

@interface VeLiveFpsTracker : NSObject
- (void)start;
- (void)stop;
- (void)updateCaptureVieoWidth:(int)width height:(int)height;
- (void)updatePreEncodeVieoWidth:(int)width height:(int)height;
- (void)updateCaptureFrame:(NSInteger)frameCount;
- (void)updatePreEncodeFrame:(NSInteger)frameCount;
- (void)updateAfterRTCFrame:(NSInteger)frameCount;
@end

@interface VertcLive ()
@property (nonatomic, strong) NSDictionary *options;
@property (nonatomic, strong) ByteRTCVideo *rtcEngine;
@property (nonatomic, strong) VeLivePusher *livePusher;
@property (nonatomic, strong) id<VeLiveVideoFrameListener> videoFrameListener;
@property (nonatomic, strong) VeLiveFpsTracker *fpsTracker;
@end

@implementation VertcLive

- (void)dealloc {
    if (self.livePusher && self.videoFrameListener) {
        [self.livePusher removeVideoFrameListener:self.videoFrameListener];
    }
    self.livePusher = nil;
    self.rtcEngine = nil;
    self.options = nil;
    [self.fpsTracker stop];
    self.fpsTracker = nil;
}

- (int)startLiveVideoCapture:(ByteRTCVideo *)rtc viewId:(NSString *)viewId options:(NSDictionary *)options {
    
    VeLivePusher *pusher = [VeLivePushHelper getPusher:viewId];
    if (pusher == nil) {
        @throw [NSException exceptionWithName:@"unknown videoEngine" reason:@"can't get pusher" userInfo:@{}];
    }
//    if (self.fpsTracker != nil) {
//        [self.fpsTracker stop];
//    } else {
//        self.fpsTracker = [[VeLiveFpsTracker alloc] init];
//    }
    self.rtcEngine = rtc;
    self.options = options;
    self.livePusher = pusher;

    NSInteger streamIndexValue = [options[@"streamIndex"] integerValue] ?: 0;
    ByteRTCStreamIndex streamIndex = (streamIndexValue == 1) ? ByteRTCStreamIndexScreen : ByteRTCStreamIndexMain;
    [rtc setVideoSourceType:ByteRTCVideoSourceTypeExternal WithStreamIndex:streamIndex];
    
//    [self.fpsTracker start];
    int addRes = [self.livePusher addVideoFrameListener:self];
    NSLog(@"addVideoFrameListener result: %d", addRes);
    return 0;
}

- (int)stopLiveVideoCapture {
    if (self.rtcEngine == nil || self.livePusher == nil) {
//        [self.fpsTracker stop];
        return 0;
    }
    NSInteger streamIndexValue = [self.options[@"streamIndex"] integerValue] ?: 0;
    ByteRTCStreamIndex streamIndex = (streamIndexValue == 1) ? ByteRTCStreamIndexScreen : ByteRTCStreamIndexMain;
    [self.rtcEngine setVideoSourceType:ByteRTCVideoSourceTypeInternal WithStreamIndex:streamIndex];
    
    [self.livePusher removeVideoFrameListener:self];

    self.options = nil;
    self.rtcEngine = nil;
    self.livePusher = nil;
//    [self.fpsTracker stop];
    return 0;
}

- (VeLiveVideoFrameSource)getObservedVideoFrameSource {
    return VeLiveVideoFrameSourceCapture | VeLiveVideoFrameSourcePreEncode;
}

- (ByteRTCVideoPixelFormat)convertToRtcPixelFormat:(VeLivePixelFormat)format {
    switch (format) {
        case VeLivePixelFormatBGRA32: return ByteRTCVideoPixelFormatBGRA;
        case VeLivePixelFormatNV12: return ByteRTCVideoPixelFormatNV12;
        case VeLivePixelFormatNV21: return ByteRTCVideoPixelFormatNV21;
        case VeLivePixelFormatI420: return ByteRTCVideoPixelFormatI420;
        case VeLivePixelFormat2DTexture: return ByteRTCVideoPixelFormatGLTexture2D;
        case VeLiveVideoBufferTypeUnKnown: return ByteRTCVideoPixelFormatUnknown;
    }
    return ByteRTCVideoPixelFormatUnknown;
}

- (void)onCaptureVideoFrame:(VeLiveVideoFrame *)frame {
    [self.fpsTracker updateCaptureFrame:1];
    [self.fpsTracker updateCaptureVieoWidth:frame.width height:frame.height];
}

- (void)onPreEncodeVideoFrame:(VeLiveVideoFrame *)frame {
//    [self.fpsTracker updatePreEncodeFrame:1];
//    [self.fpsTracker updatePreEncodeVieoWidth:frame.width height:frame.height];
    NSInteger streamIndexValue = [self.options[@"streamIndex"] integerValue] ?: 0;
    ByteRTCStreamIndex streamIndex = (streamIndexValue == 1) ? ByteRTCStreamIndexScreen : ByteRTCStreamIndexMain;
    int res = 0;
    if (streamIndex == ByteRTCStreamIndexMain) {
        ByteRTCVideoPixelFormat pixelFormat = [self convertToRtcPixelFormat:frame.pixelFormat];
        ByteRTCVideoFrame *videoFrame = [[ByteRTCVideoFrame alloc] init];
        videoFrame.width = frame.width;
        videoFrame.height = frame.height;
        videoFrame.format = (int)pixelFormat;
        videoFrame.time = frame.pts;
        // videoFrame.contentType = ByteRTCVideoContentTypeNormalFrame;
        // same enum
        videoFrame.rotation = (ByteRTCVideoRotation)frame.rotation;
        
        if (frame.sampleBuffer) {
            videoFrame.textureBuf = CMSampleBufferGetImageBuffer(frame.sampleBuffer);
            videoFrame.time = CMSampleBufferGetPresentationTimeStamp(frame.sampleBuffer);
        }
        
        if (frame.pixelBuffer) {
            videoFrame.textureBuf = frame.pixelBuffer;
        }
        
        res = [self.rtcEngine pushExternalVideoFrame: videoFrame];
        
//        [self.fpsTracker updateAfterRTCFrame:1];
    }
    if (streamIndex == ByteRTCStreamIndexScreen) {
        res = [self.rtcEngine pushScreenVideoFrame:frame.pixelBuffer time:frame.pts rotation:frame.rotation];
//        [self.fpsTracker updateAfterRTCFrame:1];
    }
    if (res != 0 && res != -202) {
        NSLog(@"bytertc push video frame fail return '%d'", res);
    }
}

@end

@protocol VeLiveTrackerProtocol <NSObject>
+ (void)sendPusherEventWith:(NSString *)eventKey api:(nullable NSString *)api value:(nullable id)value result:(nullable id)result;
@end

@interface VeLiveFpsTracker ()
@property (nonatomic, strong) Class <VeLiveTrackerProtocol> trackerCls;
@property (nonatomic, assign) NSTimeInterval lastTimestamp;
@property (nonatomic, assign) NSInteger captureFrameCount;
@property (nonatomic, assign) NSInteger preEncodeFrameCount;
@property (nonatomic, assign) NSInteger afterRtcFrameCount;
@property (nonatomic, strong) dispatch_queue_t queue;
@property (nonatomic, copy) NSString *timerName;
@property (nonatomic, assign) CGSize captureSize;
@property (nonatomic, assign) CGSize preEncodeSize;
@end

@implementation VeLiveFpsTracker
- (instancetype)init {
    if (self = [super init]) {
        _timerName = @"VeLiveFpsTracker";
        _trackerCls = NSClassFromString(@"VeLiveTracker");
        _queue = dispatch_queue_create("VeLiveFpsTracker-Queue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)start {
    [self clearFrameRecordAsync:NSDate.date.timeIntervalSince1970 * 1000];
    __weak __typeof(self)weakSelf = self;
    [[VertcHelper getInstance] schedualTimerWithIdentifier:self.timerName
                                                  interval:2
                                                     queue:self.queue
                                                   repeats:YES
                                                    action:^{
        __strong __typeof(weakSelf)self = weakSelf;
        [self reportIfNeed];
    }];
}

- (void)stop {
    [[VertcHelper getInstance] cancelTimerWithName:_timerName];
    [self clearFrameRecordAsync:0];
}

- (void)clearFrameRecordAsync:(NSTimeInterval)timestamp {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(self.queue, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        [self clearFrameRecord:timestamp];
    });
}

- (void)clearFrameRecord:(NSTimeInterval)timestamp {
    self.lastTimestamp = timestamp;
    self.captureFrameCount = 0;
    self.preEncodeFrameCount = 0;
    self.afterRtcFrameCount = 0;
    self.captureSize = CGSizeZero;
    self.preEncodeSize = CGSizeZero;
}

- (void)updateCaptureFrame:(NSInteger)frameCount {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(self.queue, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        self.captureFrameCount += frameCount;
    });
}

- (void)updatePreEncodeFrame:(NSInteger)frameCount {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(self.queue, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        self.preEncodeFrameCount += frameCount;
    });
}

- (void)updateAfterRTCFrame:(NSInteger)frameCount {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(self.queue, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        self.afterRtcFrameCount += frameCount;
    });
}

- (void)updateCaptureVieoWidth:(int)width height:(int)height {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(self.queue, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        self.captureSize = CGSizeMake((CGFloat)width, (CGFloat)height);
    });
}

- (void)updatePreEncodeVieoWidth:(int)width height:(int)height {
    __weak __typeof(self)weakSelf = self;
    dispatch_async(self.queue, ^{
        __strong __typeof(weakSelf)self = weakSelf;
        self.preEncodeSize = CGSizeMake((CGFloat)width, (CGFloat)height);
    });
}

- (void)reportIfNeed {
    NSTimeInterval currentTime = NSDate.date.timeIntervalSince1970 * 1000;
    NSTimeInterval duration = MAX(1, currentTime - self.lastTimestamp);
    
    int captureFps = (int)((self.captureFrameCount * 1000.0) / duration);
    int preEncodeFps = (int)((self.preEncodeFrameCount * 1000.0) / duration);
    int afterRtcFps = (int)((self.afterRtcFrameCount * 1000.0) / duration);
    
    [self reportEvent:@"livcore_statistics_fps" value:@{
        @"captureFps" : @(captureFps),
        @"preEncodeFps" : @(preEncodeFps),
        @"afterRtcFps" : @(afterRtcFps),
        @"captureSize" : NSStringFromCGSize(self.captureSize),
        @"preEncodeSize" : NSStringFromCGSize(self.preEncodeSize),
    }];
    [self clearFrameRecord:currentTime];
}

- (void)reportEvent:(NSString *)key value:(NSDictionary *)value {
    if (self.trackerCls != nil
        && [self.trackerCls respondsToSelector:@selector(sendPusherEventWith:api:value:result:)]) {
        [self.trackerCls sendPusherEventWith:key api:@"" value:value result:@""];
    }
#if DEBUG
    NSLog(@"report: %@ %@", key, value);
#endif
}

@end
