//--------------------------------------------------------------------------------------------------
// 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.id3FaceIndexer_Dispose.cast());

/// Performs a one-to-many search on a large number of face templates.
class FaceIndexer implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3FaceIndexer>> _pHandle;
  bool _disposable = true;

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

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

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

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


  ///
  /// Face template format accepted by this face indexer.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  FaceTemplateFormat get format => getFormat();

  ///
  /// Maximum number of templates that this face indexer can hold.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  int get maximumTemplateCount => getMaximumTemplateCount();

  ///
  /// Current number of indexed templates.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  int get templateCount => getTemplateCount();

  // Public methods
  /// Gets the face template format accepted by this face indexer.
  ///
  /// return Face template format accepted by this face indexer.
  /// throws FaceException An error has occurred during Face Library execution.
  FaceTemplateFormat getFormat() {
    Pointer<Int32> pFormat = calloc();
    try {
      var err = faceSDK.id3FaceIndexer_GetFormat(_pHandle.value, pFormat);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vFormat = FaceTemplateFormatX.fromValue(pFormat.value);
      return vFormat;
    } finally {
      calloc.free(pFormat);
    }
  }

  /// Gets the maximum number of templates that this face indexer can hold.
  ///
  /// return Maximum number of templates that this face indexer can hold.
  /// throws FaceException An error has occurred during Face Library execution.
  int getMaximumTemplateCount() {
    Pointer<Int> pMaximumTemplateCount = calloc();
    try {
      var err = faceSDK.id3FaceIndexer_GetMaximumTemplateCount(_pHandle.value, pMaximumTemplateCount);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vMaximumTemplateCount = pMaximumTemplateCount.value;
      return vMaximumTemplateCount;
    } finally {
      calloc.free(pMaximumTemplateCount);
    }
  }

  /// Gets the current number of indexed templates.
  ///
  /// return Current number of indexed templates.
  /// throws FaceException An error has occurred during Face Library execution.
  int getTemplateCount() {
    Pointer<Int> pTemplateCount = calloc();
    try {
      var err = faceSDK.id3FaceIndexer_GetTemplateCount(_pHandle.value, pTemplateCount);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vTemplateCount = pTemplateCount.value;
      return vTemplateCount;
    } finally {
      calloc.free(pTemplateCount);
    }
  }

  /// Adds a template to the Face Indexer.
  /// Note: This function is thread safe with other AddTemplate() calls but not with SearchTemplates() calls.
  ///
  /// param faceTemplate Face template to add to the face indexer.
  /// param id ID of the added face template.
  /// throws FaceException An error has occurred during Face Library execution.
  void addTemplate(FaceTemplate faceTemplate, String? id) {
    Pointer<Char>? pId = id?.toNativeUtf8().cast<Char>();
    try {
      var err = faceSDK.id3FaceIndexer_AddTemplate(_pHandle.value, faceTemplate.handle, pId ?? nullptr);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
    } finally {
      if (pId != null) {
        calloc.free(pId);
      }
    }
  }

  /// Creates an empty face indexer
  ///
  /// param maximumTemplateCount Maximum number of templates that this face indexer can hold.
  /// param format Face template format accepted by this face indexer.
  /// return The newly created face indexer.
  /// throws FaceException An error has occurred during Face Library execution.
  static FaceIndexer create(int maximumTemplateCount, FaceTemplateFormat format) {
    FaceIndexer faceIndexer = FaceIndexer();
    var err = faceSDK.id3FaceIndexer_Create(faceIndexer.handle, maximumTemplateCount, format.value);
    if (err != FaceError.success.value) {
      faceIndexer.dispose();
      throw FaceException(err);
    }
    return faceIndexer;
  }

  /// Deletes a template from the face indexer.
  ///
  /// param id ID of the face template to delete.
  /// throws FaceException An error has occurred during Face Library execution.
  void deleteTemplate(String? id) {
    Pointer<Char>? pId = id?.toNativeUtf8().cast<Char>();
    try {
      var err = faceSDK.id3FaceIndexer_DeleteTemplate(_pHandle.value, pId ?? nullptr);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
    } finally {
      if (pId != null) {
        calloc.free(pId);
      }
    }
  }

  /// Imports the face indexer object from a file.
  ///
  /// param path Path to the file to import the face indexer object from.
  /// return The newly created face indexer.
  /// throws FaceException An error has occurred during Face Library execution.
  static FaceIndexer fromFile(String? path) {
    FaceIndexer faceIndexer = FaceIndexer();
    Pointer<Char>? pPath = path?.toNativeUtf8().cast<Char>();
    try {
      var err = faceSDK.id3FaceIndexer_FromFile(faceIndexer.handle, pPath ?? nullptr);
      if (err != FaceError.success.value) {
        faceIndexer.dispose();
        throw FaceException(err);
      }
      return faceIndexer;
    } finally {
      if (pPath != null) {
        calloc.free(pPath);
      }
    }
  }

  /// Retrieves a template from the face indexer by ID.
  ///
  /// param id ID of the required face template.
  /// return The face template.
  /// throws FaceException An error has occurred during Face Library execution.
  FaceTemplate getTemplate(String? id) {
    Pointer<Char>? pId = id?.toNativeUtf8().cast<Char>();
    FaceTemplate faceTemplate = FaceTemplate();
    try {
      var err = faceSDK.id3FaceIndexer_GetTemplate(_pHandle.value, pId ?? nullptr, faceTemplate.handle);
      if (err != FaceError.success.value) {
        faceTemplate.dispose();
        throw FaceException(err);
      }
      return faceTemplate;
    } finally {
      if (pId != null) {
        calloc.free(pId);
      }
    }
  }

  /// Resizes the maximum number of templates that the face indexer can hold.
  ///
  /// param maximumCount Maximum number of templates that this face indexer can hold.
  /// throws FaceException An error has occurred during Face Library execution.
  void resize(int maximumCount) {
    var err = faceSDK.id3FaceIndexer_Resize(_pHandle.value, maximumCount);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// Performs a one-to-many search of a face template probe against a previously initialized face indexer and outputs a candidate list.
  /// This function is thread safe with other SearchTemplate() calls but not with AddTemplate() calls.
  ///
  /// param probe The face template probe.
  /// param maxCandidates Maximum number of candidates in the candidate list.
  /// return The list of candidates sorted by decreasing match score.
  /// throws FaceException An error has occurred during Face Library execution.
  FaceCandidateList searchTemplate(FaceTemplate probe, int maxCandidates) {
    FaceCandidateList candidates = FaceCandidateList();
    var err = faceSDK.id3FaceIndexer_SearchTemplate(_pHandle.value, probe.handle, maxCandidates, candidates.handle);
    if (err != FaceError.success.value) {
      candidates.dispose();
      throw FaceException(err);
    }
    return candidates;
  }

  /// Saves the face indexer object to a file.
  ///
  /// param path Path to the file to export the face indexer object to.
  /// throws FaceException An error has occurred during Face Library execution.
  void toFile(String? path) {
    Pointer<Char>? pPath = path?.toNativeUtf8().cast<Char>();
    try {
      var err = faceSDK.id3FaceIndexer_ToFile(_pHandle.value, pPath ?? nullptr);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
    } finally {
      if (pPath != null) {
        calloc.free(pPath);
      }
    }
  }

}

