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

final _finalizer = NativeFinalizer(faceSDK.addresses.id3TrackedFaceList_Dispose.cast());

/// Represents a list of tracked faces.
class TrackedFaceList extends Object with IterableMixin<TrackedFace> implements Finalizable {
  /// Native handle.
  late Pointer<Pointer<id3TrackedFaceList>> _pHandle;
  bool _disposable = true;

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

  /// Creates a new instance of the TrackedFaceList class.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  TrackedFaceList() {
    _pHandle = calloc();
    try {
      var err = faceSDK.id3TrackedFaceList_Initialize(_pHandle);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      _finalizer.attach(this, _pHandle.cast(), detach: this);
    } finally {}
  }

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

  /// Releases all resources used by this TrackedFaceList.
  void dispose() {
    if (_disposable) {
      faceSDK.id3TrackedFaceList_Dispose(_pHandle);
      calloc.free(_pHandle);
      _finalizer.detach(this);
    }
  }

  // Copyable methods

  /// Creates a copy of the TrackedFaceList object.
  ///
  /// return The newly created TrackedFaceList object.
  /// throws FaceException An error has occurred during Face Library execution.
  TrackedFaceList clone() {
    TrackedFaceList clone = TrackedFaceList();
    var err = faceSDK.id3TrackedFaceList_CopyTo(_pHandle.value, clone.handle);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
    return clone;
  }

  /// Gets or sets the element at the specified index.
  operator [](int key) => get(key);
  operator []=(int key, TrackedFace item) => set(key, item);

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

  @override
  Iterator<TrackedFace> get iterator => TrackedFaceListIterator(this);

  // Public methods
  /// Clears the TrackedFaceList object.
  ///
  /// throws FaceException An error has occurred during Face Library execution.
  void clear() {
    var err = faceSDK.id3TrackedFaceList_Clear(_pHandle.value);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// Adds an item to the TrackedFaceList object.
  ///
  /// param trackedFaceItem TrackedFace item to add.
  /// throws FaceException An error has occurred during Face Library execution.
  void add(TrackedFace trackedFaceItem) {
    var err = faceSDK.id3TrackedFaceList_Add(_pHandle.value, trackedFaceItem.handle);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// Gets an item of the TrackedFaceList object.
  ///
  /// param index Index of the TrackedFace item to get.
  /// return TrackedFace item to get.
  /// throws FaceException An error has occurred during Face Library execution.
  TrackedFace get(int index) {
    TrackedFace trackedFaceItem = TrackedFace();
    var err = faceSDK.id3TrackedFaceList_Get(_pHandle.value, index, trackedFaceItem.handle);
    if (err != FaceError.success.value) {
      trackedFaceItem.dispose();
      throw FaceException(err);
    }
    return trackedFaceItem;
  }

  /// Sets an item of the TrackedFaceList object.
  ///
  /// param index Index of the TrackedFace item to set.
  /// param trackedFaceItem TrackedFace item to set.
  /// throws FaceException An error has occurred during Face Library execution.
  void set(int index, TrackedFace trackedFaceItem) {
    var err = faceSDK.id3TrackedFaceList_Set(_pHandle.value, index, trackedFaceItem.handle);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// Gets the number of elements in the TrackedFaceList object.
  ///
  /// return Number of elements in the TrackedFace object.
  /// throws FaceException An error has occurred during Face Library execution.
  int getCount() {
    Pointer<Int> pCount = calloc();
    try {
      var err = faceSDK.id3TrackedFaceList_GetCount(_pHandle.value, pCount);
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vCount = pCount.value;
      return vCount;
    } finally {
      calloc.free(pCount);
    }
  }

  /// Removes an element of the TrackedFaceList object.
  ///
  /// param index Index of the TrackedFace item to remove.
  /// throws FaceException An error has occurred during Face Library execution.
  void removeAt(int index) {
    var err = faceSDK.id3TrackedFaceList_RemoveAt(_pHandle.value, index);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// TrackedFaceList object.
  ///
  /// param newSize TrackedFace object.
  /// throws FaceException An error has occurred during Face Library execution.
  void resize(int newSize) {
    var err = faceSDK.id3TrackedFaceList_Resize(_pHandle.value, newSize);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

  /// Finds a tracked face by ID.
  ///
  /// param id ID of the tracked face to find in the list.
  /// return The found tracked face.
  /// throws FaceException An error has occurred during Face Library execution.
  TrackedFace findTrackedFace(int id) {
    TrackedFace trackedFace = TrackedFace();
    var err = faceSDK.id3TrackedFaceList_FindTrackedFace(_pHandle.value, id, trackedFace.handle);
    if (err != FaceError.success.value) {
      trackedFace.dispose();
      throw FaceException(err);
    }
    return trackedFace;
  }

  /// Gets the list of IDs in the list.
  ///
  /// return The list of IDs in the list.
  /// throws FaceException An error has occurred during Face Library execution.
  List<int> getIdList() {
    Pointer<Int> pIds = nullptr;
    Pointer<Int> pIdsSize = calloc();
    pIdsSize[0] = -1;
    try {
      var err = faceSDK.id3TrackedFaceList_GetIdList(_pHandle.value, pIds, pIdsSize);
      if (err == FaceError.insufficientBuffer.value) {
        pIds = calloc.allocate(pIdsSize.value);
        err = faceSDK.id3TrackedFaceList_GetIdList(_pHandle.value, pIds, pIdsSize);
      }
      if (err != FaceError.success.value) {
        throw FaceException(err);
      }
      final vIds = Int32List.fromList(pIds.cast<Int32>().asTypedList(pIdsSize.value));
      return vIds;
    } finally {
      calloc.free(pIds);
      calloc.free(pIdsSize);
    }
  }

  /// Gets the largest face in the list.
  ///
  /// return The largest tracked face in the list.
  /// throws FaceException An error has occurred during Face Library execution.
  TrackedFace getLargestFace() {
    TrackedFace largestFace = TrackedFace();
    var err = faceSDK.id3TrackedFaceList_GetLargestFace(_pHandle.value, largestFace.handle);
    if (err != FaceError.success.value) {
      largestFace.dispose();
      throw FaceException(err);
    }
    return largestFace;
  }

  /// Rescales all tracked faces in the list.
  ///
  /// param scale Scale factor to apply. Range is (0;+inf(.
  /// throws FaceException An error has occurred during Face Library execution.
  void rescaleAll(double scale) {
    var err = faceSDK.id3TrackedFaceList_RescaleAll(_pHandle.value, scale);
    if (err != FaceError.success.value) {
      throw FaceException(err);
    }
  }

}

class TrackedFaceListIterator implements Iterator<TrackedFace> {
  TrackedFaceListIterator(this._list) : _count = _list.getCount();
  final TrackedFaceList _list;
  final int _count;
  int _index = -1;

  @override
  TrackedFace get current => _list.get(_index);

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