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

/// Represents the arguments to the decryption callback.
class DecryptionArgs extends Object with IterableMixin<String> implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3BiosealDecryptionArgs>> _pHandle;
  bool _disposable = true;

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

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

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

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

  /// Gets or sets the element at the specified index.
  operator [](String key) => get(key);

  /// Gets the number of elements contained in the list or dictionary.
  int get count => getCount();

  @override
  Iterator<String> get iterator => DecryptionArgsIterator(this);

  ///
  /// The name of the field to decrypt.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  String get fieldName => getFieldName();

  ///
  /// The data to decrypt as a binary array.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Uint8List get inputData => getInputData();

  ///
  /// The decrypted data as a binary array.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Uint8List get outputData => getOutputData();
  set outputData(Uint8List value) => setOutputData(value);

  ///
  /// Gets a string list containing the keys in the dict.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  StringArray get keys => getKeys();

  // Public methods
  /// Gets an item of the DecryptionArgs object.
  ///
  /// param key Unique key of the  item to get.
  /// return  item to get.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  String get(String? key) {
    Pointer<Char>? pKey = key?.toNativeUtf8().cast<Char>();
    Pointer<Char> pItem = nullptr;
    Pointer<Int> pItemSize = calloc.allocate(1);
    pItemSize[0] = -1;
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_Get(_pHandle.value, pKey ?? nullptr, pItem, pItemSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pItem = calloc.allocate(pItemSize.value);
        err = biosealSDK.id3BiosealDecryptionArgs_Get(_pHandle.value, pKey ?? nullptr, pItem, pItemSize);
        if (err != BiosealError.success.value) {
          throw BiosealException(err);
        }
      }
      final vItem = utf8.decode(Uint8List.fromList(pItem.cast<Uint8>().asTypedList(pItemSize.value)));
      return vItem;
    } finally {
      if (pKey != null) {
        calloc.free(pKey);
      }
      calloc.free(pItem);
      calloc.free(pItemSize);
    }
  }

  /// Gets the number of elements in the DecryptionArgs object.
  ///
  /// return Number of elements in the  object.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  int getCount() {
    Pointer<Int> pCount = calloc();
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_GetCount(_pHandle.value, pCount);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vCount = pCount.value;
      return vCount;
    } finally {
      calloc.free(pCount);
    }
  }

  /// Determines whether the DecryptionArgsobject contains the specified key.
  ///
  /// param key The key to locate in the  object.
  /// return true if the  object contains an element with the specified key, otherwise false.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  bool containsKey(String? key) {
    Pointer<Char>? pKey = key?.toNativeUtf8().cast<Char>();
    Pointer<Bool> pResult = calloc();
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_ContainsKey(_pHandle.value, pKey ?? nullptr, pResult);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vResult = pResult.value;
      return vResult;
    } finally {
      if (pKey != null) {
        calloc.free(pKey);
      }
      calloc.free(pResult);
    }
  }

  /// Gets the the name of the field to decrypt.
  ///
  /// return The name of the field to decrypt.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  String getFieldName() {
    Pointer<Char> pFieldName = nullptr;
    Pointer<Int> pFieldNameSize = calloc.allocate(1);
    pFieldNameSize[0] = -1;
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_GetFieldName(_pHandle.value, pFieldName, pFieldNameSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pFieldName = calloc.allocate(pFieldNameSize.value);
        err = biosealSDK.id3BiosealDecryptionArgs_GetFieldName(_pHandle.value, pFieldName, pFieldNameSize);
        if (err != BiosealError.success.value) {
          throw BiosealException(err);
        }
      }
      final vFieldName = utf8.decode(Uint8List.fromList(pFieldName.cast<Uint8>().asTypedList(pFieldNameSize.value)));
      return vFieldName;
    } finally {
      calloc.free(pFieldName);
      calloc.free(pFieldNameSize);
    }
  }

  /// Gets the the data to decrypt as a binary array.
  ///
  /// return The data to decrypt as a binary array.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Uint8List getInputData() {
    Pointer<UnsignedChar> pInputData = nullptr;
    Pointer<Int> pInputDataSize = calloc();
    pInputDataSize[0] = -1;
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_GetInputData(_pHandle.value, pInputData, pInputDataSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pInputData = calloc.allocate(pInputDataSize.value);
        err = biosealSDK.id3BiosealDecryptionArgs_GetInputData(_pHandle.value, pInputData, pInputDataSize);
      }
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vInputData = Uint8List.fromList(pInputData.cast<Uint8>().asTypedList(pInputDataSize.value));
      return vInputData;
    } finally {
      calloc.free(pInputData);
      calloc.free(pInputDataSize);
    }
  }

  /// Gets the the decrypted data as a binary array.
  ///
  /// return The decrypted data as a binary array.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Uint8List getOutputData() {
    Pointer<UnsignedChar> pOutputData = nullptr;
    Pointer<Int> pOutputDataSize = calloc();
    pOutputDataSize[0] = -1;
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_GetOutputData(_pHandle.value, pOutputData, pOutputDataSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pOutputData = calloc.allocate(pOutputDataSize.value);
        err = biosealSDK.id3BiosealDecryptionArgs_GetOutputData(_pHandle.value, pOutputData, pOutputDataSize);
      }
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vOutputData = Uint8List.fromList(pOutputData.cast<Uint8>().asTypedList(pOutputDataSize.value));
      return vOutputData;
    } finally {
      calloc.free(pOutputData);
      calloc.free(pOutputDataSize);
    }
  }

  /// Sets the the decrypted data as a binary array.
  ///
  /// param outputData The decrypted data as a binary array.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  void setOutputData(Uint8List? outputData) {
    Pointer<UnsignedChar>? pOutputData;
    if (outputData != null) {
    	pOutputData = calloc.allocate<UnsignedChar>(outputData.length);
    	pOutputData.cast<Uint8>().asTypedList(outputData.length).setAll(0, outputData);
    }
    try {
      var err = biosealSDK.id3BiosealDecryptionArgs_SetOutputData(_pHandle.value, pOutputData ?? nullptr, outputData?.length ?? 0);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
    } finally {
      if (pOutputData != null) {
        calloc.free(pOutputData);
      }
    }
  }

  /// Gets the gets a string list containing the keys in the dict.
  ///
  /// return Gets a string list containing the keys in the dict.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  StringArray getKeys() {
    StringArray keys = StringArray();
    var err = biosealSDK.id3BiosealDecryptionArgs_GetKeys(_pHandle.value, keys.handle);
    if (err != BiosealError.success.value) {
      keys.dispose();
      throw BiosealException(err);
    }
    return keys;
  }

}

class DecryptionArgsIterator implements Iterator<String> {
  DecryptionArgsIterator(this._list)
      : _count = _list.getCount(),
        _keys = [] {
    final StringArray keys = _list.getKeys();
    for (var key in keys) {
      _keys.add(key);
    }
    keys.dispose();
  }
  final DecryptionArgs _list;
  final int _count;
  int _index = -1;
  final List<String> _keys;

  @override
  String get current => _list.get(_keys[_index]);

  @override
  bool moveNext() {
    _index++;
    return _index < _count;
  }
}
