#ifdef RCT_NEW_ARCH_ENABLED
#import "RNScanbotDocumentScannerView.h"

#import <ScanbotSDK/ScanbotSDK-Swift.h>
#import <ScanbotSDKNativeWrapper/ScanbotSDKNativeWrapper-Swift.h>

#if __has_include(<RNScanbotSDK/RNScanbotSDK-Swift.h>)
#import <RNScanbotSDK/RNScanbotSDK-Swift.h>
#else
#import "RNScanbotSDK-Swift.h"
#endif

using namespace facebook::react;

@interface RNScanbotDocumentScannerView()  <RCTScanbotDocumentScannerViewViewProtocol, RNScanbotDocumentScannerEventDelegate>
@property (strong, nonatomic) RNScanbotDocumentScannerViewController *scannerViewController;
@property (nonatomic) BOOL attached;
@end

@implementation RNScanbotDocumentScannerView

+ (ComponentDescriptorProvider)componentDescriptorProvider {
    return concreteComponentDescriptorProvider<ScanbotDocumentScannerViewComponentDescriptor>();
}

static const auto defaultProps = std::make_shared<const ScanbotDocumentScannerViewProps>();

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        _props = defaultProps;
        self.attached = false;
        
        self.scannerViewController = [[RNScanbotDocumentScannerViewController alloc] init];
        [self.scannerViewController setDefaults];
        self.scannerViewController.delegate = self;
    }
    return self;
}

- (void)didMoveToWindow {
    [super didMoveToWindow];
    
    if(self.window && !self.attached){
        [self.scannerViewController attachControllerWithParentViewController:[self reactViewController] inView:self];
        self.attached = true;
    }
}

- (void) prepareForRecycle {
    self.scannerViewController.delegate = nil;
    [self.scannerViewController detachController];
    self.scannerViewController = nil;

    self.attached = false;
    _props = defaultProps;
    [super prepareForRecycle];
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
    
    if (self.scannerViewController == nil) {
        self.scannerViewController = [[RNScanbotDocumentScannerViewController alloc] init];
        [self.scannerViewController setDefaults];
        self.scannerViewController.delegate = self;
    }
    
    const auto &oldViewProps = *std::static_pointer_cast<ScanbotDocumentScannerViewProps const>(_props);
    const auto &newViewProps = *std::static_pointer_cast<ScanbotDocumentScannerViewProps const>(props);
    
    // MARK: General settings
    if(oldViewProps.flashEnabled != newViewProps.flashEnabled){
        [self.scannerViewController flashEnabled: newViewProps.flashEnabled];
    }
    if(oldViewProps.hardwareButtonsEnabled != newViewProps.hardwareButtonsEnabled){
        [self.scannerViewController hardwareButtonsEnabled:newViewProps.hardwareButtonsEnabled];
    }
    if(oldViewProps.detectDocumentAfterSnap != newViewProps.detectDocumentAfterSnap){
        [self.scannerViewController detectDocumentAfterSnap:newViewProps.detectDocumentAfterSnap];
    }
    // MARK: Document Configuration
    if(oldViewProps.acceptedAngleScore != newViewProps.acceptedAngleScore){
        [self.scannerViewController acceptedAngleScore: @(newViewProps.acceptedAngleScore)];
    }
    if(oldViewProps.acceptedBrightnessThreshold != newViewProps.acceptedBrightnessThreshold){
        [self.scannerViewController acceptedBrightnessThreshold:@(newViewProps.acceptedBrightnessThreshold)];
    }
    if(oldViewProps.acceptedSizeScore != newViewProps.acceptedSizeScore){
        [self.scannerViewController acceptedSizeScore:@(newViewProps.acceptedSizeScore)];
    }
    if(oldViewProps.autoSnappingEnabled != newViewProps.autoSnappingEnabled){
        [self.scannerViewController autoSnappingEnabled:newViewProps.autoSnappingEnabled];
    }
    if(oldViewProps.autoSnappingSensitivity != newViewProps.autoSnappingSensitivity){
        [self.scannerViewController autoSnappingSensitivity:@(newViewProps.autoSnappingSensitivity)];
    }
    if(oldViewProps.autoSnappingDelay != newViewProps.autoSnappingDelay){
        [self.scannerViewController autoSnappingDelay:@(newViewProps.autoSnappingDelay)];
    }
    if(oldViewProps.ignoreOrientationMismatch != newViewProps.ignoreOrientationMismatch){
        [self.scannerViewController ignoreOrientationMismatch:newViewProps.ignoreOrientationMismatch];
    }
    if(oldViewProps.partiallyVisibleDocumentConfiguration != newViewProps.partiallyVisibleDocumentConfiguration){
        [self.scannerViewController
         partiallyVisibleDocumentConfigurationWithAllowPartiallyVisibleDocuments:newViewProps.partiallyVisibleDocumentConfiguration.allowPartiallyVisibleDocuments
         accumulationDuration:newViewProps.partiallyVisibleDocumentConfiguration.accumulationDuration
         retentionTime:newViewProps.partiallyVisibleDocumentConfiguration.retentionTime
         minimumBrightness:newViewProps.partiallyVisibleDocumentConfiguration.minimumBrightness
        ];
    }
    if(oldViewProps.acceptedAspectRatioScore != newViewProps.acceptedAspectRatioScore){
        [self.scannerViewController acceptedAspectRatioScore:@(newViewProps.acceptedAspectRatioScore)];
    }
    if(oldViewProps.requiredAspectRatios != newViewProps.requiredAspectRatios){
        NSMutableArray *aspectRatios = [NSMutableArray array];
        for (ScanbotDocumentScannerViewRequiredAspectRatiosStruct value : newViewProps.requiredAspectRatios) {
            [aspectRatios addObject:@{
                @"width": [NSNumber numberWithDouble:value.width],
                @"height": [NSNumber numberWithDouble:value.height]
            }];
        }
        [self.scannerViewController requiredAspectRatios:aspectRatios];
    }
    // MARK: Camera configuration
    if(![self compareStringProps:oldViewProps.cameraModule withString:newViewProps.cameraModule]){
        [self.scannerViewController cameraModule: [self toNSString: newViewProps.cameraModule]];
    }
    if(![self compareStringProps:oldViewProps.cameraPreviewMode withString:newViewProps.cameraPreviewMode]){
        [self.scannerViewController cameraPreviewMode: [self toNSString: newViewProps.cameraPreviewMode]];
    }
    if(![self compareStringProps:oldViewProps.photoQualityPrioritization withString:newViewProps.photoQualityPrioritization]){
        [self.scannerViewController photoQualityPrioritization: [self toNSString:newViewProps.photoQualityPrioritization]];
    }
    // MARK: Finder Configuration
    if(oldViewProps.finderEnabled != newViewProps.finderEnabled){
        [self.scannerViewController finderEnabled:newViewProps.finderEnabled];
    }
    if(oldViewProps.finderLineColor != newViewProps.finderLineColor){
        [self.scannerViewController finderLineColor: RCTUIColorFromSharedColor(newViewProps.finderLineColor)];
    }
    if(oldViewProps.finderLineWidth != newViewProps.finderLineWidth){
        [self.scannerViewController finderLineWidth: @(newViewProps.finderLineWidth)];
    }
    if(oldViewProps.finderOverlayColor != newViewProps.finderOverlayColor){
        [self.scannerViewController finderOverlayColor: RCTUIColorFromSharedColor(newViewProps.finderOverlayColor)];
    }
    if(oldViewProps.finderCornerRadius != newViewProps.finderCornerRadius){
        [self.scannerViewController finderCornerRadius:@(newViewProps.finderCornerRadius)];
    }
    if(oldViewProps.finderMinimumPadding != newViewProps.finderMinimumPadding){
        [self.scannerViewController finderMinimumPadding:@(newViewProps.finderMinimumPadding)];
    }
    if(oldViewProps.finderAspectRatio != newViewProps.finderAspectRatio){
        [self.scannerViewController finderAspectRatioWithWidth:@(newViewProps.finderAspectRatio.width)
                                                        height:@(newViewProps.finderAspectRatio.height)];
    }
    // MARK: Overlay Configuration
    if(oldViewProps.polygonEnabled != newViewProps.polygonEnabled){
        [self.scannerViewController polygonEnabled: newViewProps.polygonEnabled];
    }
    if(oldViewProps.polygonBackgroundColor != newViewProps.polygonBackgroundColor){
        [self.scannerViewController polygonBackgroundColor: RCTUIColorFromSharedColor(newViewProps.polygonBackgroundColor)];
    }
    if(oldViewProps.polygonBackgroundColorOK != newViewProps.polygonBackgroundColorOK){
        [self.scannerViewController polygonBackgroundColorOK: RCTUIColorFromSharedColor(newViewProps.polygonBackgroundColorOK)];
    }
    if(oldViewProps.polygonColor != newViewProps.polygonColor){
        [self.scannerViewController polygonColor: RCTUIColorFromSharedColor(newViewProps.polygonColor)];
    }
    if(oldViewProps.polygonColorOK != newViewProps.polygonColorOK){
        [self.scannerViewController polygonColorOK: RCTUIColorFromSharedColor(newViewProps.polygonColorOK)];
    }
    if(oldViewProps.polygonLineWidth != newViewProps.polygonLineWidth){
        [self.scannerViewController polygonLineWidth: @(newViewProps.polygonLineWidth)];
    }
    if(oldViewProps.polygonCornerRadius != newViewProps.polygonCornerRadius){
        [self.scannerViewController polygonCornerRadius: @(newViewProps.polygonCornerRadius)];
    }
    if(oldViewProps.polygonAutoSnapProgressColor != newViewProps.polygonAutoSnapProgressColor){
        [self.scannerViewController polygonAutoSnapProgressColor: RCTUIColorFromSharedColor(newViewProps.polygonAutoSnapProgressColor)];
    }
    if(oldViewProps.polygonAutoSnapProgressLineWidth != newViewProps.polygonAutoSnapProgressLineWidth){
        [self.scannerViewController polygonAutoSnapProgressLineWidth:@(newViewProps.polygonAutoSnapProgressLineWidth)];
    }
    if(oldViewProps.polygonAutoSnapProgressEnabled != newViewProps.polygonAutoSnapProgressEnabled){
        [self.scannerViewController polygonAutoSnapProgressEnabled:newViewProps.polygonAutoSnapProgressEnabled];
    }
    
    [super updateProps:props oldProps:oldProps];
}

//MARK: Commands

- (void)handleCommand:(nonnull const NSString *)commandName
                 args:(nonnull const NSArray *)args {
    RCTScanbotDocumentScannerViewHandleCommand(self, commandName, args);
}

- (void)freezeCamera {
    [self.scannerViewController freezeCamera];
}

- (void)unfreezeCamera {
    [self.scannerViewController unfreezeCamera];
}

- (void)snapDocument:(BOOL)acquireFocus {
    [self.scannerViewController snapDocument];
}

//MARK: Delegate Events

- (void)onDocumentDetectionStatus:(NSString * _Nonnull)status { 
    if (_eventEmitter == nullptr) return;
    
    const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotDocumentScannerViewEventEmitter>(_eventEmitter);
    
    eventEmitter->onDetectionResult(facebook::react::ScanbotDocumentScannerViewEventEmitter::OnDetectionResult{.result = RCTStringFromNSString(status)});
}

- (void)onScannedDocument:(SBSDKImageRef * _Nonnull)originalImageRef :(SBSDKImageRef * _Nullable)croppedImageRef :(NSString * _Nullable)detectionResult {
    if (_eventEmitter == nullptr) return;
    
    const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotDocumentScannerViewEventEmitter>(_eventEmitter);
    
    eventEmitter->onDocumentScannerResult(facebook::react::ScanbotDocumentScannerViewEventEmitter::OnDocumentScannerResult{
        .originalImage = RCTStringFromNSString([originalImageRef serialize]),
        .documentImage = RCTStringFromNSString(croppedImageRef ? [croppedImageRef serialize] : @""),
        .detectionResult = RCTStringFromNSString(detectionResult ? detectionResult : @"")
    });
}

- (void)onError:(NSString * _Nonnull)errorMessage errorCode:(NSInteger)errorCode { 
    if (_eventEmitter == nullptr) return;
    
    const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotDocumentScannerViewEventEmitter>(_eventEmitter);
    
    eventEmitter->onError(facebook::react::ScanbotDocumentScannerViewEventEmitter::OnError{
        .message = RCTStringFromNSString(errorMessage),
        .code = (int)errorCode
    });
}

//MARK: Utils

- (NSString*) toNSString:(std::string) value {
    return [[NSString alloc] initWithCString:value.c_str() encoding:NSUTF8StringEncoding];
}

- (BOOL)compareStringProps:(std::string)oldProp withString:(std::string)newProp {
    return [[self toNSString:oldProp] isEqualToString: [self toNSString:newProp]];
}

Class <RCTComponentViewProtocol> ScanbotDocumentScannerViewCls(void){
    return RNScanbotDocumentScannerView.class;
}


@end

#endif
