/**
 * @file CosmicDriver.hpp
 *
 * @brief Header file that declares the methods to manage the Cosmic Driver.
 */

#pragma once

#ifdef __wasm__
#include "hidapi_wasm.h"
#else
#include <hidapi.h>
#endif

#include <functional>
#include <iostream>
#include <memory>
#include <string>
#ifndef __wasm__
#include <thread>
#endif
#include <utility>
#include <vector>

class CosmicDriver {
public:
  /**
   * Type alias for the response callback function.
   */
  using ResponseCallback = std::function<void(const std::vector<uint8_t>&)>;

  /**
   * Constructs a new Cosmic Driver object
   */
  CosmicDriver();

  /**
   * Destructor: cleans up the Cosmic Driver and its components
   */
  ~CosmicDriver();

  /// Deleted copy constructor and assignment operator to prevent copying
  CosmicDriver(const CosmicDriver&)            = delete;
  CosmicDriver& operator=(const CosmicDriver&) = delete;

  /**
   * Opens a connection to the device using the specified path or serial number.
   * @param devicePath The path to the device. If empty, connects using serial number or
   * to the first available device.
   * @param serialNumber The serial number of the device to connect to. If empty, connects
   * using devicePath or to the first available device.
   * @return true if the connection was successful, false otherwise.
   */
  bool open(const std::string& devicePath = "", const std::string& serialNumber = "");

  /**
   * Disconnects from the device.
   */
  void close();

  /**
   * Sends a command to the device.
   * @param command The request to be sent to the device.
   * @return true if the command was sent successfully, false otherwise.
   */
  bool sendCommand(const std::vector<uint8_t>& command);

  /**
   * Sets the callback function to be called when a response is received.
   * @param callback The callback function to be invoked with the response data.
   */
  void onResponse(ResponseCallback callback);

  /**
   * Gets the endpoint size of the connected device.
   * @return The endpoint size.
   */
  size_t getEndpointSize() const;

#ifdef __wasm__
  /**
   * Polls the device for a single response (WASM only).
   * Called from the BlockingApi polling loop instead of a background thread.
   * Calls hid_read_timeout(100ms) once and invokes the response callback
   * if data is received.
   */
  void pollOnce();

  /**
   * Sets the endpoint size externally (WASM only).
   * In native builds, endpoint size is determined by device enumeration.
   * In WASM, the JS host knows the device type and passes the size in.
   * @param size The endpoint size in bytes (64 for Supernova, 1024 for Pulsar)
   */
  void setEndpointSize(uint16_t size);
#endif

  /// Device characteristics
  struct BinhoDeviceCharacteristics {
    uint16_t vid;
    uint16_t pid;
    size_t endpointSize;
  };

  /// Device information for connected devices
  struct BinhoDevice {
    const BinhoDeviceCharacteristics* info;
    std::string path;
  };

private:
  /// The device information for the connected device
  BinhoDevice connectedDevice;

  /// The HID device handle
  hid_device* hidDeviceHandle;

  /// The HID device information
  ResponseCallback responseCallback;

  /// Flag to indicate if the device is open
  bool isOpen{false};

  /// Flag to indicate if the device is polling for responses
  bool isPolling{false};

#ifndef __wasm__
  /// The polling thread for receiving responses
  std::thread pollingThread;
#endif

  /// The list of Binho devices
  static std::vector<BinhoDeviceCharacteristics> binhoDeviceList;

  /**
   * Searches for a Binho device with the specified VID and PID.
   *
   * @param vid The vendor ID of the device
   * @param pid The product ID of the device
   * @return A pointer to the BinhoDeviceCharacteristics structure if found, nullptr otherwise
   */
  static const BinhoDeviceCharacteristics* findBinhoDeviceCharacteristics(uint16_t vid,
                                                                          uint16_t pid);

  /**
   * Polling loop to receive responses from the device.
   */
  void pollingLoop();
};
