#import "CmbsdkFlutterPlugin.h"
#if __has_include(<cmbsdk_flutter/cmbsdk_flutter-Swift.h>)
#import <cmbsdk_flutter/cmbsdk_flutter-Swift.h>
#else
// Support project import fallback if the generated compatibility header
// is not copied when this plugin is created as a library.
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
#import "cmbsdk_flutter-Swift.h"
#endif

#import <cmbSDK/cmbSDK.h>
#import <CMBCrossPlatform/CMBCrossPlatform.h>
#import "CMBEventChannel.h"

@interface CmbsdkFlutterPlugin()<CMBReaderDeviceDelegate, CMBObserversDelegate>

@property CCPBridge *cmb;

@property CMBEventChannel *availabilityChannel;
@property CMBEventChannel *connectionChannel;
@property CMBEventChannel *resultsChannel;
@property CMBEventChannel *scanningStateChannel;

@end

@implementation CmbsdkFlutterPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* channel = [FlutterMethodChannel
                                     methodChannelWithName:@"cmbsdk_flutter"
                                     binaryMessenger:[registrar messenger]];
    CmbsdkFlutterPlugin* instance = [[CmbsdkFlutterPlugin alloc] init];
    
    instance.cmb = [[CCPBridge alloc] init];
    instance.cmb.ccpPluginDelegate = instance;
    instance.cmb.ccpPluginScanningStateDelegate = instance;
    
    [registrar addMethodCallDelegate:instance channel:channel];
    
    // init channels
    FlutterEventChannel *availabilityChannel = [FlutterEventChannel eventChannelWithName:@"availabilityChannel" binaryMessenger:[registrar messenger]];
    instance.availabilityChannel = [CMBEventChannel new];
    [availabilityChannel setStreamHandler:instance.availabilityChannel];
    
    FlutterEventChannel *connectionChannel = [FlutterEventChannel eventChannelWithName:@"connectionStateChannel" binaryMessenger:[registrar messenger]];
    instance.connectionChannel = [CMBEventChannel new];
    [connectionChannel setStreamHandler:instance.connectionChannel];
    
    FlutterEventChannel *resultsChannel = [FlutterEventChannel eventChannelWithName:@"resultsChannel" binaryMessenger:[registrar messenger]];
    instance.resultsChannel = [CMBEventChannel new];
    [resultsChannel setStreamHandler:instance.resultsChannel];
    
    FlutterEventChannel *scanningStateChannel = [FlutterEventChannel eventChannelWithName:@"scanningStateChannel" binaryMessenger:[registrar messenger]];
    instance.scanningStateChannel = [CMBEventChannel new];
    [scanningStateChannel setStreamHandler:instance.scanningStateChannel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    if ([@"loadScanner" isEqualToString:call.method]) {
        [self loadScanner:call withCallback:result];
    }else if ([@"connect" isEqualToString:call.method]) {
        [self connect:call withCallback:result];
    }else if ([@"disconnect" isEqualToString:call.method]) {
        [self disconnect:call withCallback:result];
    }else if ([@"startScanning" isEqualToString:call.method]) {
        [self startScanning:call withCallback:result];
    }else if ([@"stopScanning" isEqualToString:call.method]) {
        [self stopScanning:call withCallback:result];
    }else if ([@"registerSDK" isEqualToString:call.method]) {
        [self registerSDK:call withCallback:result];
    }else if ([@"setCameraMode" isEqualToString:call.method]) {
        [self setCameraMode:call withCallback:result];
    }else if ([@"setPreviewOptions" isEqualToString:call.method]) {
        [self setPreviewOptions:call withCallback:result];
    }else if ([@"setPreviewOverlayMode" isEqualToString:call.method]) {
        [self setPreviewOverlayMode:call withCallback:result];
    }else if ([@"setPreviewContainerPositionAndSize" isEqualToString:call.method]) {
        [self setPreviewContainerPositionAndSize:call withCallback:result];
    }else if ([@"setPreviewContainerBelowStatusBar" isEqualToString:call.method]) {
        [self setPreviewContainerBelowStatusBar:call withCallback:result];
    }else if ([@"setPreviewContainerFullScreen" isEqualToString:call.method]) {
        [self setPreviewContainerFullScreen:call withCallback:result];
    }else if ([@"setStopScannerOnRotate" isEqualToString:call.method]) {
        [self setStopScannerOnRotate:call withCallback:result];
    }else if ([@"enableImage" isEqualToString:call.method]) {
        [self enableImage:call withCallback:result];
    }else if ([@"enableImageGraphics" isEqualToString:call.method]) {
        [self enableImageGraphics:call withCallback:result];
    }else if ([@"setSymbologyEnabled" isEqualToString:call.method]) {
        [self setSymbologyEnabled:call withCallback:result];
    }else if ([@"isSymbologyEnabled" isEqualToString:call.method]) {
        [self isSymbologyEnabled:call withCallback:result];
    }else if ([@"setLightsOn" isEqualToString:call.method]) {
        [self setLightsOn:call withCallback:result];
    }else if ([@"isLightsOn" isEqualToString:call.method]) {
        [self isLightsOn:call withCallback:result];
    }else if ([@"sendCommand" isEqualToString:call.method]) {
        [self sendCommand:call withCallback:result];
    }else if ([@"resetConfig" isEqualToString:call.method]) {
        [self resetConfig:call withCallback:result];
    }else if ([@"beep" isEqualToString:call.method]) {
        [self beep:call withCallback:result];
    }else if ([@"getDeviceBatteryLevel" isEqualToString:call.method]) {
        [self getDeviceBatteryLevel:call withCallback:result];
    }else if ([@"getAvailability" isEqualToString:call.method]) {
        [self getAvailability:call withCallback:result];
    }else if ([@"getConnectionState" isEqualToString:call.method]) {
        [self getConnectionState:call withCallback:result];
    }else if ([@"getSdkVersion" isEqualToString:call.method]) {
        [self getSdkVersion:call withCallback:result];
    }else if ([@"enableCameraFlag" isEqualToString:call.method]) {
        [self enableCameraFlag:call withCallback:result];
    }else if ([@"disableCameraFlag" isEqualToString:call.method]) {
        [self disableCameraFlag:call withCallback:result];
    }else if ([@"setCameraParam" isEqualToString:call.method]) {
        [self setCameraParam:call withCallback:result];
    }else if ([@"setParser" isEqualToString:call.method]) {
        [self setParser:call withCallback:result];
    }else if ([@"setReadStringEncoding" isEqualToString:call.method]) {
        [self setReadStringEncoding:call withCallback:result];
    }else if ([@"setMDMReportingEnabled" isEqualToString:call.method]) {
        [self setMDMReportingEnabled:call withCallback:result];
    }else if ([@"createMDMAuthCredentials" isEqualToString:call.method]) {
        [self createMDMAuthCredentials:call withCallback:result];
    } else if([@"getCameraExposureCompensationRange" isEqualToString:call.method]) {
        [self getCameraExposureCompensationRange:call withCallback:result];
    } else if ([@"setCameraExposureCompensation" isEqualToString:call.method]) {
        [self setCameraExposureCompensation:call withCallback:result];
    } else if ([@"loadCameraConfig" isEqualToString:call.method]) {
        [self loadCameraConfig:call withCallback:result];
    } else if ([@"showToast" isEqualToString:call.method]) {
        [self showToast:call withCallback:result];
    } else if ([@"hideToast" isEqualToString:call.method]) {
        [self hideToast:call withCallback:result];
    } else if ([@"scanImageFromUri" isEqualToString:call.method]) {
        [self scanImageFromUri:call withCallback:result];
    } else if ([@"scanImageFromBase64" isEqualToString:call.method]) {
        [self scanImageFromBase64:call withCallback:result];
    } else if ([@"setPairedBluetoothDevice" isEqualToString:call.method]) {
        [self setPairedBluetoothDevice:call withCallback:result];
    } else if ([@"getPairedBluetoothDevice" isEqualToString:call.method]) {
        [self getPairedBluetoothDevice:call withCallback:result];
    } else {
        result([NSString stringWithFormat:@"'%@' is not available in iOS yet", call.method]);
    }
}

- (CCPCompletionWithResponse)createResponseForResult:(FlutterResult)flutterResult {
    return ^(NSString* errorCode, NSString * _Nonnull response) {
        if ([errorCode intValue] != 0) {
            flutterResult([FlutterError errorWithCode:errorCode message:response details:nil]);
        } else {
            flutterResult(response);
        }
    };
}

- (void)loadScanner:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb initReaderWithClass:[call.arguments intValue] callback:^{
        result(nil);
    }];
}

-(void)connect:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb connect:[self createResponseForResult:result]];
}

-(void)disconnect:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb disconnect:[self createResponseForResult:result]];
}

-(void)startScanning:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb startScanning:[self createResponseForResult:result]];
}

-(void)stopScanning:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb stopScanning:[self createResponseForResult:result]];
}

-(void)setSymbologyEnabled:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setSymbology:[call.arguments[@"symbology"] intValue] enabled:[call.arguments[@"enable"] boolValue] completion:[self createResponseForResult:result]];
}

-(void)isSymbologyEnabled:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb isSymbologyEnabled:[call.arguments[@"symbology"] intValue] completion:[self createResponseForResult:result]];
}

-(void)setCameraMode:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setCameraMode:[call.arguments intValue]];
}

-(void)setPreviewOptions:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setPreviewOptions:[call.arguments intValue]];
}

-(void)setPreviewOverlayMode:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setPreviewOverlayMode:[call.arguments intValue]];
}

-(void)setPreviewContainerPositionAndSize:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setPreviewContainerPositionAndSizeX:[call.arguments[@"x"] floatValue] Y:[call.arguments[@"y"] floatValue] width:[call.arguments[@"width"] floatValue] height:[call.arguments[@"height"] floatValue]];
}

-(void)setPreviewContainerBelowStatusBar:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setPreviewContainerBelowStatusBar:[call.arguments boolValue]];
}

-(void)setPreviewContainerFullScreen:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setPreviewContainerFullScreenWithCompletion:[self createResponseForResult:result]];
}

-(void)setLightsOn:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setLightsOn:[call.arguments boolValue] completion:[self createResponseForResult:result]];
}

-(void)isLightsOn:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb isLightsOnWithCompletion:[self createResponseForResult:result]];
}

-(void)enableImage:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb enableImage:[call.arguments boolValue] completion:[self createResponseForResult:result]];
}

-(void)enableImageGraphics:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb enableImageGraphics:[call.arguments boolValue] completion:[self createResponseForResult:result]];
}

-(void)sendCommand:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb sendCommand:call.arguments completion:[self createResponseForResult:result]];
}

-(void)resetConfig:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb resetConfigWithCompletion:[self createResponseForResult:result]];
}

-(void)registerSDK:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb registerSDK:call.arguments];
}

-(void)getAvailability:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb getAvailability:[self createResponseForResult:result]];
}

-(void)getConnectionState:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb getConnectionState:[self createResponseForResult:result]];
}

-(void)beep:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb beep:[self createResponseForResult:result]];
}

-(void)getDeviceBatteryLevel:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb getDeviceBatteryLevel:[self createResponseForResult:result]];
}

-(void)getSdkVersion:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb getSdkVersion:[self createResponseForResult:result]];
}

-(void)setStopScannerOnRotate:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setStopScannerOnRotate:[call.arguments boolValue]];
}

-(void)enableCameraFlag:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb enableCameraFlag:[call.arguments[@"flag"] intValue] forMask:[call.arguments[@"codeMask"] intValue] completion:[self createResponseForResult:result]];
}

-(void)disableCameraFlag:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb disableCameraFlag:[call.arguments[@"flag"] intValue] forMask:[call.arguments[@"codeMask"] intValue] completion:[self createResponseForResult:result]];
}

-(void)setCameraParam:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setCameraParam:[call.arguments[@"param"] intValue] paramValue:[call.arguments[@"value"] intValue] forMask:[call.arguments[@"codeMask"] intValue] completion:[self createResponseForResult:result]];
}

- (void)getCameraExposureCompensationRange:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb getCameraExposureCompensationRange:[self createResponseForResult:result]];
}

- (void)setCameraExposureCompensation:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setCameraExposureCompensation:[call.arguments floatValue] completion:[self createResponseForResult:result]];
}

-(void)setParser:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setParser:[call.arguments boolValue] completion:[self createResponseForResult:result]];
}

-(void)setReadStringEncoding:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setReadStringEncoding:[call.arguments intValue] completion:[self createResponseForResult:result]];
}

-(void)setMDMReportingEnabled:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setMDMReportingEnabled:[call.arguments boolValue]];
}

-(void)createMDMAuthCredentials:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb createMDMAuthCredentialsWithUsername:call.arguments[@"username"] password:call.arguments[@"password"] clientID:call.arguments[@"clientID"] clientSecret:call.arguments[@"clientSecret"]];
}

-(void)loadCameraConfig:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb loadCameraConfig:[self createResponseForResult:result]];
}

-(void)showToast:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb showMessage:call.arguments];
}

-(void)hideToast:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb hideMessage];
}

-(void)scanImageFromUri:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb scanImageFromUri:call.arguments completion:[self createResponseForResult:result]];
}

-(void)scanImageFromBase64:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb scanImageFromBase64:call.arguments completion:[self createResponseForResult:result]];
}

-(void)setPairedBluetoothDevice:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb setPairedBluetoothDevice:call.arguments];
}

-(void)getPairedBluetoothDevice:(FlutterMethodCall*)call withCallback:(FlutterResult)result {
    [self.cmb getPairedBluetoothDevice:[self createResponseForResult:result]];
}

#pragma MARK CMBReaderDevice Delegate

- (void)availabilityDidChangeOfReader:(CMBReaderDevice *)reader {
    self.availabilityChannel.eventSink(@(reader.availability));
}

- (void)connectionStateDidChangeOfReader:(CMBReaderDevice *)reader {
    self.connectionChannel.eventSink(@(reader.connectionState));
}

- (void)didReceiveReadResultFromReader:(CMBReaderDevice *)reader results:(CMBReadResults *)readResults {
    NSString *jsonString = [CCPUtilities getResultsJSONStringFromResults:readResults keyResults:@"results" keySubResults:@"subResults"];
    self.resultsChannel.eventSink(jsonString);
}

- (void)scanningStateChanged:(BOOL)isScanning {
    self.scanningStateChannel.eventSink(@(isScanning));
}

@end
