//--------------------------------------------------------------------------------------------------
// 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 'bioseal_sdk_generated_bindings.dart';
import 'bioseal_native.dart';

import '../id3_bioseal.dart';

final _finalizer = NativeFinalizer(biosealSDK.addresses.id3BiosealManifest_Dispose.cast());

/// Provides information about the manifest.
class Manifest implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3BiosealManifest>> _pHandle;
  bool _disposable = true;

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

  /// Creates a new instance of the Manifest class.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Manifest() {
    _pHandle = calloc();
    try {
      var err = biosealSDK.id3BiosealManifest_Initialize(_pHandle);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      _finalizer.attach(this, _pHandle.cast(), detach: this);
    } finally {}
  }

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

  /// Releases all resources used by this Manifest.
  void dispose() {
    if (_disposable) {
      biosealSDK.id3BiosealManifest_Dispose(_pHandle);
      calloc.free(_pHandle);
      _finalizer.detach(this);
    }
  }


  ///
  /// The manifest ID in ISO 22385 format.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  int get manifestId => getManifestId();

  ///
  /// The manifest version.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  int get manifestVersion => getManifestVersion();

  ///
  /// The list of supported languages declared in the manifest.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  StringArray get supportedLanguages => getSupportedLanguages();

  ///
  /// The extension list.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  ExtensionList get extensionList => getExtensionList();

  ///
  /// The log.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Log get log => getLog();
  set log(Log value) => setLog(value);

  // Public methods
  /// Gets the the manifest ID in ISO 22385 format.
  ///
  /// return The manifest ID in ISO 22385 format.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  int getManifestId() {
    Pointer<Int> pManifestId = calloc();
    try {
      var err = biosealSDK.id3BiosealManifest_GetManifestId(_pHandle.value, pManifestId);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vManifestId = pManifestId.value;
      return vManifestId;
    } finally {
      calloc.free(pManifestId);
    }
  }

  /// Gets the the manifest version.
  ///
  /// return The manifest version.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  int getManifestVersion() {
    Pointer<Int> pManifestVersion = calloc();
    try {
      var err = biosealSDK.id3BiosealManifest_GetManifestVersion(_pHandle.value, pManifestVersion);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vManifestVersion = pManifestVersion.value;
      return vManifestVersion;
    } finally {
      calloc.free(pManifestVersion);
    }
  }

  /// Gets the the list of supported languages declared in the manifest.
  ///
  /// return The list of supported languages declared in the manifest.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  StringArray getSupportedLanguages() {
    StringArray supportedLanguages = StringArray();
    var err = biosealSDK.id3BiosealManifest_GetSupportedLanguages(_pHandle.value, supportedLanguages.handle);
    if (err != BiosealError.success.value) {
      supportedLanguages.dispose();
      throw BiosealException(err);
    }
    return supportedLanguages;
  }

  /// Gets the the extension list.
  ///
  /// return The extension list.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  ExtensionList getExtensionList() {
    ExtensionList extensionList = ExtensionList();
    var err = biosealSDK.id3BiosealManifest_GetExtensionList(_pHandle.value, extensionList.handle);
    if (err != BiosealError.success.value) {
      extensionList.dispose();
      throw BiosealException(err);
    }
    return extensionList;
  }

  /// Gets the the log.
  ///
  /// return The log.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Log getLog() {
    Log log = Log();
    var err = biosealSDK.id3BiosealManifest_GetLog(_pHandle.value, log.handle);
    if (err != BiosealError.success.value) {
      log.dispose();
      throw BiosealException(err);
    }
    return log;
  }

  /// Sets the the log.
  ///
  /// param log The log.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  void setLog(Log log) {
    var err = biosealSDK.id3BiosealManifest_SetLog(_pHandle.value, log.handle);
    if (err != BiosealError.success.value) {
      throw BiosealException(err);
    }
  }

  /// Loads the manifest.
  ///
  /// param manifestData The manifest data.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  void load(Uint8List? manifestData) {
    Pointer<UnsignedChar>? pManifestData;
    if (manifestData != null) {
    	pManifestData = calloc.allocate<UnsignedChar>(manifestData.length);
    	pManifestData.cast<Uint8>().asTypedList(manifestData.length).setAll(0, manifestData);
    }
    try {
      var err = biosealSDK.id3BiosealManifest_Load(_pHandle.value, pManifestData ?? nullptr, manifestData?.length ?? 0);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
    } finally {
      if (pManifestData != null) {
        calloc.free(pManifestData);
      }
    }
  }

  /// Loads the manifest.
  ///
  /// param manifestData The manifest data.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  void loadFromString(String? manifestData) {
    Pointer<Char>? pManifestData = manifestData?.toNativeUtf8().cast<Char>();
    try {
      var err = biosealSDK.id3BiosealManifest_LoadFromString(_pHandle.value, pManifestData ?? nullptr);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
    } finally {
      if (pManifestData != null) {
        calloc.free(pManifestData);
      }
    }
  }

  /// Loads the manifest from file.
  ///
  /// param path The manifest path.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  void loadFromFile(String? path) {
    Pointer<Char>? pPath = path?.toNativeUtf8().cast<Char>();
    try {
      var err = biosealSDK.id3BiosealManifest_LoadFromFile(_pHandle.value, pPath ?? nullptr);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
    } finally {
      if (pPath != null) {
        calloc.free(pPath);
      }
    }
  }

  /// Gets the name of the document.
  ///
  /// param language The expected language.
  /// return The name of the document.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  String getDocumentName(String? language) {
    Pointer<Char>? pLanguage = language?.toNativeUtf8().cast<Char>();
    Pointer<Char> pDocumentName = nullptr;
    Pointer<Int> pDocumentNameSize = calloc.allocate(1);
    pDocumentNameSize[0] = -1;
    try {
      var err = biosealSDK.id3BiosealManifest_GetDocumentName(_pHandle.value, pLanguage ?? nullptr, pDocumentName, pDocumentNameSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pDocumentName = calloc.allocate(pDocumentNameSize.value);
        err = biosealSDK.id3BiosealManifest_GetDocumentName(_pHandle.value, pLanguage ?? nullptr, pDocumentName, pDocumentNameSize);
        if (err != BiosealError.success.value) {
          throw BiosealException(err);
        }
      }
      final vDocumentName = utf8.decode(Uint8List.fromList(pDocumentName.cast<Uint8>().asTypedList(pDocumentNameSize.value)));
      return vDocumentName;
    } finally {
      if (pLanguage != null) {
        calloc.free(pLanguage);
      }
      calloc.free(pDocumentName);
      calloc.free(pDocumentNameSize);
    }
  }

  /// Gets the description of the document.
  ///
  /// param language The expected language.
  /// return The description of the document.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  String getDocumentDescription(String? language) {
    Pointer<Char>? pLanguage = language?.toNativeUtf8().cast<Char>();
    Pointer<Char> pDocumentDescription = nullptr;
    Pointer<Int> pDocumentDescriptionSize = calloc.allocate(1);
    pDocumentDescriptionSize[0] = -1;
    try {
      var err = biosealSDK.id3BiosealManifest_GetDocumentDescription(_pHandle.value, pLanguage ?? nullptr, pDocumentDescription, pDocumentDescriptionSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pDocumentDescription = calloc.allocate(pDocumentDescriptionSize.value);
        err = biosealSDK.id3BiosealManifest_GetDocumentDescription(_pHandle.value, pLanguage ?? nullptr, pDocumentDescription, pDocumentDescriptionSize);
        if (err != BiosealError.success.value) {
          throw BiosealException(err);
        }
      }
      final vDocumentDescription = utf8.decode(Uint8List.fromList(pDocumentDescription.cast<Uint8>().asTypedList(pDocumentDescriptionSize.value)));
      return vDocumentDescription;
    } finally {
      if (pLanguage != null) {
        calloc.free(pLanguage);
      }
      calloc.free(pDocumentDescription);
      calloc.free(pDocumentDescriptionSize);
    }
  }

}

