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

import '../id3_bioseal.dart';

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

/// Provides functionality to decode barcodes from raw image data.
class BarcodeReader implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3BiosealBarcodeReader>> _pHandle;
  bool _disposable = true;

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

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

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

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


  ///
  /// Holds the decoded data extracted from the barcode.
  ///
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  Uint8List get outputData => getOutputData();

  // Public methods
  /// Gets the holds the decoded data extracted from the barcode.
  ///
  /// return Holds the decoded data extracted from the barcode.
  /// 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.id3BiosealBarcodeReader_GetOutputData(_pHandle.value, pOutputData, pOutputDataSize);
      if (err == BiosealError.insufficientBuffer.value) {
        pOutputData = calloc.allocate(pOutputDataSize.value);
        err = biosealSDK.id3BiosealBarcodeReader_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);
    }
  }

  /// Attempts to decode a barcode from the provided raw image data.
  ///
  /// param data An array of bytes representing the raw image data that contains the barcode to be decoded.
  /// return A boolean output that indicates the success of the decoding process. It is set to 'true' if the barcode is successfully decoded, otherwise 'false'.
  /// throws BiosealException An error has occurred during Bioseal Library execution.
  bool decode(Uint8List? data) {
    Pointer<UnsignedChar>? pData;
    if (data != null) {
    	pData = calloc.allocate<UnsignedChar>(data.length);
    	pData.cast<Uint8>().asTypedList(data.length).setAll(0, data);
    }
    Pointer<Bool> pResult = calloc();
    try {
      var err = biosealSDK.id3BiosealBarcodeReader_Decode(_pHandle.value, pData ?? nullptr, data?.length ?? 0, pResult);
      if (err != BiosealError.success.value) {
        throw BiosealException(err);
      }
      final vResult = pResult.value;
      return vResult;
    } finally {
      if (pData != null) {
        calloc.free(pData);
      }
      calloc.free(pResult);
    }
  }

}

