
#ifdef RCT_NEW_ARCH_ENABLED
#import "RNScanbotBarcodeScannerView.h"

#import <react/renderer/components/RNScanbotBarcodeScannerViewSpec/ComponentDescriptors.h>
#import <react/renderer/components/RNScanbotBarcodeScannerViewSpec/EventEmitters.h>
#import <react/renderer/components/RNScanbotBarcodeScannerViewSpec/Props.h>
#import <react/renderer/components/RNScanbotBarcodeScannerViewSpec/RCTComponentViewHelpers.h>

#import "RCTFabricComponentsPlugins.h"
#import <React/RCTConversions.h>

using namespace facebook::react;

@interface RNScanbotBarcodeScannerView() <RCTScanbotBarcodeScannerViewViewProtocol>
@property (strong, nonatomic) SBSDKBarcodeScannerViewController *scannerViewController;
@property (strong, nonatomic) UIViewController *vc;
@property (nonatomic) BOOL scanningEnabled;
@property (nonatomic) NSString* _Nullable barcodeItemOverlayViewLoadingText;
@property (atomic) NSMutableDictionary* _Nullable barcodeItemOverlayViewBinders;
@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;
        _scanningEnabled = true;
        _barcodeItemOverlayViewLoadingText = nil;
        _barcodeItemOverlayViewBinders = nil;
        
        self.vc = [[UIViewController alloc] init];
        self.vc.view = self;
        self.scannerViewController = [RNScanbotBarcodeScannerViewControllerWrapper initController];
        self.scannerViewController.delegate = self;
        self.scannerViewController.trackingOverlayController.delegate = self;
    }
    
    return self;
}

- (void)didMoveToWindow {
    [super didMoveToWindow];
    
    if(self.window) {
        if (self.vc == nil){
            self.vc = [[UIViewController alloc] init];
            self.vc.view = self;
        }
        [self.scannerViewController attachWithParentViewController:self.vc parentView:self];
    }
}

- (void)prepareForRecycle {
    self.scannerViewController = nil;
    self.vc = nil;
    self.barcodeItemOverlayViewLoadingText = nil;
    self.barcodeItemOverlayViewBinders = nil;
    
    static const auto defaultProps = std::make_shared<const ScanbotBarcodeScannerViewProps>();
    _props = defaultProps;
    [super prepareForRecycle];
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
    if (self.scannerViewController == nil) {
        self.scannerViewController = [RNScanbotBarcodeScannerViewControllerWrapper initController];
        self.scannerViewController.delegate = self;
        self.scannerViewController.trackingOverlayController.delegate = self;
    }

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

    SBSDKBaseScannerViewFinderConfiguration *finderConfig = self.scannerViewController.viewFinderConfiguration;
    SBSDKBaseScannerZoomConfiguration *zoomConfig = self.scannerViewController.zoomConfiguration;
    SBSDKBarcodeAdditionalParameters *additionalConfig = self.scannerViewController.additionalDetectionParameters;
    SBBWrapperBarcodeCameraViewConfiguration* decodedConfig = [SBBWrapperBarcodeCameraViewConfiguration fromDicitonary: @{
        @"barcodeFormats": [self vectorStringToNSArray:newViewProps.detectorConfig.barcodeFormats],
        @"acceptedDocumentFormats": [self vectorStringToNSArray:newViewProps.detectorConfig.acceptedDocumentFormats],
        @"engineMode": [self toNSString:newViewProps.detectorConfig.engineMode],
        @"barcodesExtensionFilter": [self toNSString:newViewProps.detectorConfig.barcodesExtensionFilter],
        @"gs1HandlingMode": [self toNSString:newViewProps.detectorConfig.gs1HandlingMode],
        @"msiPlesseyChecksumAlgorithm": [self toNSString:newViewProps.detectorConfig.msiPlesseyChecksumAlgorithm],
        @"textFormat": [self toNSString:newViewProps.overlayTextFormat]
    }];

    if(oldViewProps.flashEnabled != newViewProps.flashEnabled){
        [RNScanbotBarcodeScannerViewControllerWrapper setFlashEnabled:self.scannerViewController value:newViewProps.flashEnabled];
    }
    if(oldViewProps.scanningEnabled != newViewProps.scanningEnabled){
        self.scanningEnabled = newViewProps.scanningEnabled;
    }
    // MARK: FinderConfig Props
    if(oldViewProps.finderEnabled != newViewProps.finderEnabled){
        [RNScanbotBarcodeScannerViewControllerWrapper setFinderEnabled:finderConfig value:newViewProps.finderEnabled];
    }
    if(oldViewProps.finderStrokeWidth != newViewProps.finderStrokeWidth){
        [RNScanbotBarcodeScannerViewControllerWrapper setFinderStrokeWidth:finderConfig value:newViewProps.finderStrokeWidth];
    }
    if(oldViewProps.finderStrokeColor != newViewProps.finderStrokeColor){
        [RNScanbotBarcodeScannerViewControllerWrapper setFinderStrokeColor:finderConfig value:RCTUIColorFromSharedColor(newViewProps.finderStrokeColor)];
    }
    if(oldViewProps.finderOverlayColor != newViewProps.finderOverlayColor){
        [RNScanbotBarcodeScannerViewControllerWrapper setFinderOverlayColor:finderConfig value:RCTUIColorFromSharedColor(newViewProps.finderOverlayColor)];
    }
    if(oldViewProps.finderInset != newViewProps.finderInset){
        [RNScanbotBarcodeScannerViewControllerWrapper setFinderInset:finderConfig
                                                                 top:newViewProps.finderInset.top
                                                                left:newViewProps.finderInset.left
                                                              bottom:newViewProps.finderInset.bottom
                                                               right:newViewProps.finderInset.right];
    }
    if(oldViewProps.finderRequiredAspectRatios != newViewProps.finderRequiredAspectRatios){
        [RNScanbotBarcodeScannerViewControllerWrapper setFinderRequiredAspectRatios:finderConfig
                                                                              width:newViewProps.finderRequiredAspectRatios.width
                                                                             height:newViewProps.finderRequiredAspectRatios.height];
    }
    // MARK: -
    // MARK: CameraConfig Props
    if(oldViewProps.cameraZoomFactor != newViewProps.cameraZoomFactor){
        [RNScanbotBarcodeScannerViewControllerWrapper setCameraZoomFactor:zoomConfig value:newViewProps.cameraZoomFactor];
    }
    if(oldViewProps.cameraZoomRange != newViewProps.cameraZoomRange){
        [RNScanbotBarcodeScannerViewControllerWrapper setCameraZoomRange:zoomConfig
                                                                 minZoom:newViewProps.cameraZoomRange.minZoom
                                                                 maxZoom:newViewProps.cameraZoomRange.maxZoom];
    }
    if(![self compareStringProps:oldViewProps.cameraModule withString:newViewProps.cameraModule]){
        [RNScanbotBarcodeScannerViewControllerWrapper setCameraDevice:self.scannerViewController value:[self toNSString:newViewProps.cameraModule]];
    }
    if(oldViewProps.minFocusDistanceLock != newViewProps.minFocusDistanceLock){
        [RNScanbotBarcodeScannerViewControllerWrapper setMinFocusDistanceLock:self.scannerViewController value:newViewProps.minFocusDistanceLock];
    }
    // MARK: -
    // MARK: Detector Config
    if(oldViewProps.detectorConfig.barcodeFormats != newViewProps.detectorConfig.barcodeFormats){
        [RNScanbotBarcodeScannerViewControllerWrapper setBarcodeFormats:self.scannerViewController
                                                   acceptedBarcodeTypes:decodedConfig.acceptedBarcodeTypes];
    }
    if(oldViewProps.detectorConfig.acceptedDocumentFormats != newViewProps.detectorConfig.acceptedDocumentFormats){
        [RNScanbotBarcodeScannerViewControllerWrapper setDocumentFormats:self.scannerViewController
                                                   acceptedDocumentTypes:decodedConfig.acceptedDocumentTypes];
    }
    if(![self compareStringProps:oldViewProps.detectorConfig.engineMode withString:newViewProps.detectorConfig.engineMode]){
        [RNScanbotBarcodeScannerViewControllerWrapper setEngineMode:self.scannerViewController
                                                         engineMode:decodedConfig.engineMode];
    }
    if(![self compareStringProps:oldViewProps.detectorConfig.barcodesExtensionFilter
                      withString:newViewProps.detectorConfig.barcodesExtensionFilter]){
        [RNScanbotBarcodeScannerViewControllerWrapper setBarcodeExtensionFilter:self.scannerViewController
                                                                         filter:decodedConfig.barcodesExtensionFilter];
    }
    if(oldViewProps.detectorConfig.lowPowerMode != newViewProps.detectorConfig.lowPowerMode){
        [RNScanbotBarcodeScannerViewControllerWrapper setLowPowerMode:self.scannerViewController value: newViewProps.detectorConfig.lowPowerMode];
    }
    if(oldViewProps.detectorConfig.minimumTextLength != newViewProps.detectorConfig.minimumTextLength){
        [RNScanbotBarcodeScannerViewControllerWrapper setMinimumTextLenght:additionalConfig value:newViewProps.detectorConfig.minimumTextLength];
    }
    if(oldViewProps.detectorConfig.maximumTextLength != newViewProps.detectorConfig.maximumTextLength){
        [RNScanbotBarcodeScannerViewControllerWrapper setMaximumTextLenght:additionalConfig value:newViewProps.detectorConfig.maximumTextLength];
    }
    if(oldViewProps.detectorConfig.minimum1DBarcodesQuietZone != newViewProps.detectorConfig.minimum1DBarcodesQuietZone){
        [RNScanbotBarcodeScannerViewControllerWrapper setMinimum1DBarcodesQuietZone:additionalConfig value:newViewProps.detectorConfig.minimum1DBarcodesQuietZone];
    }
    if(oldViewProps.detectorConfig.stripCheckDigits != newViewProps.detectorConfig.stripCheckDigits){
        [RNScanbotBarcodeScannerViewControllerWrapper setStripCheckDigits:additionalConfig value:newViewProps.detectorConfig.stripCheckDigits];
    }
    if(![self compareStringProps:oldViewProps.detectorConfig.gs1HandlingMode withString:newViewProps.detectorConfig.gs1HandlingMode]){
        [RNScanbotBarcodeScannerViewControllerWrapper setGS1HandlingMode:additionalConfig
                                                         gs1HandlingMode:decodedConfig.gs1Handling];
    }
    if(![self compareStringProps:oldViewProps.detectorConfig.msiPlesseyChecksumAlgorithm withString:newViewProps.detectorConfig.msiPlesseyChecksumAlgorithm]){
        [RNScanbotBarcodeScannerViewControllerWrapper setMsiPlesseyChecksumAlgorithm:additionalConfig
                                                         msiPlesseyChecksumAlgorithm:decodedConfig.msiPlesseyChecksumAlgorithm];
    }
    // MARK: -
    // MARK: Tracking Overlay Config
    if(oldViewProps.overlayEnabled != newViewProps.overlayEnabled){
        [RNScanbotBarcodeScannerViewControllerWrapper setOverlayEnabled:self.scannerViewController value:newViewProps.overlayEnabled];
    }
    if(oldViewProps.overlayPolygonColor != newViewProps.overlayPolygonColor){
        [RNScanbotBarcodeScannerViewControllerWrapper setOverlayPolygonColor:self.scannerViewController value:RCTUIColorFromSharedColor(newViewProps.overlayPolygonColor)];
    }
    if(oldViewProps.overlayStrokeColor != newViewProps.overlayStrokeColor){
        [RNScanbotBarcodeScannerViewControllerWrapper setOverlayStrokeColor:self.scannerViewController value:RCTUIColorFromSharedColor(newViewProps.overlayStrokeColor)];
    }
    if(oldViewProps.overlayTextColor != newViewProps.overlayTextColor){
        [RNScanbotBarcodeScannerViewControllerWrapper setOverlayTextColor:self.scannerViewController value:RCTUIColorFromSharedColor(newViewProps.overlayTextColor)];
    }
    if(oldViewProps.overlayTextContainerColor != newViewProps.overlayTextContainerColor){
        [RNScanbotBarcodeScannerViewControllerWrapper setOverlayTextContainerColor:self.scannerViewController value:RCTUIColorFromSharedColor(newViewProps.overlayTextContainerColor)];
    }
    if(![self compareStringProps:oldViewProps.overlayTextFormat withString:newViewProps.overlayTextFormat]){
        [RNScanbotBarcodeScannerViewControllerWrapper setOverlayTextFormat:self.scannerViewController
                                                                     value:decodedConfig.textFormat];
    }
    
    self.barcodeItemOverlayViewLoadingText = [self toNSString:newViewProps.overlayLoadingTextValue];
    
    if(newViewProps.overlayBarcodeItemOverlayViewBinder != oldViewProps.overlayBarcodeItemOverlayViewBinder){
        self.barcodeItemOverlayViewBinders = [[NSMutableDictionary alloc] init];
    }

    self.scannerViewController.viewFinderConfiguration = finderConfig;
    self.scannerViewController.zoomConfiguration = zoomConfig;
    self.scannerViewController.additionalDetectionParameters = additionalConfig;

    [super updateProps:props oldProps:oldProps];
}

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

- (void)bindBarcodeItemOverlayView:(nonnull NSString *)barcodeItemUuid bindingConfig:(nonnull NSString *)bindingConfigJson {
    [RNScanbotBarcodeScannerViewControllerWrapper updateBarcodeItemOverlayViewBinderInDictionary:self.barcodeItemOverlayViewBinders
                                                                                 barcodeItemUuid:barcodeItemUuid
                                                                    barcodeItemOverlayViewConfigJson:bindingConfigJson];
}

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

- (void)barcodeScannerController:(SBSDKBarcodeScannerViewController *)controller
               didDetectBarcodes:(NSArray<SBSDKBarcodeScannerResult *> *)codes {
    if (_eventEmitter == nullptr) return;

    NSString *result = [RNScanbotBarcodeScannerViewControllerWrapper jsonFromResultOrNil:codes];
    if (result != nil){
        const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotBarcodeScannerViewEventEmitter>(_eventEmitter);
        eventEmitter->onBarcodeScannerResult(facebook::react::ScanbotBarcodeScannerViewEventEmitter::OnBarcodeScannerResult{.result = std::string([result UTF8String])});
        
        [RNScanbotBarcodeScannerViewControllerWrapper addBarcodeItemOverlayViewInitialBinderInDictionary:self.barcodeItemOverlayViewBinders
                                                                                             loadingText:self.barcodeItemOverlayViewLoadingText
                                                                                                barcodes:codes];
    }
}

- (void)barcodeTrackingOverlay:(SBSDKBarcodeTrackingOverlayController *)controller didTapOnBarcode:(SBSDKBarcodeScannerResult *)barcode {

    if (_eventEmitter == nullptr) return;

    NSString *result = [RNScanbotBarcodeScannerViewControllerWrapper jsonFromResultOrNil:@[barcode]];
    if(result != nil){
        const auto &eventEmitter = std::dynamic_pointer_cast<const facebook::react::ScanbotBarcodeScannerViewEventEmitter>(_eventEmitter);
        eventEmitter->onSelectBarcodeResult(facebook::react::ScanbotBarcodeScannerViewEventEmitter::OnSelectBarcodeResult{.result = std::string([result UTF8String])});
    }

}

- (SBSDKBarcodeTrackedViewPolygonStyle *)barcodeTrackingOverlay:(SBSDKBarcodeTrackingOverlayController *)controller polygonStyleFor:(SBSDKBarcodeScannerResult *)barcode {

    NSString * _Nonnull barcodeItemUuid = [BarcodeItem uuidFor:barcode];
    NSDictionary * _Nullable barcodeItemOverlayViewBinding = nil;
  
    if (self.barcodeItemOverlayViewBinders != nil) {
      barcodeItemOverlayViewBinding = [self.barcodeItemOverlayViewBinders objectForKey:barcodeItemUuid];
    }
    
    return [RNScanbotBarcodeScannerViewControllerWrapper scannedBarcodePolygonStyle:barcode
                                                 barcodeItemOverlayViewBinding:barcodeItemOverlayViewBinding
                                                                        controller:self.scannerViewController];

}

- (SBSDKBarcodeTrackedViewTextStyle *)barcodeTrackingOverlay:(SBSDKBarcodeTrackingOverlayController *)controller textStyleFor:(SBSDKBarcodeScannerResult *)barcode {
    
    NSString * _Nonnull barcodeItemUuid = [BarcodeItem uuidFor:barcode];
    NSDictionary * _Nullable barcodeItemOverlayViewBinding = nil;
  
    if (self.barcodeItemOverlayViewBinders != nil) {
      barcodeItemOverlayViewBinding = [self.barcodeItemOverlayViewBinders objectForKey:barcodeItemUuid];
    }
    
    return [RNScanbotBarcodeScannerViewControllerWrapper scannedBarcodeOverlayViewTextStyle:barcode
                                               barcodeItemOverlayViewBinding:barcodeItemOverlayViewBinding
                                                                          controller:self.scannerViewController];
}

- (NSString * _Nonnull)barcodeTrackingOverlay:(SBSDKBarcodeTrackingOverlayController * _Nonnull)controller overrideTextFor:(SBSDKBarcodeScannerResult * _Nonnull)barcode proposedString:(NSString * _Nonnull)proposedString {
    
    NSString * _Nonnull barcodeItemUuid = [BarcodeItem uuidFor:barcode];
    NSDictionary * _Nullable barcodeItemOverlayViewBinding = nil;
  
    if (self.barcodeItemOverlayViewBinders != nil) {
      barcodeItemOverlayViewBinding = [self.barcodeItemOverlayViewBinders objectForKey:barcodeItemUuid];
    }
    
    return [RNScanbotBarcodeScannerViewControllerWrapper scannedBarcodeOverlayViewText:barcodeItemUuid
                                                                        proposedString:proposedString
                                                     barcodeItemOverlayViewBinding:barcodeItemOverlayViewBinding];
}

- (BOOL)barcodeScannerControllerShouldDetectBarcodes:(SBSDKBarcodeScannerViewController *)controller {
    return self.scanningEnabled;
}

bool operator!=(ScanbotBarcodeScannerViewFinderInsetStruct const &i,ScanbotBarcodeScannerViewFinderInsetStruct const &j) {
    return !(i.left == j.left && i.top == j.top && i.bottom == j.bottom && i.right == j.right);
}

bool operator!=(ScanbotBarcodeScannerViewFinderRequiredAspectRatiosStruct const &i,ScanbotBarcodeScannerViewFinderRequiredAspectRatiosStruct const &j) {
    return !(i.width == j.width && i.height == j.height);
}

bool operator!=(ScanbotBarcodeScannerViewCameraZoomRangeStruct const &i, ScanbotBarcodeScannerViewCameraZoomRangeStruct const &j) {
    return !(i.maxZoom == j.maxZoom && i.minZoom == j.minZoom);
}

bool operator!=(std::vector<std::string> const &v1, std::vector<std::string> const &v2) {

    bool differentItems = false;
    if(v1.size() != v2.size()) return true;

    for(int i = 0; i< v1.size(); i++){
        if(!(v1[i] != v2[i])){
            differentItems = true;
            break;
        }
    }

    return differentItems;
}

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

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

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

    return nsArray;
}

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

@end

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