//--------------------------------------------------------------------------------------------------
// 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 'face_sdk_generated_bindings.dart';
import 'face_native.dart';

import '../id3_face.dart';

final _finalizer = NativeFinalizer(faceSDK.addresses.id3FacePad_Dispose.cast());

/// Detects presentation attacks in still images or video frames.
class FacePad implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3FacePad>> _pHandle;
  bool _disposable = true;

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

  /// Creates a new instance of the FacePad class.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  FacePad() {
    _pHandle = calloc();
    try {
      var err = faceSDK.id3FacePad_Initialize(_pHandle);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      _finalizer.attach(this, _pHandle.cast(), detach: this);
    } finally {}
  }

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

  /// Releases all resources used by this FacePad.
  void dispose() {
    if (_disposable) {
      faceSDK.id3FacePad_Dispose(_pHandle);
      calloc.free(_pHandle);
      _finalizer.detach(this);
    }
  }


  ///
  /// Attack support detector confidence threshold, in the range [0;100].
  /// Hint: Default value is 25.
  /// Note: Setting a high threshold reduces false attack support detections but can increase the number of undetected attack supports.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  int get attackSupportDetectorConfidenceThreshold => getAttackSupportDetectorConfidenceThreshold();
  set attackSupportDetectorConfidenceThreshold(int value) => setAttackSupportDetectorConfidenceThreshold(value);

  // Public methods
  /// Gets the attack support detector confidence threshold, in the range (0;100).
  /// Hint: Default value is 25.
  /// Note: Setting a high threshold reduces false attack support detections but can increase the number of undetected attack supports.
  ///
  /// return Attack support detector confidence threshold, in the range (0;100).
  /// throws FaceException An error has occurred during Face Library execution.
  int getAttackSupportDetectorConfidenceThreshold() {
    Pointer<Int> pAttackSupportDetectorConfidenceThreshold = calloc();
    try {
      var err = faceSDK.id3FacePad_GetAttackSupportDetectorConfidenceThreshold(_pHandle.value, pAttackSupportDetectorConfidenceThreshold);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vAttackSupportDetectorConfidenceThreshold = pAttackSupportDetectorConfidenceThreshold.value;
      return vAttackSupportDetectorConfidenceThreshold;
    } finally {
      calloc.free(pAttackSupportDetectorConfidenceThreshold);
    }
  }

  /// Sets the attack support detector confidence threshold, in the range (0;100).
  /// Hint: Default value is 25.
  /// Note: Setting a high threshold reduces false attack support detections but can increase the number of undetected attack supports.
  ///
  /// param attackSupportDetectorConfidenceThreshold Attack support detector confidence threshold, in the range (0;100).
  /// throws FaceException An error has occurred during Face Library execution.
  void setAttackSupportDetectorConfidenceThreshold(int attackSupportDetectorConfidenceThreshold) {
    var err = faceSDK.id3FacePad_SetAttackSupportDetectorConfidenceThreshold(_pHandle.value, attackSupportDetectorConfidenceThreshold);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// Computes a blurriness score for a color image.
  /// An attack presented on a low resolution support has more chance to be blurred than a bonafide.
  /// The maximum recommended value is 20.
  /// Important: Loading the model ``FaceBlurrinessDetector`` is required to use this function.
  ///
  /// param image Source image to process.
  /// param detectedFace Detected face to process.
  /// return The computed blurriness score, in the range (0;100).
  /// throws FaceException An error has occurred during Face Library execution.
  int computeBlurrinessScore(Image image, DetectedFace detectedFace) {
    Pointer<Int> pScore = calloc();
    try {
      var err = faceSDK.id3FacePad_ComputeBlurrinessScore(_pHandle.value, image.handle, detectedFace.handle, pScore);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vScore = pScore.value;
      return vScore;
    } finally {
      calloc.free(pScore);
    }
  }

  /// Computes the PAD score and confidence of a detected face using only the color image.
  /// A high score means a high probability for the face to be bona-fide, hence not an attack. The minimum recommended score is 90.
  /// A low confidence means that the quality of the image is not sufficient enough to take a decision. The minimum recommended confidence is 70.
  /// Warning: A minimum IOD (64 pixels per default) for the detected face is required for this function, below this value it will output an error.
  /// Important: Loading the ``FaceColorBasedPad`` model is required to use this function.
  ///
  /// param image Source image to process.
  /// param detectedFace Detected face to process.
  /// return The computed PAD result including score and confidence.
  /// throws FaceException An error has occurred during Face Library execution.
  ColorBasedPadResult computeColorBasedScore(Image image, DetectedFace detectedFace) {
    Pointer<id3FaceColorBasedPadResult> pColorBasedPadResult = calloc();
    var err = faceSDK.id3FacePad_ComputeColorBasedScore(_pHandle.value, image.handle, detectedFace.handle, pColorBasedPadResult);
    if (err != FaceError.success.value) {
    	calloc.free(pColorBasedPadResult);
    	throw FaceException(err);
    }
    return ColorBasedPadResult(pColorBasedPadResult);
  }

  /// Computes the PAD score of a detected face using a depth map image.
  /// A high score means a high probability for the face to be bona-fide, hence not an attack.
  /// The minimum recommended value is 10.
  /// Important: Loading the ``FaceDepthBasedPad`` model is required to use this function.
  ///
  /// param image Source image to process. Must be Grayscale 16 Bits.
  /// param detectedFace Detected face to process.
  /// return The computed PAD score, in the range (0;100).
  /// throws FaceException An error has occurred during Face Library execution.
  int computeDepthBasedScore(Image image, DetectedFace detectedFace) {
    Pointer<Int> pScore = calloc();
    try {
      var err = faceSDK.id3FacePad_ComputeDepthBasedScore(_pHandle.value, image.handle, detectedFace.handle, pScore);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vScore = pScore.value;
      return vScore;
    } finally {
      calloc.free(pScore);
    }
  }

  /// Detects if an attack support surrounds the detected face.
  /// The recommended usage is to consider the image to be an attack as soon as an attack support is detected.
  /// Important: Loading the ``FaceAttackSupportDetector`` model is required to use this function.
  ///
  /// param image Source image to process.
  /// param detectedFace Detected face to process.
  /// return The detected face attack support.
  /// throws FaceException An error has occurred during Face Library execution.
  DetectedFaceAttackSupport detectAttackSupport(Image image, DetectedFace detectedFace) {
    Pointer<id3DetectedFaceAttackSupport> pDetectedFaceAttackSupport = calloc();
    var err = faceSDK.id3FacePad_DetectAttackSupport(_pHandle.value, image.handle, detectedFace.handle, pDetectedFaceAttackSupport);
    if (err != FaceError.success.value) {
    	calloc.free(pDetectedFaceAttackSupport);
    	throw FaceException(err);
    }
    return DetectedFaceAttackSupport(pDetectedFaceAttackSupport);
  }

}

