//
//  RNScanbotCameraView.m
//  RNScanbotSDK
//
//  Created by Marco Saia on 18.05.21.
//

#import "RNScanbotBarcodeCameraView.h"
#import "ObjectMapper.h"
#import "BarcodeMapping.h"

@import ScanbotSDK;
@import Foundation;

@implementation RNScanbotBarcodeCameraView {
    SBSDKBarcodeScannerViewController* viewController;
    RNScanbotCameraViewConfiguration* _cameraViewConfiguration;
    CGFloat _userDefinedCameraZoomFactor;
    BOOL _isAttached;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    self.barcodes = [[NSMutableArray alloc] init];
    self.shouldUseFinderView = false;
    _cameraViewConfiguration = [[RNScanbotCameraViewConfiguration alloc] init];
    _userDefinedCameraZoomFactor = -1;
    _isAttached = false;
    [self createCameraViewController];
    return self;
}

- (void)layoutSubviews {
    if (viewController) {
        if (!_isAttached) {
            [viewController attach:[self getParentViewController] parentView:self];
            [self updateWithConfiguration];
            _isAttached = true;
        }
        viewController.view.frame = [self bounds];
        return;
    }
    
    [self createCameraViewController];
}

- (void)createCameraViewController {
    UIViewController* parentViewController = [self getParentViewController];
    if (!parentViewController) {
        return;
    }
    
    if (!viewController) {
        viewController = [[SBSDKBarcodeScannerViewController alloc] init];
        viewController.delegate = self;
    }
    
}

- (void)setConfiguration:(NSDictionary *)configuration {
    _configuration = configuration;
    if (configuration == NULL) {
        return;
    }
    RNScanbotCameraViewConfiguration* cameraViewConfiguration = [[RNScanbotCameraViewConfiguration alloc] init];
    
    NSMutableDictionary* config = [configuration mutableCopy];
    if ([config objectForKey:@"barcodeFormats"]) {
        cameraViewConfiguration.barcodeFormats = extractBarcodeTypesFromConfiguration(config);
        [config removeObjectForKey:@"barcodeFormats"];
    }
    
    @try {
        [ObjectMapper populateInstance:cameraViewConfiguration fromDictionary:config];
    } @catch (NSException *exception) {
        NSLog(@"%@", [exception reason]);
        return;
    }
    
    cameraViewConfiguration.msiPlesseyChecksumAlgorithm = extractPlesseyChecksumAlgorithmFromConfiguration(configuration);
    
    _cameraViewConfiguration = cameraViewConfiguration;
    
    if(viewController) {
        [self updateWithConfiguration];
    }
}

- (void)updateWithConfiguration {

    // View Finder Configuration
    SBSDKBaseScannerViewFinderConfiguration* viewFinderConfig = [[SBSDKBaseScannerViewFinderConfiguration alloc] init];
    viewFinderConfig.lineWidth = _cameraViewConfiguration.finderLineWidth;
    viewFinderConfig.lineColor = _cameraViewConfiguration.finderLineColor;
    viewFinderConfig.backgroundColor = _cameraViewConfiguration.finderBackgroundColor;
    viewFinderConfig.backgroundColor = [viewFinderConfig.backgroundColor colorWithAlphaComponent:_cameraViewConfiguration.finderBackgroundOpacity];
    viewFinderConfig.aspectRatio = _cameraViewConfiguration.finderAspectRatio;

    CGFloat offset = _cameraViewConfiguration.finderVerticalOffset;
    CGFloat padding = _cameraViewConfiguration.finderMinimumPadding;
    viewFinderConfig.minimumInset = UIEdgeInsetsMake(padding + offset > 0 ? offset : 0,
                                                     padding,
                                                     padding + offset < 0 ? -1*offset : 0,
                                                     padding);

    if (_cameraViewConfiguration.shouldUseFinderView) {
        viewFinderConfig.viewFinderEnabled = [_cameraViewConfiguration.shouldUseFinderView boolValue];
    }
    
    [viewController setViewFinderConfiguration:viewFinderConfig];

    // Behavior Config
    if (_cameraViewConfiguration.barcodeFormats) {
        viewController.acceptedBarcodeTypes = _cameraViewConfiguration.barcodeFormats;
    }
    if (_cameraViewConfiguration.acceptedDocumentFormats) {
        viewController.acceptedDocumentTypes = _cameraViewConfiguration.acceptedDocumentFormats;
    }

    // Additional Parameters
    SBSDKBarcodeAdditionalParameters* additionalParameters = [[SBSDKBarcodeAdditionalParameters alloc] init];

    if (_cameraViewConfiguration.gs1DecodingEnabled) {
        additionalParameters.enableGS1Decoding = [_cameraViewConfiguration.gs1DecodingEnabled boolValue];
    }
    if (_cameraViewConfiguration.maximumTextLength) {
        additionalParameters.maximumTextLength = [_cameraViewConfiguration.maximumTextLength intValue];
    }
    if (_cameraViewConfiguration.minimumTextLength) {
        additionalParameters.minimumTextLength = [_cameraViewConfiguration.minimumTextLength intValue];
    }
    if (_cameraViewConfiguration.msiPlesseyChecksumAlgorithm != -1) {
        additionalParameters.msiPlesseyChecksumAlgorithm = _cameraViewConfiguration.msiPlesseyChecksumAlgorithm;
    }

    viewController.additionalDetectionParameters = additionalParameters;

    if (_cameraViewConfiguration.cameraZoomFactor) {
        CGFloat value = [_cameraViewConfiguration.cameraZoomFactor floatValue];
        if (value != _userDefinedCameraZoomFactor || _userDefinedCameraZoomFactor == -1) {
            viewController.cameraZoomFactor = value;
            _userDefinedCameraZoomFactor = value;
        }
    }
    if (_cameraViewConfiguration.engineMode != -1) {
        viewController.engineMode = _cameraViewConfiguration.engineMode;
    }
    if (_cameraViewConfiguration.flashEnabled) {
        if (!_isAttached && [_cameraViewConfiguration.flashEnabled boolValue]) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self->viewController.flashLightEnabled = true;
            });
        } else {
            viewController.flashLightEnabled = [_cameraViewConfiguration.flashEnabled boolValue];
        }
    }
}

- (UIViewController* _Nullable)getParentViewController {
    UIResponder* parentResponder = self;
    while (parentResponder) {
        parentResponder = parentResponder.nextResponder;
        if (parentResponder && [parentResponder isKindOfClass:[UIViewController class]]) {
            return (UIViewController*) parentResponder;
        }
    }
    return NULL;
}

- (void)sendScanResults:(NSArray<SBSDKBarcodeScannerResult*>*) results {
    if(!self.onBarcodeScannerResult) {
        return;
    }

    if(!results || results.count == 0) {
        return;
    }
    
    NSMutableArray* jsonBarcodes = [[NSMutableArray alloc] init];
    
    for (SBSDKBarcodeScannerResult* result in results) {
        [jsonBarcodes addObject:@{
            @"text": result.rawTextString,
            @"type": result.type.name
        }];
    }
    
    NSDictionary* outResult = @{
        @"result": @{
            @"status": @"OK",
            @"barcodes": jsonBarcodes
        }
    };
    
    self.onBarcodeScannerResult(outResult);
}

// MARK: - SBSDKBarcodeScannerViewControllerDelegate

- (void)barcodeScannerController:(SBSDKBarcodeScannerViewController *)controller
               didDetectBarcodes:(NSArray<SBSDKBarcodeScannerResult *> *)codes {
    [self sendScanResults:codes];
}

- (BOOL)barcodeScannerControllerShouldDetectBarcodes:(SBSDKBarcodeScannerViewController *)controller {
    return true;
}

- (NSArray*)hashedBarcodes {
    NSMutableArray* outArray = [[NSMutableArray alloc] init];
    for (NSDictionary* barcode in self.barcodes) {
        NSString* hash = [RNScanbotBarcodeCameraView hashFromBarcode:barcode];
        [outArray addObject:hash];
    }
    return outArray;
}

+ (NSString*)hashFromBarcode: (NSDictionary* _Nonnull)barcode {
    NSString* text = [barcode objectForKey:@"barcode"];
    NSString* type = [barcode objectForKey:@"type"];
    return [[NSString stringWithFormat:@"%@_%@", text, type] lowercaseString];
}

@end

@implementation RNScanbotCameraViewConfiguration

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.shouldUseFinderView = false;
        self.finderLineWidth = 1.0;
        self.finderLineColor = UIColor.whiteColor;
        self.finderAspectRatio = [[SBSDKAspectRatio alloc] initWithWidth:1 andHeight:1];
        self.finderBackgroundColor = UIColor.blackColor;
        self.finderBackgroundOpacity = 0.6;
        self.finderMinimumPadding = 32.0;
        self.finderVerticalOffset = 0.0;
        self.acceptedDocumentFormats = nil;
        self.gs1DecodingEnabled = nil;
        self.maximumTextLength = nil;
        self.minimumTextLength = nil;
        self.msiPlesseyChecksumAlgorithm = -1;
        self.cameraZoomFactor = nil;
        self.engineMode = -1;
        self.flashEnabled = nil;
    }
    return self;
}

@end
