//--------------------------------------------------------------------------------------------------
// Copyright (c) id3 Technologies
// All Rights Reserved.
//--------------------------------------------------------------------------------------------------
// ignore_for_file: unused_import
import 'dart:convert';
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';

/// The FaceLibrary class serves as the entry point to the id3 Face SDK. It provides essential methods for initializing the SDK, managing face models, and accessing version information. This class must be utilized to leverage the facial recognition capabilities offered by the SDK.
class FaceLibrary {

  // Public methods
  /// Retrieves the model file name which is needed in the LoadModel function.
  ///
  /// param model The Face Model to look for.
  /// return The expected file name
  /// throws FaceException An error has occurred during Face Library execution.
  static String getModelFileName(FaceModel model) {
    Pointer<Char> pFileName = nullptr;
    Pointer<Int> pFileNameSize = calloc.allocate(1);
    pFileNameSize[0] = -1;
    try {
      var err = faceSDK.id3FaceLibrary_GetModelFileName(model.value, pFileName, pFileNameSize);
      if (err == FaceError.insufficientBuffer.value) {
        pFileName = calloc.allocate(pFileNameSize.value);
        err = faceSDK.id3FaceLibrary_GetModelFileName(model.value, pFileName, pFileNameSize);
        if (err != FaceError.success.value) {
          throw FaceException(err);
        }
      }
      final vFileName = utf8.decode(Uint8List.fromList(pFileName.cast<Uint8>().asTypedList(pFileNameSize.value)));
      return vFileName;
    } finally {
      calloc.free(pFileName);
      calloc.free(pFileNameSize);
    }
  }

  /// Retrieves the library version as a `X.Y.Z` formatted string.
  ///
  /// return A string that identifies the library version.
  /// throws FaceException An error has occurred during Face Library execution.
  static String getVersion() {
    Pointer<Char> pLibraryVersion = calloc.allocate(8);
    Pointer<Int> pLibraryVersionSize = calloc.allocate(1);
    pLibraryVersionSize[0] = 8;
    try {
      var err = faceSDK.id3FaceLibrary_GetVersion(pLibraryVersion, pLibraryVersionSize);
      if (err == FaceError.insufficientBuffer.value) {
        calloc.free(pLibraryVersion);
        pLibraryVersion = calloc.allocate(pLibraryVersionSize.value);
        err = faceSDK.id3FaceLibrary_GetVersion(pLibraryVersion, pLibraryVersionSize);
        if (err != FaceError.success.value) {
          throw FaceException(err);
        }
      }
      final vLibraryVersion = utf8.decode(Uint8List.fromList(pLibraryVersion.cast<Uint8>().asTypedList(pLibraryVersionSize.value)));
      return vLibraryVersion;
    } finally {
      calloc.free(pLibraryVersion);
      calloc.free(pLibraryVersionSize);
    }
  }

  /// Retrieves the library version as a structure.
  ///
  /// return The version.
  /// throws FaceException An error has occurred during Face Library execution.
  static LibVersion getVersionEx() {
    Pointer<id3FaceLibVersion> pVersion = calloc();
    var err = faceSDK.id3FaceLibrary_GetVersionEx(pVersion);
    if (err != FaceError.success.value) {
    	calloc.free(pVersion);
    	throw FaceException(err);
    }
    return LibVersion(pVersion);
  }

  /// Loads a specified AI model into memory from the specified directory.
  ///
  /// param modelPath The path to directory containing the AI model files.
  /// param faceModel The AI model to be loaded.
  /// param processingUnit The processing unit to be used.
  /// throws FaceException An error has occurred during Face Library execution.
  static void loadModel(String? modelPath, FaceModel faceModel, ProcessingUnit processingUnit) {
    Pointer<Char>? pModelPath = modelPath?.toNativeUtf8().cast<Char>();
    try {
      var err = faceSDK.id3FaceLibrary_LoadModel(pModelPath ?? nullptr, faceModel.value, processingUnit.value);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
    } finally {
      if (pModelPath != null) {
        calloc.free(pModelPath);
      }
    }
  }

  /// Loads a model into memory from the specified buffer.
  ///
  /// param modelBuffer A buffer containing the AI model to be loaded.
  /// param faceModel The AI model to be loaded.
  /// param processingUnit The processing unit to be used.
  /// throws FaceException An error has occurred during Face Library execution.
  static void loadModelBuffer(Uint8List? modelBuffer, FaceModel faceModel, ProcessingUnit processingUnit) {
    Pointer<UnsignedChar>? pModelBuffer;
    if (modelBuffer != null) {
    	pModelBuffer = calloc.allocate<UnsignedChar>(modelBuffer.length);
    	pModelBuffer.cast<Uint8>().asTypedList(modelBuffer.length).setAll(0, modelBuffer);
    }
    try {
      var err = faceSDK.id3FaceLibrary_LoadModelBuffer(pModelBuffer ?? nullptr, modelBuffer?.length ?? 0, faceModel.value, processingUnit.value);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
    } finally {
      if (pModelBuffer != null) {
        calloc.free(pModelBuffer);
      }
    }
  }

  /// Unloads a model from memory.
  ///
  /// param faceModel The AI model to be unloaded.
  /// param processingUnit The processing unit used for the model.
  /// throws FaceException An error has occurred during Face Library execution.
  static void unloadModel(FaceModel faceModel, ProcessingUnit processingUnit) {
    var err = faceSDK.id3FaceLibrary_UnloadModel(faceModel.value, processingUnit.value);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

}

