//--------------------------------------------------------------------------------------------------
// Copyright (c) id3 Technologies
// All Rights Reserved.
//--------------------------------------------------------------------------------------------------
// ignore_for_file: unused_import
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'document_sdk_generated_bindings.dart';
import 'document_native.dart';

import '../id3_document.dart';

final _finalizer = NativeFinalizer(documentSDK.addresses.id3DocumentDetector_Dispose.cast());

/// Detects documents in images.
class DocumentDetector implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3DocumentDetector>> _pHandle;
  bool _disposable = true;

  /// Gets the native handle.
  /// return The native handle.
  Pointer<id3DocumentDetector> get handle => _pHandle.value;

  /// Creates a new instance of the DocumentDetector class.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  DocumentDetector() {
    _pHandle = calloc();
    try {
      var err = documentSDK.id3DocumentDetector_Initialize(_pHandle);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      _finalizer.attach(this, _pHandle.cast(), detach: this);
    } finally {}
  }

  /// Creates a new instance of the DocumentDetector class.
  ///
  /// param handle     Handle to the DocumentDetector.
  /// throws DocumentException An error has occurred during Document Library execution.
  DocumentDetector.fromHandle(Pointer<id3DocumentDetector> handle) {
    _pHandle = calloc();
    _pHandle.value = handle;
    _disposable = false;
  }

  /// Releases all resources used by this DocumentDetector.
  void dispose() {
    if (_disposable) {
      documentSDK.id3DocumentDetector_Dispose(_pHandle);
      calloc.free(_pHandle);
      _finalizer.detach(this);
    }
  }


  ///
  /// Confidence threshold, from 0 to 100+.
  /// Hint: Default value is 50. Lower threshold can increase number of false detection.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  int get confidenceThreshold => getConfidenceThreshold();
  set confidenceThreshold(int value) => setConfidenceThreshold(value);

  ///
  /// Ratio of the estimated width of the searched document in the image.
  /// Hint: Default value is 1.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  double get documentWidthRatio => getDocumentWidthRatio();
  set documentWidthRatio(double value) => setDocumentWidthRatio(value);

  ///
  /// Model to be used for document detection.
  /// Hint: Default value is DocumentDetector2A.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  DocumentModel get model => getModel();
  set model(DocumentModel value) => setModel(value);

  ///
  /// Processing unit where to run the detection process.
  /// Hint: Default value is CPU.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  ProcessingUnit get processingUnit => getProcessingUnit();
  set processingUnit(ProcessingUnit value) => setProcessingUnit(value);

  ///
  /// Number of threads to use for detection.
  /// Hint: Default value is 1. Allocating more than 1 thread here can increase the speed of the process.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  int get threadCount => getThreadCount();
  set threadCount(int value) => setThreadCount(value);

  // Public methods
  /// Gets the confidence threshold, from 0 to 100+.
  /// Hint: Default value is 50. Lower threshold can increase number of false detection.
  ///
  /// return Confidence threshold, from 0 to 100+.
  /// throws DocumentException An error has occurred during Document Library execution.
  int getConfidenceThreshold() {
    Pointer<Int> pConfidenceThreshold = calloc();
    try {
      var err = documentSDK.id3DocumentDetector_GetConfidenceThreshold(_pHandle.value, pConfidenceThreshold);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vConfidenceThreshold = pConfidenceThreshold.value;
      return vConfidenceThreshold;
    } finally {
      calloc.free(pConfidenceThreshold);
    }
  }

  /// Sets the confidence threshold, from 0 to 100+.
  /// Hint: Default value is 50. Lower threshold can increase number of false detection.
  ///
  /// param confidenceThreshold Confidence threshold, from 0 to 100+.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setConfidenceThreshold(int confidenceThreshold) {
    var err = documentSDK.id3DocumentDetector_SetConfidenceThreshold(_pHandle.value, confidenceThreshold);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Gets the ratio of the estimated width of the searched document in the image.
  /// Hint: Default value is 1.
  ///
  /// return Ratio of the estimated width of the searched document in the image.
  /// throws DocumentException An error has occurred during Document Library execution.
  double getDocumentWidthRatio() {
    Pointer<Float> pDocumentWidthRatio = calloc();
    try {
      var err = documentSDK.id3DocumentDetector_GetDocumentWidthRatio(_pHandle.value, pDocumentWidthRatio);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vDocumentWidthRatio = pDocumentWidthRatio.value;
      return vDocumentWidthRatio;
    } finally {
      calloc.free(pDocumentWidthRatio);
    }
  }

  /// Sets the ratio of the estimated width of the searched document in the image.
  /// Hint: Default value is 1.
  ///
  /// param documentWidthRatio Ratio of the estimated width of the searched document in the image.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setDocumentWidthRatio(double documentWidthRatio) {
    var err = documentSDK.id3DocumentDetector_SetDocumentWidthRatio(_pHandle.value, documentWidthRatio);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Gets the model to be used for document detection.
  /// Hint: Default value is DocumentDetector2A.
  ///
  /// return Model to be used for document detection.
  /// throws DocumentException An error has occurred during Document Library execution.
  DocumentModel getModel() {
    Pointer<Int32> pModel = calloc();
    try {
      var err = documentSDK.id3DocumentDetector_GetModel(_pHandle.value, pModel);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vModel = DocumentModelX.fromValue(pModel.value);
      return vModel;
    } finally {
      calloc.free(pModel);
    }
  }

  /// Sets the model to be used for document detection.
  /// Hint: Default value is DocumentDetector2A.
  ///
  /// param model Model to be used for document detection.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setModel(DocumentModel model) {
    var err = documentSDK.id3DocumentDetector_SetModel(_pHandle.value, model.value);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Gets the processing unit where to run the detection process.
  /// Hint: Default value is CPU.
  ///
  /// return Processing unit where to run the detection process.
  /// throws DocumentException An error has occurred during Document Library execution.
  ProcessingUnit getProcessingUnit() {
    Pointer<Int32> pProcessingUnit = calloc();
    try {
      var err = documentSDK.id3DocumentDetector_GetProcessingUnit(_pHandle.value, pProcessingUnit);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vProcessingUnit = ProcessingUnitX.fromValue(pProcessingUnit.value);
      return vProcessingUnit;
    } finally {
      calloc.free(pProcessingUnit);
    }
  }

  /// Sets the processing unit where to run the detection process.
  /// Hint: Default value is CPU.
  ///
  /// param processingUnit Processing unit where to run the detection process.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setProcessingUnit(ProcessingUnit processingUnit) {
    var err = documentSDK.id3DocumentDetector_SetProcessingUnit(_pHandle.value, processingUnit.value);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Gets the number of threads to use for detection.
  /// Hint: Default value is 1. Allocating more than 1 thread here can increase the speed of the process.
  ///
  /// return Number of threads to use for detection.
  /// throws DocumentException An error has occurred during Document Library execution.
  int getThreadCount() {
    Pointer<Int> pThreadCount = calloc();
    try {
      var err = documentSDK.id3DocumentDetector_GetThreadCount(_pHandle.value, pThreadCount);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vThreadCount = pThreadCount.value;
      return vThreadCount;
    } finally {
      calloc.free(pThreadCount);
    }
  }

  /// Sets the number of threads to use for detection.
  /// Hint: Default value is 1. Allocating more than 1 thread here can increase the speed of the process.
  ///
  /// param threadCount Number of threads to use for detection.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setThreadCount(int threadCount) {
    var err = documentSDK.id3DocumentDetector_SetThreadCount(_pHandle.value, threadCount);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Aligns and crops the specified document on the specified image and returns a new DocumentImage.
  ///
  /// param sourceImage The source image containing the document.
  /// param detectedDocument The detected document in the image.
  /// return Realigned and cropped image of the document.
  /// throws DocumentException An error has occurred during Document Library execution.
  DocumentImage alignDocument(DocumentImage sourceImage, DetectedDocument detectedDocument) {
    DocumentImage alignedImage = DocumentImage();
    var err = documentSDK.id3DocumentDetector_AlignDocument(_pHandle.value, sourceImage.handle, detectedDocument.handle, alignedImage.handle);
    if (err != DocumentError.success.value) {
      alignedImage.dispose();
      throw DocumentException(err);
    }
    return alignedImage;
  }

  /// Detects a document on a delimited area of the specified DocumentImage and returns a DetectedDocument.
  /// Important: Document templates must be loaded before calling this method.
  ///
  /// param image Source image to process
  /// param detectionZone Crop in the image where to look for document. Default is the full image. Output corners are in the full image referential.
  /// return Detected document in the image
  /// throws DocumentException An error has occurred during Document Library execution.
  DetectedDocument detectDocument(DocumentImage image, Rectangle detectionZone) {
    DetectedDocument detectedDocument = DetectedDocument();
    var err = documentSDK.id3DocumentDetector_DetectDocument(_pHandle.value, image.handle, detectionZone.handle, detectedDocument.handle);
    if (err != DocumentError.success.value) {
      detectedDocument.dispose();
      throw DocumentException(err);
    }
    return detectedDocument;
  }

  /// Detects a document with specified name on a delimited area of the specified DocumentImage and returns a DetectedDocument.
  /// Important: The relevant document template must be loaded before calling this method.
  ///
  /// param image The source image to process.
  /// param documentName The name of the document to be searched for. Must be a key loaded in DocumentLibrary.
  /// param detectionZone A delimited area in the image where to search for the document. Default is the full image. Output corners are in the full image referential.
  /// return Detected document in the image
  /// throws DocumentException An error has occurred during Document Library execution.
  DetectedDocument detectDocumentByName(DocumentImage image, String? documentName, Rectangle detectionZone) {
    Pointer<Char>? pDocumentName = documentName?.toNativeUtf8().cast<Char>();
    DetectedDocument detectedDocument = DetectedDocument();
    try {
      var err = documentSDK.id3DocumentDetector_DetectDocumentByName(_pHandle.value, image.handle, pDocumentName ?? nullptr, detectionZone.handle, detectedDocument.handle);
      if (err != DocumentError.success.value) {
        detectedDocument.dispose();
        throw DocumentException(err);
      }
      return detectedDocument;
    } finally {
      if (pDocumentName != null) {
        calloc.free(pDocumentName);
      }
    }
  }

}

