//--------------------------------------------------------------------------------------------------
// 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 'document_sdk_generated_bindings.dart';
import 'document_native.dart';

import '../id3_document.dart';

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

/// Verifies the authenticity of a document using security features such as holograms.
class DocumentAuthenticator implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3DocumentAuthenticator>> _pHandle;
  bool _disposable = true;

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

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

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

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


  ///
  /// Document type to authenticate.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  String get documentName => getDocumentName();
  set documentName(String value) => setDocumentName(value);

  ///
  /// Size of the FIFO frame buffer, used for processing.
  ///
  /// throws DocumentException An error has occurred during Document Library execution.
  int get temporalWindowSize => getTemporalWindowSize();
  set temporalWindowSize(int value) => setTemporalWindowSize(value);

  // Public methods
  /// Gets the document type to authenticate.
  ///
  /// return Document type to authenticate.
  /// throws DocumentException An error has occurred during Document Library execution.
  String getDocumentName() {
    Pointer<Char> pDocumentName = nullptr;
    Pointer<Int> pDocumentNameSize = calloc.allocate(1);
    pDocumentNameSize[0] = -1;
    try {
      var err = documentSDK.id3DocumentAuthenticator_GetDocumentName(_pHandle.value, pDocumentName, pDocumentNameSize);
      if (err == DocumentError.insufficientBuffer.value) {
        pDocumentName = calloc.allocate(pDocumentNameSize.value);
        err = documentSDK.id3DocumentAuthenticator_GetDocumentName(_pHandle.value, pDocumentName, pDocumentNameSize);
        if (err != DocumentError.success.value) {
          throw DocumentException(err);
        }
      }
      final vDocumentName = utf8.decode(Uint8List.fromList(pDocumentName.cast<Uint8>().asTypedList(pDocumentNameSize.value)));
      return vDocumentName;
    } finally {
      calloc.free(pDocumentName);
      calloc.free(pDocumentNameSize);
    }
  }

  /// Sets the document type to authenticate.
  ///
  /// param documentName Document type to authenticate.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setDocumentName(String? documentName) {
    Pointer<Char>? pDocumentName = documentName?.toNativeUtf8().cast<Char>();
    try {
      var err = documentSDK.id3DocumentAuthenticator_SetDocumentName(_pHandle.value, pDocumentName ?? nullptr);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
    } finally {
      if (pDocumentName != null) {
        calloc.free(pDocumentName);
      }
    }
  }

  /// Gets the size of the FIFO frame buffer, used for processing.
  ///
  /// return Size of the FIFO frame buffer, used for processing.
  /// throws DocumentException An error has occurred during Document Library execution.
  int getTemporalWindowSize() {
    Pointer<Int> pTemporalWindowSize = calloc();
    try {
      var err = documentSDK.id3DocumentAuthenticator_GetTemporalWindowSize(_pHandle.value, pTemporalWindowSize);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vTemporalWindowSize = pTemporalWindowSize.value;
      return vTemporalWindowSize;
    } finally {
      calloc.free(pTemporalWindowSize);
    }
  }

  /// Sets the size of the FIFO frame buffer, used for processing.
  ///
  /// param temporalWindowSize Size of the FIFO frame buffer, used for processing.
  /// throws DocumentException An error has occurred during Document Library execution.
  void setTemporalWindowSize(int temporalWindowSize) {
    var err = documentSDK.id3DocumentAuthenticator_SetTemporalWindowSize(_pHandle.value, temporalWindowSize);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Appends a frame in the FIFO buffer.
  ///
  /// param image Source image of the aligned document to append
  /// throws DocumentException An error has occurred during Document Library execution.
  void appendFrame(DocumentImage image) {
    var err = documentSDK.id3DocumentAuthenticator_AppendFrame(_pHandle.value, image.handle);
    if (err != DocumentError.success.value) {
      throw DocumentException(err);
    }
  }

  /// Authenticates the holograms on the current frame buffer.
  /// Important: Buffer must be full to the temporal window size.
  ///
  /// return List of result for each hologram enrolled in document.
  /// throws DocumentException An error has occurred during Document Library execution.
  HologramAuthenticationResultList authenticateHolograms() {
    HologramAuthenticationResultList scoreList = HologramAuthenticationResultList();
    var err = documentSDK.id3DocumentAuthenticator_AuthenticateHolograms(_pHandle.value, scoreList.handle);
    if (err != DocumentError.success.value) {
      scoreList.dispose();
      throw DocumentException(err);
    }
    return scoreList;
  }

  /// Checks if the frame buffer is full and ready to authenticate for all holograms.
  ///
  /// return A value indicating whether the frame buffer is full.
  /// throws DocumentException An error has occurred during Document Library execution.
  bool isFull() {
    Pointer<Bool> pResult = calloc();
    try {
      var err = documentSDK.id3DocumentAuthenticator_IsFull(_pHandle.value, pResult);
      if (err != DocumentError.success.value) {
        throw DocumentException(err);
      }
      final vResult = pResult.value;
      return vResult;
    } finally {
      calloc.free(pResult);
    }
  }

}

