import Foundation
import Capacitor
import ScanbotBarcodeSDKWrapper

@objc(ScanbotBarcodeSDKPlugin)
public class ScanbotBarcodeSDKPlugin: CAPPlugin, CAPBridgedPlugin {

  private var barcodeItemMapperPluginCall : CAPPluginCall? = nil

  // MARK: - Methods from CAPBridgedPlugin. We use this protocol to avoid creating .h and .m files for exporting swift to objc
  public var identifier: String = "ScanbotBarcodeSDKPlugin"

  public var jsName: String = "ScanbotBarcodeSDK"

  // Need to define all methods here to be available for the Capacitor web runtime
  public var pluginMethods: [CAPPluginMethod] = [
    CAPPluginMethod(name: "initialize", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "getLicenseInfo", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "cleanupStorage", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "mockCamera", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "startBarcodeScanner", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "forceCloseBarcodeScanner", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "scanBarcodesFromImage", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "scanBarcodesFromPdf", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "parseBarcodeDocument", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "readImageData", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "registerBarcodeItemMapperCallback", returnType: CAPPluginReturnNone),
    CAPPluginMethod(name: "onBarcodeItemMapper", returnType: CAPPluginReturnNone),
    // ImageRefs
    CAPPluginMethod(name: "imageRefSerialize", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefDeserialize", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefFromImageFileUri", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefFromEncodedBuffer", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefHibernate", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefClear", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefInfo", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefSaveImage", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefEncodeImage", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefRelease", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "makeSnapshot", returnType: CAPPluginReturnPromise),
    CAPPluginMethod(name: "imageRefReleaseAll", returnType: CAPPluginReturnPromise),
  ];

  // MARK: - Public methods

  @objc func initialize(_ pluginCall: CAPPluginCall) {
    SBBWrapper.initializeSDK(configuration: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func getLicenseInfo(_ pluginCall: CAPPluginCall) {
    SBSDKManager.getLicenseInfo(resultDelegate: pluginCall.delegate)
  }

  @objc func cleanupStorage(_ pluginCall: CAPPluginCall) {
    SBBWrapper.cleanup(resultDelegate: pluginCall.delegate)
  }

  @objc func mockCamera(_ pluginCall: CAPPluginCall) {
    SBSDKManager.mockCamera(options: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func startBarcodeScanner(_ pluginCall: CAPPluginCall) {
    guard let parentVC = getPresentedViewControllerOrReject(pluginCall)
    else {
      return
    }

    if self.barcodeItemMapperPluginCall != nil {


      let pluginResultDelegate = ScanbotBarcodeSDKPluginResultDelegate(pluginCall) {
        if let safeBarcodeItemMapperPluginCall = self.barcodeItemMapperPluginCall {
          self.bridge?.releaseCall(safeBarcodeItemMapperPluginCall)
          self.barcodeItemMapperPluginCall = nil
        }
      }

      SBBarcode.startScanner(
        parentViewController: parentVC,
        configuration: pluginCall.data,
        resultDelegate: pluginResultDelegate) { barcodeItem in
          if let safeBarcodeItemMapperPluginCall = self.barcodeItemMapperPluginCall {
            safeBarcodeItemMapperPluginCall.resolve(barcodeItem)
          }
        }

    } else {
      SBBarcode.startScanner(
        parentViewController: parentVC,
        configuration: pluginCall.data,
        resultDelegate: pluginCall.delegate)
    }
  }

  @objc func forceCloseBarcodeScanner(_ pluginCall: CAPPluginCall) {
    SBBarcode.forceClose(resultDelegate: pluginCall.delegate)
  }

  @objc func scanBarcodesFromImage(_ pluginCall: CAPPluginCall) {
    SBBarcode.scanFromImage(options: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func scanBarcodesFromPdf(_ pluginCall: CAPPluginCall){
    SBBarcode.scanFromPDF(options: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func parseBarcodeDocument(_ pluginCall: CAPPluginCall){
    SBBarcode.parseDocument(options: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func readImageData(_ pluginCall: CAPPluginCall) {
    SBImageProcessor.readImageData(options: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  // MARK: - ImageRefs

  @objc func imageRefSerialize(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefSerialize(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefDeserialize(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefDeserialize(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefFromImageFileUri(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefFromImageFileUri(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefFromEncodedBuffer(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefFromEncodedBuffer(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefHibernate(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefHibernate(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefClear(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefClear(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefRelease(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefRelease(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefInfo(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefInfo(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefSaveImage(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefSaveImage(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefEncodeImage(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefEncodeImage(operationConfig: pluginCall.data, resultDelegate: pluginCall.delegate)
  }

  @objc func makeSnapshot(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefProfilerSnapshot(resultDelegate: pluginCall.delegate)
  }

  @objc func imageRefReleaseAll(_ pluginCall: CAPPluginCall) {
    SBImageRef.imageRefReleaseAll(resultDelegate: pluginCall.delegate)
  }

  // MARK: - Helper methods that are used only by us from the plugin middle layer (not exposed to the users)

  @objc func registerBarcodeItemMapperCallback(_ pluginCall: CAPPluginCall) {
    pluginCall.keepAlive = true
    barcodeItemMapperPluginCall = pluginCall
  }

  @objc func onBarcodeItemMapper(_ call: CAPPluginCall) {
    guard let barcodeUUID = call.options["barcodeItemUuid"] as? String else {
      call.reject("Missing required property barcodeItemUuid")
      return
    }

    let barcodeMappedData = call.options["barcodeMappedData"] as? [String: Any]

    SBBarcode.onBarcodeItemMapper(barcodeItemUUID: barcodeUUID, barcodeMappedDataAsDictionary: barcodeMappedData)
  }


  // MARK: - Private helper methods
  private func getPresentedViewControllerOrReject(_ pluginCall: CAPPluginCall) -> UIViewController? {
    guard let presentedVC = self.bridge?.viewController else {
      pluginCall.reject("No active view controller")
      return nil
    }

    return presentedVC
  }

}

extension CAPPluginCall {

  var data: [String: Any] { options as? [String: Any] ?? [:] }

  var delegate: ScanbotBarcodeSDKPluginResultDelegate { ScanbotBarcodeSDKPluginResultDelegate(self) }

}
