#ifdef RCT_NEW_ARCH_ENABLED

#import "RNScanbotBarcodeScannerView.h"
#import <ScanbotBarcodeScannerSDK/ScanbotBarcodeScannerSDK-Swift.h>

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

using namespace facebook::react;

@interface RNScanbotBarcodeScannerView() <RCTScanbotBarcodeScannerViewViewProtocol, RNScanbotBarcodeScannerEventDelegate>
@property (strong, nonatomic) RNScanbotBarcodeScannerViewController *scannerViewController;
@property (nonatomic) BOOL attached;
@end

@implementation RNScanbotBarcodeScannerView

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

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        static const auto defaultProps = std::make_shared<const ScanbotBarcodeScannerViewProps>();
        _props = defaultProps;
        self.attached = false;

        self.scannerViewController = [[RNScanbotBarcodeScannerViewController 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 prepareForRecycle];
    self.scannerViewController = nil;

    static const auto defaultProps = std::make_shared<const ScanbotBarcodeScannerViewProps>();
    self.attached = false;
    _props = defaultProps;
    [super prepareForRecycle];
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
    if (self.scannerViewController == nil) {
        self.scannerViewController = [[RNScanbotBarcodeScannerViewController alloc] init];
        [self.scannerViewController setDefaults];
        self.scannerViewController.delegate = self;
    }

    const auto &oldViewProps = *std::static_pointer_cast<ScanbotBarcodeScannerViewProps const>(_props);
    const auto &newViewProps = *std::static_pointer_cast<ScanbotBarcodeScannerViewProps const>(props);

    // MARK: General settings
    if(oldViewProps.flashEnabled != newViewProps.flashEnabled){
        [self.scannerViewController flashEnabled: newViewProps.flashEnabled];
    }
    if(oldViewProps.scanningEnabled != newViewProps.scanningEnabled){
        [self.scannerViewController scanningEnabled: newViewProps.scanningEnabled];
    }
    if(oldViewProps.hardwareButtonsEnabled != newViewProps.hardwareButtonsEnabled){
        [self.scannerViewController hardwareButtonsEnabled: newViewProps.hardwareButtonsEnabled];
    }
    // MARK: Finder Configuration Props
    if(oldViewProps.finderEnabled != newViewProps.finderEnabled){
        [self.scannerViewController finderEnabled:newViewProps.finderEnabled];
    }
    if(oldViewProps.finderStrokeWidth != newViewProps.finderStrokeWidth){
        [self.scannerViewController finderStrokeWidth:@(newViewProps.finderStrokeWidth)];
    }
    if(oldViewProps.finderStrokeColor != newViewProps.finderStrokeColor){
        [self.scannerViewController finderStrokeColor: RCTUIColorFromSharedColor(newViewProps.finderStrokeColor)];
    }
    if(oldViewProps.finderOverlayColor != newViewProps.finderOverlayColor){
        [self.scannerViewController finderOverlayColor: RCTUIColorFromSharedColor(newViewProps.finderOverlayColor)];
    }
    if(oldViewProps.finderInset != newViewProps.finderInset){
        [self.scannerViewController finderInsetsWithLeft: newViewProps.finderInset.left
                                                     top: newViewProps.finderInset.top
                                                   right: newViewProps.finderInset.right
                                                  bottom: newViewProps.finderInset.bottom];
    }
    if(oldViewProps.finderRequiredAspectRatios != newViewProps.finderRequiredAspectRatios){
        [self.scannerViewController finderRequiredAspectRatiosWithWidth:newViewProps.finderRequiredAspectRatios.width
                                                                 height:newViewProps.finderRequiredAspectRatios.height];
    }
    // MARK: Camera Configuration Props
    if(oldViewProps.cameraZoomFactor != newViewProps.cameraZoomFactor){
        [self.scannerViewController cameraZoomFactor:@(newViewProps.cameraZoomFactor)];
    }
    if(oldViewProps.cameraZoomRange != newViewProps.cameraZoomRange){
        [self.scannerViewController cameraZoomRangeWithMinZoom:newViewProps.cameraZoomRange.minZoom
                                                       maxZoom:newViewProps.cameraZoomRange.maxZoom];
    }
    if(![self compareStringProps:oldViewProps.cameraModule withString:newViewProps.cameraModule]){
        [self.scannerViewController cameraDevice:[self toNSString:newViewProps.cameraModule]];
    }
    if(oldViewProps.minFocusDistanceLock != newViewProps.minFocusDistanceLock){
        [self.scannerViewController minFocusDistanceLock:newViewProps.minFocusDistanceLock];
    }
    // MARK: Tracking Overlay Config
    if(oldViewProps.overlayEnabled != newViewProps.overlayEnabled){
        [self.scannerViewController overlayEnabled:newViewProps.overlayEnabled];
    }
    if(oldViewProps.overlayPolygonColor != newViewProps.overlayPolygonColor){
        [self.scannerViewController overlayPolygonColor:RCTUIColorFromSharedColor(newViewProps.overlayPolygonColor)];
    }
    if(oldViewProps.overlayStrokeColor != newViewProps.overlayStrokeColor){
        [self.scannerViewController overlayStrokeColor:RCTUIColorFromSharedColor(newViewProps.overlayStrokeColor)];
    }
    if(oldViewProps.overlayTextColor != newViewProps.overlayTextColor){
        [self.scannerViewController overlayTextColor:RCTUIColorFromSharedColor(newViewProps.overlayTextColor)];
    }
    if(oldViewProps.overlayTextContainerColor != newViewProps.overlayTextContainerColor){
        [self.scannerViewController overlayTextContainerColor:RCTUIColorFromSharedColor(newViewProps.overlayTextContainerColor)];
    }
    if(![self compareStringProps:oldViewProps.overlayTextFormat withString:newViewProps.overlayTextFormat]){
        [self.scannerViewController overlayTextFormat:[self toNSString:newViewProps.overlayTextFormat]];
    }
    if(![self compareStringProps:oldViewProps.overlayLoadingTextValue withString:newViewProps.overlayLoadingTextValue]){
        [self.scannerViewController overlayLoadingTextValue:[self toNSString:newViewProps.overlayLoadingTextValue]];
    }
    if(oldViewProps.overlayBarcodeItemOverlayViewBinder != newViewProps.overlayBarcodeItemOverlayViewBinder){
        [self.scannerViewController overlayViewBinder:newViewProps.overlayBarcodeItemOverlayViewBinder];
    }

    // MARK: BarcodeScanningConfiguration
    if(oldViewProps.configFormatConfigurations != newViewProps.configFormatConfigurations){
        NSMutableArray<NSDictionary *> *formatConfigurations = [[NSMutableArray alloc] initWithCapacity:newViewProps.configExtractedDocumentFormats.size()];
        for(const ScanbotBarcodeScannerViewConfigFormatConfigurationsStruct &item : newViewProps.configFormatConfigurations){
            [formatConfigurations addObject:[self dictionaryFromFormatConfigurationStruct: item]];
        }

        [self.scannerViewController configFormatConfigurations:formatConfigurations];
    }
    if(oldViewProps.configExtractedDocumentFormats != newViewProps.configExtractedDocumentFormats){
        NSMutableArray<NSString *> *formats = [[NSMutableArray alloc] initWithCapacity:newViewProps.configExtractedDocumentFormats.size()];
        for (const std::string &str : newViewProps.configExtractedDocumentFormats) {
            [formats addObject:[self toNSString:str]];
        }

        [self.scannerViewController configExtractedDocumentFormats:formats];
    }
    if(oldViewProps.configOnlyAcceptDocuments != newViewProps.configOnlyAcceptDocuments){
        [self.scannerViewController configOnlyAcceptDocuments:newViewProps.configOnlyAcceptDocuments];
    }
    if(oldViewProps.configEngineMode != newViewProps.configEngineMode){
        [self.scannerViewController configEngineMode:[self toNSString:newViewProps.configEngineMode]];
    }
    if(oldViewProps.configReturnBarcodeImage != newViewProps.configReturnBarcodeImage){
        [self.scannerViewController configReturnBarcodeImage:newViewProps.configReturnBarcodeImage];
    }
    if(oldViewProps.configOptimizedForOverlays != newViewProps.configOptimizedForOverlays){
        [self.scannerViewController configOptimizedForOverlays:newViewProps.configOptimizedForOverlays];
    }
    if(oldViewProps.configAccumulation != newViewProps.configAccumulation){
        [self.scannerViewController configAccumulationConfigurationWithAccumulationTime:newViewProps.configAccumulation.accumulationTime
                                                               removeUnconnectedResults:newViewProps.configAccumulation.removeUnconnectedResults
                                                                                 method:[self toNSString:newViewProps.configAccumulation.method]];
    }

    [super updateProps:props oldProps:oldProps];
}

//MARK: Commands

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

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

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

-(void)bindBarcodeItemOverlayView:(NSString *)barcodeItemUuid bindingConfig:(NSString *)bindingConfig{
    [self.scannerViewController bindBarcodeItemOverlayViewWithBarcodeID:barcodeItemUuid bindingConfigJson:bindingConfig];
}

//MARK: Delegate Events

- (void)onScannedBarcode:(NSString *)barcode {
    if (_eventEmitter == nullptr) return;

    const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotBarcodeScannerViewEventEmitter>(_eventEmitter);
    eventEmitter->onBarcodeScannerResult(facebook::react::ScanbotBarcodeScannerViewEventEmitter::OnBarcodeScannerResult{.result = std::string([barcode UTF8String])});
}

- (void)onSelectedBarcode:(NSString *)barcode {
    if (_eventEmitter == nullptr) return;

    const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotBarcodeScannerViewEventEmitter>(_eventEmitter);
    eventEmitter->onSelectBarcodeResult(facebook::react::ScanbotBarcodeScannerViewEventEmitter::OnSelectBarcodeResult{.result = std::string([barcode UTF8String])});
}

- (void)onError:(NSString * _Nonnull)errorMessage errorCode:(NSInteger)errorCode {
    if (_eventEmitter == nullptr) return;

    const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotBarcodeScannerViewEventEmitter>(_eventEmitter);
    eventEmitter->onError(facebook::react::ScanbotBarcodeScannerViewEventEmitter::OnError{
        .message = std::string([errorMessage UTF8String]),
        .code = (int)errorCode
    });
}


//MARK: Utils

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

- (nonnull NSArray*)vectorStringToNSArray:(const std::vector<std::string>) vector {
    NSMutableArray* nsArray = [NSMutableArray array];

    for (auto const& value : vector) {
        [nsArray addObject:[self toNSString:value]];
    }

    return nsArray;
}

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

- (nonnull NSDictionary *)dictionaryFromFormatConfigurationStruct:(const ScanbotBarcodeScannerViewConfigFormatConfigurationsStruct &)item {
    return @{
        @"_type": [self toNSString: item._type],
        @"regexFilter": [self toNSString:item.regexFilter],
        @"minimumSizeScore": [NSNumber numberWithDouble:item.minimumSizeScore],
        @"addAdditionalQuietZone": @(item.addAdditionalQuietZone),
        @"minimum1DQuietZoneSize": [NSNumber numberWithDouble:item.minimum1DQuietZoneSize],
        @"minimumTextLength": [NSNumber numberWithDouble:item.minimumTextLength],
        @"maximumTextLength": [NSNumber numberWithDouble:item.maximumTextLength],
        @"enableOneDBlurScanner": @(item.enableOneDBlurScanner),
        @"returnStartEnd": @(item.returnStartEnd),
        @"stripCheckDigits": @(item.stripCheckDigits),
        @"checksum": @(item.checksum),
        @"code32": @(item.code32),
        @"code39": @(item.code39),
        @"pzn7": @(item.pzn7),
        @"pzn8": @(item.pzn8),
        @"tryCode39ExtendedMode": @(item.tryCode39ExtendedMode),
        @"useCode39CheckDigit": @(item.useCode39CheckDigit),
        @"gs1Handling": [self toNSString:item.gs1Handling],
        @"iata2of5": @(item.iata2of5),
        @"code25": @(item.code25),
        @"industrial2of5": @(item.industrial2of5),
        @"useIATA2OF5Checksum": @(item.useIATA2OF5Checksum),
        @"checksumAlgorithms": [self vectorStringToNSArray:item.checksumAlgorithms],
        @"ean8": @(item.ean8),
        @"ean13": @(item.ean13),
        @"upca": @(item.upca),
        @"upce": @(item.upce),
        @"extensions": [self toNSString: item.extensions],
        @"minimumValue": [NSNumber numberWithDouble:item.minimumValue],
        @"allowNarrowBarsOnly": @(item.allowNarrowBarsOnly),
        @"allowWideBarsOnly": @(item.allowWideBarsOnly),
        @"strictMode": @(item.strictMode),
        @"qr": @(item.qr),
        @"microQr": @(item.microQr),
        @"rmqr": @(item.rmqr),
        @"australiaPostCustomerFormat": [self toNSString:item.australiaPostCustomerFormat],
        @"formats": [self vectorStringToNSArray:item.formats],
        @"minimumNumberOfRequiredFramesWithEqualRecognitionResult": [NSNumber numberWithInt:item.minimumNumberOfRequiredFramesWithEqualRecognitionResult],
        @"minimumNumberOfRequiredFramesWithEqualRecognitionResultExtensionless": [NSNumber numberWithInt:item.minimumNumberOfRequiredFramesWithEqualRecognitionResultExtensionless],
        @"oneDConfirmationMode":  [self toNSString: item.oneDConfirmationMode]
    };
}

@end

Class<RCTComponentViewProtocol> ScanbotBarcodeScannerViewCls(void)
{
    return RNScanbotBarcodeScannerView.class;
}

#endif
