#ifndef REVO3_SDK_H
#define REVO3_SDK_H

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>

constexpr static const uint8_t DEFAULT_MASTER_ID = 1;

/// Maximum touch points buffer capacity allocated per module (matching the 57-point Thumb Pad).
constexpr static const uintptr_t REVO3_TOUCH_MAX_POINTS = 57;

/// Baudrate for RS485 (1M, 2M, 3M, 5M)
enum Baudrate : uint8_t {
  BAUDRATE_BAUD1_MBPS = 1,
  BAUDRATE_BAUD2_MBPS = 2,
  BAUDRATE_BAUD3_MBPS = 3,
  BAUDRATE_BAUD5_MBPS = 5,
};

/// Baudrate for CANFD (1M, 2M, 4M, 5M)
enum BaudrateCAN : uint8_t {
  BAUDRATE_CAN_BAUD1_MBPS = 1,
  BAUDRATE_CAN_BAUD2_MBPS = 2,
  BAUDRATE_CAN_BAUD4_MBPS = 4,
  BAUDRATE_CAN_BAUD5_MBPS = 5,
};

enum DfuState : uint8_t {
  DFU_STATE_IDLE = 0,
  DFU_STATE_STARTING = 1,
  DFU_STATE_STARTED = 2,
  DFU_STATE_TRANSFER = 3,
  DFU_STATE_COMPLETED = 4,
  DFU_STATE_ABORTED = 5,
};

enum EtherCATFoeType : uint8_t {
  ETHER_CAT_FOE_TYPE_WRIST = 1,
  ETHER_CAT_FOE_TYPE_CONTROL = 2,
};

/// Revo3 hand side.
enum HandType : uint8_t {
  HAND_TYPE_LEFT = 0,
  HAND_TYPE_RIGHT = 1,
};

enum LogLevel : uint8_t {
  LOG_LEVEL_ERROR = 0,
  LOG_LEVEL_WARN = 1,
  LOG_LEVEL_INFO = 2,
  LOG_LEVEL_DEBUG = 3,
  LOG_LEVEL_TRACE = 4,
};

/// Revo3 hand side and reserved size marker.
enum SkuType : uint8_t {
  SKU_TYPE_MEDIUM_RIGHT = 1,
  SKU_TYPE_MEDIUM_LEFT = 2,
  SKU_TYPE_SMALL_RIGHT = 3,
  SKU_TYPE_SMALL_LEFT = 4,
};

/// Revo3 hardware variants only.
enum StarkHardwareType : uint8_t {
  STARK_HARDWARE_TYPE_REVO3_ULTRA = 20,
  STARK_HARDWARE_TYPE_REVO3_ULTRA_TOUCH = 21,
  STARK_HARDWARE_TYPE_REVO3_ULTRA_VISION_TOUCH = 22,
  STARK_HARDWARE_TYPE_REVO3_PRO = 23,
  STARK_HARDWARE_TYPE_REVO3_PRO_TOUCH = 24,
  STARK_HARDWARE_TYPE_REVO3_BASIC = 26,
  STARK_HARDWARE_TYPE_REVO3_BASIC_TOUCH = 27,
};

enum StarkProtocolType : uint8_t {
  STARK_PROTOCOL_TYPE_AUTO = 0,
  STARK_PROTOCOL_TYPE_MODBUS = 1,
  STARK_PROTOCOL_TYPE_CAN_FD = 3,
  STARK_PROTOCOL_TYPE_ETHER_CAT = 4,
};

struct CDataCollector;

struct CRevo3MotorStatusBuffer;

struct CRevo3TouchDataBuffer;

/// Opaque device handle for the C API.
struct DeviceHandler;

/// Streaming auto-detect handle (C interface)
struct Revo3AutoDetectHandle;

struct CRevo3MotorStatusData {
  uint16_t statuses[21];
  float positions[21];
  float velocities[21];
  float currents[21];
  uint16_t errors[21];
};

struct CRevo3TouchData {
  uint16_t summary[42];
  uint16_t modules[11][REVO3_TOUCH_MAX_POINTS];
  uint16_t module_counts[11];
};

struct CDeviceConfig {
  StarkProtocolType protocol;
  const char *port_name;
  uint32_t baudrate;
  uint8_t slave_id;
};

/// ZQWL device info struct (C interface)
struct CZqwlDeviceInfo {
  /// Device type (0x0100=dual-channel CANFD, 0x0101=single-channel CANFD, 0x0102=dual-channel CAN, etc.)
  uint16_t device_type;
  /// Serial port name
  char *port_name;
  /// USB VID
  uint16_t vid;
  /// USB PID
  uint16_t pid;
  /// Whether CANFD is supported
  bool supports_canfd;
  /// Number of channels
  uint8_t channel_count;
};

/// ZQWL device list struct (C interface)
struct ZqwlDeviceList {
  /// Device array pointer
  CZqwlDeviceInfo *devices;
  /// Device count
  uintptr_t count;
};

/// SocketCAN device info struct (C interface)
struct CSocketCanDeviceInfo {
  /// Interface name (e.g. "can0")
  char *iface;
  /// Whether CANFD is supported
  bool supports_canfd;
  /// Whether interface is up
  bool is_up;
};

/// SocketCAN device list struct (C interface)
struct SocketCanDeviceList {
  /// Device array pointer
  CSocketCanDeviceInfo *devices;
  /// Device count
  uintptr_t count;
};

/// Detected device information (C interface)
struct CDetectedDevice {
  /// Protocol type
  StarkProtocolType protocol;
  /// Port name (serial port or CAN adapter port)
  char *port_name;
  /// Slave ID on the bus
  uint8_t slave_id;
  /// Baudrate (for Modbus/serial protocols, or CAN arbitration baudrate)
  uint32_t baudrate;
  /// Data baudrate (for CANFD only, 0 for other protocols)
  uint32_t data_baudrate;
  /// Hardware type
  StarkHardwareType hardware_type;
  /// SKU type
  SkuType sku_type;
  /// Serial number (NULL if not detected)
  char *serial_number;
  /// Firmware version (NULL if not detected)
  char *firmware_version;
  /// Hardware version (NULL if not detected)
  char *hardware_version;
};

/// Detected device list (C interface)
struct CDetectedDeviceList {
  /// Device array pointer
  CDetectedDevice *devices;
  /// Device count
  uintptr_t count;
};

/// Streaming auto-detect callback.
///
/// Return true to continue scanning or false to stop scanning.
/// The device pointer is only valid during the callback invocation.
using Revo3AutoDetectCallback = bool(*)(const CDetectedDevice *device, void *user_data);

/// Modbus async read/write result callback.
/// This callback processes results of asynchronous Modbus operations.
/// Return value: 0 on success, non‑zero on failure.
using ModbusOperationResultCallback = void(*)(uint8_t*, int, int, void*);

/// Modbus async read/write callback type.
/// Return value: 0 on success, non‑zero on failure.
using ModbusOperationCallback = int32_t(*)(const uint8_t *values,
                                           int len,
                                           ModbusOperationResultCallback callback,
                                           void *user_data);

/// Custom Modbus RX callback.
/// Return value: 0 on success, non‑zero on failure.
using ModbusRxCallback = int32_t(*)(uint8_t slave_id,
                                    uint16_t register_address,
                                    uint16_t *data_out,
                                    uint16_t count);

/// Custom Modbus TX callback.
/// Return value: 0 on success, non‑zero on failure.
using ModbusTxCallback = int32_t(*)(uint8_t slave_id,
                                    uint16_t register_address,
                                    const uint16_t *data,
                                    uint16_t count);

/// CANFD RX callback.
///
/// # Parameters
/// - `slave_id`: Slave ID
/// - `expected_can_id`: Expected CANFD ID; matches by slave_id and master_id
/// - `expected_frames`: `0` = auto-detect multi-frame, `> 0` = specific frame count
/// - `can_id_out`: Output CAN ID
/// - `data_out`: Output data buffer (at least 512 bytes for multi-frame)
/// - `data_len_out`: Output data length
///
/// # Return value
/// 0 on success, non‑zero on failure.
using CanRxCallback = int32_t(*)(uint8_t slave_id,
                                 uint32_t expected_can_id,
                                 uint8_t expected_frames,
                                 uint32_t *can_id_out,
                                 uint8_t *data_out,
                                 uintptr_t *data_len_out);

/// CANFD TX callback.
/// Return value: 0 on success, non‑zero on failure.
using CanTxCallback = int32_t(*)(uint8_t slave_id,
                                 uint32_t can_id,
                                 const uint8_t *data,
                                 uintptr_t data_len);

/// DFU state callback, `state` corresponds to `DfuState`.
using DfuStateCallback = void(*)(uint8_t slave_id, uint8_t state);

/// DFU progress callback.
using DfuProgressCallback = void(*)(uint8_t slave_id, float progress);

struct CDeviceInfo {
  SkuType sku_type;
  HandType hand_type;
  StarkHardwareType hardware_type;
  const char *serial_number;
  const char *firmware_version;
  const char *hardware_version;
};

/// Revo3 system status data (C-compatible).
struct CRevo3SystemStatus {
  uint8_t system_state;
  uint8_t error_code;
  uint16_t current_ma;
  uint16_t voltage_v;
  uint16_t power_w;
  uint16_t temperature_c;
};

extern "C" {

CRevo3MotorStatusBuffer *revo3_motor_buffer_new(uintptr_t max_size);

void revo3_motor_buffer_free(CRevo3MotorStatusBuffer *buffer);

int revo3_motor_buffer_pop_all(CRevo3MotorStatusBuffer *buffer,
                               CRevo3MotorStatusData *out_data,
                               uintptr_t max_count);

uintptr_t revo3_motor_buffer_len(const CRevo3MotorStatusBuffer *buffer);

void revo3_motor_buffer_clear(CRevo3MotorStatusBuffer *buffer);

int revo3_get_all_touch_data(DeviceHandler *handle,
                             unsigned char slave_id,
                             CRevo3TouchData *out_data);

CRevo3TouchDataBuffer *revo3_touch_buffer_new(uintptr_t max_size);

void revo3_touch_buffer_free(CRevo3TouchDataBuffer *buffer);

int revo3_touch_buffer_pop_all(CRevo3TouchDataBuffer *buffer,
                               CRevo3TouchData *out_data,
                               uintptr_t max_count);

uintptr_t revo3_touch_buffer_len(const CRevo3TouchDataBuffer *buffer);

void revo3_touch_buffer_clear(CRevo3TouchDataBuffer *buffer);

CDataCollector *data_collector_new_revo3_basic(DeviceHandler *handle,
                                               CRevo3MotorStatusBuffer *motor_buffer,
                                               unsigned char slave_id,
                                               uint32_t motor_frequency,
                                               int enable_stats);

CDataCollector *data_collector_new_revo3_full(DeviceHandler *handle,
                                              CRevo3MotorStatusBuffer *motor_buffer,
                                              CRevo3TouchDataBuffer *touch_buffer,
                                              unsigned char slave_id,
                                              uint32_t motor_frequency,
                                              uint32_t touch_frequency,
                                              int enable_stats);

int data_collector_start(CDataCollector *collector);

int data_collector_stop(CDataCollector *collector);

int data_collector_wait(CDataCollector *collector);

int data_collector_is_running(const CDataCollector *collector);

void data_collector_free(CDataCollector *collector);

/// Initialize SDK logging.
/// log_level: Log level (Debug, Info, Warn, Error). Default is Info.
void init_logging(LogLevel log_level);

/// List all available Stark serial ports.
void list_available_ports();

/// Automatically detect a Revo3 device on a serial port.
/// port: Serial port name; when NULL, the function will auto‑detect ports.
/// quick: Whether to use quick detection. Default is true, which only checks
///        a subset of baudrates and slave IDs.
/// Returns a pointer to `DeviceConfig` (protocol, port, baudrate, slave ID);
/// call `free_device_config` to free the memory.
/// On failure, returns NULL.
/// Note: When `quick` is false, this function scans slave IDs in [1, 247],
///       which may take a long time.
///
/// @deprecated Use `stark_auto_detect` + `init_from_detected` instead for better
///             multi-protocol support (Modbus, CANFD).
CDeviceConfig *auto_detect_device(const char *port, bool quick);

/// Automatically detect a Revo3 hand over Modbus.
/// port: Serial port name; when NULL, the function will auto‑detect ports.
/// quick: Whether to use quick detection.
/// Revo3 uses 5Mbps baudrate and slave_id 1.
/// Returns a pointer to `DeviceConfig` (protocol, port, baudrate, slave ID);
/// call `free_device_config` to free the memory.
/// On failure, returns NULL.
CDeviceConfig *auto_detect_modbus_revo3(const char *port, bool quick);

/// Open a serial port.
/// port: Serial port name, for example "/dev/ttyUSB0" or "COM1".
/// baudrate: Revo3 RS485 baud rate. 5 Mbps is the default factory setting.
/// Returns a pointer to `DeviceHandler`; call `modbus_close` to free it.
/// On failure, returns NULL.
///
/// NOTE: After successfully opening the connection, it is highly recommended to call
/// `revo3_get_device_info` (or `stark_get_device_info`) to retrieve device details
/// and automatically synchronize the actual joint position limits from the physical device.
/// Otherwise, the context will use the default hardcoded joint limits.
DeviceHandler *modbus_open(const char *port, uint32_t baudrate);

/// Close serial port
void modbus_close(DeviceHandler *handle);

/// @brief  Create a device handler with specified protocol and optional master ID.
/// @param protocol_type Protocol type (Modbus, CanFd, EtherCAT)
/// @param master_id Master ID (required for CanFd and EtherCAT, 0 for others)
/// @return Pointer to the newly created `DeviceHandler`. Call
///         `close_device_handler` to release it.
///
/// @note To fully initialize the handler, retrieve device details, and synchronize
///       the actual joint position limits from the physical device, you should call
///       `revo3_get_device_info` (or `stark_get_device_info`) after connecting.
DeviceHandler *init_device_handler(StarkProtocolType protocol_type, uint8_t master_id);

/// @brief  Create a device handler for CANFD protocol.
///
/// This function creates a device handler and stores the CAN baudrate information.
///
/// @param protocol_type Protocol type (STARK_PROTOCOL_TYPE_CAN_FD)
/// @param master_id Master ID (typically 1)
/// @param arb_baudrate Arbitration baudrate in bps (e.g., 1000000 for 1 Mbps)
/// @param data_baudrate Data baudrate in bps
/// @return Pointer to the newly created `DeviceHandler`. Call
///         `close_device_handler` to release it.
DeviceHandler *init_device_handler_can(StarkProtocolType protocol_type,
                                       uint8_t master_id,
                                       uint32_t arb_baudrate,
                                       uint32_t data_baudrate);

/// @brief  Create a device handler for CANFD protocol with hardware type pre-set.
///
/// @param protocol_type Protocol type (STARK_PROTOCOL_TYPE_CAN_FD)
/// @param master_id Master ID (typically 1)
/// @param slave_id Slave ID to set hardware type for
/// @param arb_baudrate Arbitration baudrate in bps (e.g., 1000000 for 1 Mbps)
/// @param data_baudrate Data baudrate in bps
/// @param hw_type Hardware type (StarkHardwareType enum value)
/// @return Pointer to the newly created `DeviceHandler`. Call
///         `close_device_handler` to release it.
DeviceHandler *init_device_handler_can_with_hw_type(StarkProtocolType protocol_type,
                                                    uint8_t master_id,
                                                    uint8_t slave_id,
                                                    uint32_t arb_baudrate,
                                                    uint32_t data_baudrate,
                                                    StarkHardwareType hw_type);

/// @brief  Create a device handler with hardware type pre-set.
///
/// This is useful when you already know the hardware type (e.g., from auto_detect)
/// and want to avoid an extra get_device_info call.
///
/// @param protocol_type Protocol type (Modbus, CanFd, EtherCAT)
/// @param master_id Master ID (required for CanFd and EtherCAT, 0 for others)
/// @param slave_id Slave ID to set hardware type for
/// @param hw_type Hardware type (StarkHardwareType enum value)
/// @return Pointer to the newly created `DeviceHandler`. Call
///         `close_device_handler` to release it.
DeviceHandler *init_device_handler_with_hw_type(StarkProtocolType protocol_type,
                                                uint8_t master_id,
                                                uint8_t slave_id,
                                                StarkHardwareType hw_type);

/// List all ZQWL USB CANFD-capable devices
///
/// Returns device list pointer. Call `free_zqwl_device_list` to free memory after use.
/// Returns list with count=0 if no devices found.
///
/// @note This is a low-level API. Recommend using high-level API `stark_auto_detect` + `init_from_detected`
///       for automatic device detection and initialization. Low-level API is for advanced users
///       who need manual control of CAN adapter.
ZqwlDeviceList *list_zqwl_devices();

/// Free ZQWL device list memory
void free_zqwl_device_list(ZqwlDeviceList *list);

/// Initialize ZQWL CANFD device
///
/// Revo3 default arbitration baudrate is 1 Mbps; data baudrate is 5 Mbps.
///
/// @param port_name Serial port name
/// @param arb_baudrate Arbitration baudrate (bps), typically 1000000
/// @param data_baudrate Data baudrate (bps), typically 5000000
/// @return 0: success, -1: failure
///
/// @note This is a low-level API. Recommend using high-level API `stark_auto_detect` + `init_from_detected`
///       for automatic device detection and initialization. Low-level API is for advanced users
///       who need manual control of CAN adapter.
int init_zqwl_canfd(const char *port_name,
                    uint32_t arb_baudrate,
                    uint32_t data_baudrate);

/// Close ZQWL CANFD device
///
/// @note This is a low-level API. If device was initialized with `init_from_detected`,
///       use `close_device_handler` instead, which automatically handles ZQWL resource cleanup.
void close_zqwl();

/// List all SocketCAN interfaces (Linux only)
///
/// Scans /sys/class/net/ for CAN interfaces.
/// Returns device list pointer. Call `free_socketcan_device_list` to free memory.
/// Returns list with count=0 on non-Linux platforms or if no interfaces found.
SocketCanDeviceList *list_socketcan_devices();

/// Free SocketCAN device list memory
void free_socketcan_device_list(SocketCanDeviceList *list);

/// Initialize SocketCAN CANFD device (Linux only)
///
/// @param iface Interface name (e.g. "can0"). If NULL, uses STARK_SOCKETCAN_IFACE env var or "can0"
/// @return 0: success, -1: failure (or non-Linux platform)
int init_socketcan_canfd(const char *iface);

/// Close SocketCAN device (Linux only)
void close_socketcan();

/// Check if a SocketCAN interface is available and up (Linux only)
///
/// @param iface Interface name (e.g. "can0")
/// @return true if interface exists and is up, false otherwise
bool is_socketcan_available(const char *iface);

/// Auto-detect Revo3 devices across supported protocols.
///
/// Scans for devices in priority order:
/// 1. Modbus/RS485 (5/1/2/3 Mbps; IDs 0x7E and 0x7F)
/// 2. CANFD adapters (BrainCo/ZQWL/SocketCAN)
///
/// When a specific protocol is requested, only that protocol is scanned.
///
/// # Parameters
/// - scan_all: If true, scan for all devices. If false, stop at first found.
/// - port: Optional port name to scan. If NULL, scans all available ports.
/// - protocol: Protocol filter:
///   - STARK_PROTOCOL_TYPE_AUTO: Auto-detect all protocols (recommended)
///   - STARK_PROTOCOL_TYPE_MODBUS: Modbus only
///   - STARK_PROTOCOL_TYPE_CAN_FD: CANFD only
/// - slave_id: Slave ID to probe. Use 0 to probe the default Revo3 IDs.
/// - modbus_baudrate: Modbus baudrate to probe. Use 0 to probe the default Revo3 baudrates.
/// - broadcast: If true, probe broadcast slave ID 0 first before default IDs (Modbus only) (recommended: true).
///
/// # Returns
/// Pointer to CDetectedDeviceList. Call `free_detected_device_list` to free.
/// Returns empty list (count=0) if no devices found.
CDetectedDeviceList *stark_auto_detect(bool scan_all,
                                       const char *port,
                                       StarkProtocolType protocol,
                                       uint8_t slave_id,
                                       uint32_t modbus_baudrate,
                                       bool broadcast);

/// Start streaming Revo3 auto-detect.
///
/// The callback is invoked immediately when each device is found. Return false
/// from the callback to stop scanning. The scan can also be stopped manually by
/// calling `revo3_auto_detect_stop`. Use `slave_id=0` to probe the default
/// Revo3 IDs. Use `modbus_baudrate=0` to probe the default Revo3 baudrates.
/// Use `broadcast=true` to probe broadcast ID 0 first (Modbus only) (recommended).
Revo3AutoDetectHandle *revo3_auto_detect_start(bool stop_on_first,
                                               const char *port,
                                               StarkProtocolType protocol,
                                               uint8_t slave_id,
                                               uint32_t modbus_baudrate,
                                               bool broadcast,
                                               Revo3AutoDetectCallback callback,
                                               void *user_data);

/// Request a streaming auto-detect scan to stop.
void revo3_auto_detect_stop(Revo3AutoDetectHandle *handle);

/// Wait for a streaming auto-detect scan to finish.
void revo3_auto_detect_join(Revo3AutoDetectHandle *handle);

/// Check whether a streaming auto-detect scan has finished.
bool revo3_auto_detect_is_finished(const Revo3AutoDetectHandle *handle);

/// Free a streaming auto-detect handle.
void revo3_auto_detect_free_handle(Revo3AutoDetectHandle *handle);

/// Free detected device list memory
void free_detected_device_list(CDetectedDeviceList *list);

/// Initialize device handler from detected device info.
///
/// This function initializes the appropriate transport (Modbus, CANFD)
/// based on the detected device's protocol and returns a ready-to-use handler.
///
/// # Parameters
/// - device: Pointer to CDetectedDevice from stark_auto_detect()
///
/// # Returns
/// - Pointer to DeviceHandler on success
/// - NULL on failure
///
/// # Notes
/// - For CANFD: Initializes BrainCo/ZQWL/SocketCAN adapter automatically
/// - For Modbus: Opens serial port with detected baudrate
/// - Call `close_from_detected` to cleanup
/// - Although this function sets the hardware type from the detected info,
///   the newly initialized context starts with default joint position limits.
///   To synchronize the actual joint position limits from the physical device,
///   you should call `revo3_get_device_info` (or `stark_get_device_info`).
DeviceHandler *init_from_detected(const CDetectedDevice *device);

/// Close device handler based on protocol type.
///
/// Convenience function that handles cleanup for any protocol type.
/// Automatically closes CANFD adapters when needed.
///
/// # Parameters
/// - handle: Device handler to close
/// - protocol: Protocol type (from CDetectedDevice.protocol)
void close_device_handler(DeviceHandler *handle, uint8_t protocol);

/// Configure SDO for an EtherCAT slave device.
void ethercat_setup_sdo(DeviceHandler *handle, uint16_t slave_pos);

void ethercat_reserve_master(DeviceHandler *handle);

/// Start EtherCAT cyclic loop with PDO communication (control & read).
/// dc_assign_activate: DC flags; 0x0000 means DC is not enabled.
/// sync0_cycle_time: SYNC0 cycle time in nanoseconds. The loop period matches
///                   SYNC0 cycle time.
/// sync0_shift_time: SYNC0 phase shift in nanoseconds.
/// sync1_cycle_time: SYNC1 cycle time in nanoseconds.
/// sync1_shift_time: SYNC1 phase shift in nanoseconds.
void ethercat_start_loop(DeviceHandler *handle,
                         const uint16_t *slave_positions,
                         int count,
                         uint16_t dc_assign_activate,
                         uint32_t sync0_cycle_time,
                         int32_t sync0_shift_time,
                         uint32_t sync1_cycle_time,
                         int32_t sync1_shift_time);

/// Stop the EtherCAT cyclic loop.
void ethercat_stop_loop(DeviceHandler *handle);

/// EtherCAT DFU: upgrade firmware via FoE protocol.
/// slave_pos: Slave position.
/// dfu_type: DFU type, Control or Wrist.
/// file_path: Firmware file path.
void ethercat_start_dfu(DeviceHandler *handle,
                        uint16_t slave_pos,
                        EtherCATFoeType dfu_type,
                        const char *file_path);

/// Configure whether to skip querying detailed device info during auto-detection.
/// When set to `true`, a fast probe is performed, directly returning a `minimal` device structure.
void revo3_set_skip_device_info_query(bool skip);

/// Set Modbus async read/write callback.
void set_modbus_operation_callback(ModbusOperationCallback cb);

/// Set Modbus read callback for input registers.
void set_modbus_read_input_callback(ModbusRxCallback cb);

/// Set Modbus read callback for holding registers.
void set_modbus_read_holding_callback(ModbusRxCallback cb);

/// Set Modbus write callback.
void set_modbus_write_callback(ModbusTxCallback cb);

/// Set CAN/CANFD RX callback.
void set_can_rx_callback(CanRxCallback cb);

/// Set CAN/CANFD TX callback.
void set_can_tx_callback(CanTxCallback cb);

/// Set DFU state callback.
void set_dfu_state_callback(DfuStateCallback cb);

/// Set DFU progress callback.
void set_dfu_progress_callback(DfuProgressCallback cb);

/// Get device information.
/// Returns a pointer to `DeviceInfo`; you must call `free_device_info` to free
/// it. Returns NULL on failure.
///
/// NOTE: Hardware type is determined by Revo3 SN prefix. If the SN is not
/// recognized, hardware_type defaults to Revo3Ultra.
CDeviceInfo *stark_get_device_info(DeviceHandler *handle, uint8_t slave_id);

/// Revo3-prefixed alias for `stark_get_device_info`.
CDeviceInfo *revo3_get_device_info(DeviceHandler *handle, uint8_t slave_id);

/// Free device info returned by `stark_get_device_info`.
void free_device_info(CDeviceInfo *info);

/// Get Revo3 device serial number string.
/// Returns a C string pointer; call `free_string` to release.
/// Returns NULL on failure.
const char *revo3_get_serial_number(DeviceHandler *handle, uint8_t slave_id);

/// Revo3-prefixed alias for `revo3_get_serial_number`.
const char *revo3_get_device_sn(DeviceHandler *handle, uint8_t slave_id);

/// Get Revo3 firmware version string.
/// Returns a C string pointer; call `free_string` to release.
/// Returns NULL on failure.
const char *revo3_get_firmware_version(DeviceHandler *handle, uint8_t slave_id);

/// Revo3-prefixed alias for `revo3_get_firmware_version`.
const char *revo3_get_device_fw_version(DeviceHandler *handle, uint8_t slave_id);

/// Get Revo3 hand type mapped to SkuType-compatible value.
/// Returns the enum value or -1 on failure.
int revo3_get_hand_type(DeviceHandler *handle, uint8_t slave_id);

/// Check if the configured hardware type uses the Revo3 motor API.
bool revo3_uses_motor_api(DeviceHandler *handle, uint8_t slave_id);

/// Set hardware type for a specific slave device.
///
/// Use this to manually override the hardware type when SN-based auto-detection
/// in `stark_get_device_info()` returns an incorrect result.
///
/// Can also be used to modify `CDetectedDevice.hardware_type` before calling
/// `init_from_detected()`, or to override after initialization.
///
/// # Parameters
/// - handle: Device handler
/// - slave_id: The slave ID
/// - hw_type: StarkHardwareType enum value
void stark_set_hardware_type(DeviceHandler *handle, uint8_t slave_id, StarkHardwareType hw_type);

/// Read holding registers (fast, no retry).
/// address: Start register address.
/// count: Number of registers to read.
/// out_data: Pointer to array to store data (caller must allocate enough space).
/// Returns 0 on success, -1 on failure.
int32_t stark_read_holding_registers(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     uint16_t address,
                                     uint16_t count,
                                     uint16_t *out_data);

/// Read input registers (fast, no retry).
/// address: Start register address.
/// count: Number of registers to read.
/// out_data: Pointer to array to store data (caller must allocate enough space).
/// Returns 0 on success, -1 on failure.
int32_t stark_read_input_registers(DeviceHandler *handle,
                                   uint8_t slave_id,
                                   uint16_t address,
                                   uint16_t count,
                                   uint16_t *out_data);

/// Get the protocol type of the device handler.
/// Returns: StarkProtocolType enum value:
///   - STARK_PROTOCOL_TYPE_MODBUS = 1
///   - STARK_PROTOCOL_TYPE_CAN_FD = 3
///   - STARK_PROTOCOL_TYPE_ETHER_CAT = 4
StarkProtocolType stark_get_protocol_type(DeviceHandler *handle);

/// Get the serial port name of the device handler.
/// Returns: Port name string (e.g., "/dev/ttyUSB0" or "COM3"), or NULL if not available.
/// The returned string is owned by the DeviceHandler and should not be freed.
/// For CAN/CANFD protocols, this may return an empty string.
const char *stark_get_port_name(DeviceHandler *handle);

/// Get the baudrate of the device handler.
/// Returns: Revo3 serial baudrate in bps (1/2/3/5 Mbps), or 0 if not applicable.
/// For Modbus: returns serial baudrate
/// For CANFD/EtherCAT: returns 0 (use stark_get_can_arb_baudrate/stark_get_can_data_baudrate instead)
///
/// Note: This function only returns serial port baudrate.
/// For CAN baudrate information, use:
///   - stark_get_can_arb_baudrate() - Get CAN arbitration baudrate
///   - stark_get_can_data_baudrate() - Get CAN data baudrate (CANFD only)
uint32_t stark_get_baudrate(DeviceHandler *handle);

/// Get the CAN arbitration baudrate.
/// Returns: CANFD arbitration baudrate in bps (e.g., 1000000 for 1 Mbps), or 0 if not CANFD protocol.
uint32_t stark_get_can_arb_baudrate(DeviceHandler *handle);

/// Get the CAN data baudrate.
/// Returns: CANFD data phase baudrate in bps (e.g., 5000000 for 5 Mbps), or 0 if not CANFD protocol.
uint32_t stark_get_can_data_baudrate(DeviceHandler *handle);

/// Get Revo3 touch sensor vendor type.
/// Returns: 0=Unknown, 2=Pressure.
uint8_t stark_get_touch_vendor(DeviceHandler *handle, uint8_t slave_id);

/// Enter Revo3 OTA mode.
void revo3_enter_ota(DeviceHandler *handle, uint8_t slave_id);

/// Start Revo3 DFU.
/// file_path: Path to the firmware file.
/// wait_secs: Seconds to wait after sending enter OTA command. Use 0 for default.
int32_t revo3_start_dfu(DeviceHandler *handle,
                        uint8_t slave_id,
                        const char *file_path,
                        uintptr_t wait_secs);

/// Start Revo3 DFU with target type (V1.1+).
/// file_path: Path to the firmware file.
/// target: Upgrade target (0=MainFw, 1=Image, 2=MotorFw).
/// wait_secs: Seconds to wait after sending enter OTA command. Use 0 for default.
int32_t revo3_start_dfu_with_target(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    const char *file_path,
                                    uint8_t target,
                                    uintptr_t wait_secs);

/// Start Revo3 Main Board MCU firmware upgrade (V1.1+).
/// file_path: Path to the firmware file.
/// wait_secs: Seconds to wait after sending enter OTA command. Use 0 for default.
int32_t revo3_start_mcu_dfu(DeviceHandler *handle,
                            uint8_t slave_id,
                            const char *file_path,
                            uintptr_t wait_secs);

/// Start Revo3 Image resource pack upgrade (V1.1+).
/// file_path: Path to the image resource pack file.
/// wait_secs: Seconds to wait. Use 0 for default.
int32_t revo3_start_image_dfu(DeviceHandler *handle,
                              uint8_t slave_id,
                              const char *file_path,
                              uintptr_t wait_secs);

/// Start Revo3 Motor board firmware upgrade (V1.1+).
/// file_path: Path to the motor firmware file.
/// wait_secs: Seconds to wait. Use 0 for default.
int32_t revo3_start_motor_dfu(DeviceHandler *handle,
                              uint8_t slave_id,
                              const char *file_path,
                              uintptr_t wait_secs);

/// Stop Revo3 DFU.
void revo3_stop_dfu(uint8_t slave_id);

/// Abort Revo3 DFU.
void revo3_abort_dfu(uint8_t slave_id);

/// Reset Revo3 DFU state.
void revo3_reset_dfu_state(uint8_t slave_id);

/// Enable/disable a single Revo3 touch module.
/// module_id: 0=Palm, 1=ThumbTip, 2=ThumbPad, ... 10=PinkyPad
void revo3_set_touch_module_enabled(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    uint8_t module_id,
                                    bool enabled);

/// Read whether a single Revo3 touch module is enabled.
/// Returns 1 if enabled, 0 if disabled, -1 on failure.
int revo3_get_touch_module_enabled(DeviceHandler *handle, uint8_t slave_id, uint8_t module_id);

/// Set target position for a single Revo3 motor (degrees).
void revo3_set_motor_position(DeviceHandler *handle,
                              uint8_t slave_id,
                              uint16_t motor_id,
                              float position_deg);

/// Set target current for a single Revo3 motor (mA).
void revo3_set_motor_current(DeviceHandler *handle,
                             uint8_t slave_id,
                             uint16_t motor_id,
                             float current);

/// Full MIT mode control for a single Revo3 motor.
/// τ = Kp * (P_des - P_actual) + Kd * (V_des - V_actual) + T_ff
void revo3_set_motor_mit(DeviceHandler *handle,
                         uint8_t slave_id,
                         uint16_t motor_id,
                         float position_deg,
                         float velocity,
                         float current,
                         float kp,
                         float kd);

/// Set all 21 Revo3 motor positions at once (degrees).
/// `positions` must point to an array of at least 21 f32 values.
void revo3_set_all_motor_positions(DeviceHandler *handle, uint8_t slave_id, const float *positions);

/// Set target velocity for a single Revo3 motor (RPM).
void revo3_set_motor_velocity(DeviceHandler *handle,
                              uint8_t slave_id,
                              uint16_t motor_id,
                              float velocity);

/// Set all 21 Revo3 motor velocities at once (RPM).
/// `velocities` must point to an array of at least 21 f32 values.
void revo3_set_all_motor_velocities(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    const float *velocities);

/// Set all 21 Revo3 motor currents at once (mA).
/// `currents` must point to an array of at least 21 f32 values.
void revo3_set_all_motor_currents(DeviceHandler *handle, uint8_t slave_id, const float *currents);

/// Clear all Revo3 motor error states.
void revo3_clear_motor_errors(DeviceHandler *handle, uint8_t slave_id);

/// Set Revo3 calibration current.
void revo3_set_calibration_current(DeviceHandler *handle, uint8_t slave_id, float current);

/// Trigger Revo3 manual zero calibration.
void revo3_manual_calibration(DeviceHandler *handle, uint8_t slave_id);

/// Enable or disable Revo3 auto calibration on power-up.
void revo3_set_auto_calibration(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Set Revo3 max continuous current.
void revo3_set_max_continuous_current(DeviceHandler *handle, uint8_t slave_id, float current);

/// Read complete Revo3 motor status data.
/// Returns NULL on failure. Call `free_revo3_motor_status_data` to release.
CRevo3MotorStatusData *revo3_get_motor_status_data(DeviceHandler *handle, uint8_t slave_id);

/// Read all 21 Revo3 motor positions (degrees).
/// `out_positions` must point to an array of at least 21 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_positions(DeviceHandler *handle, uint8_t slave_id, float *out_positions);

/// Check if hardware type uses Revo3 motor API.
bool stark_uses_revo3_motor_api(uint8_t hw_type);

/// Read all 21 Revo3 motor velocities.
/// `out_velocities` must point to an array of at least 21 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_velocities(DeviceHandler *handle, uint8_t slave_id, float *out_velocities);

/// Read all 21 Revo3 motor currents.
/// `out_currents` must point to an array of at least 21 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_currents(DeviceHandler *handle, uint8_t slave_id, float *out_currents);

/// Read all 21 Revo3 motor error codes.
/// `out_errors` must point to an array of at least 21 u16 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_errors(DeviceHandler *handle, uint8_t slave_id, uint16_t *out_errors);

/// Free Revo3 motor status data returned by `revo3_get_motor_status_data`.
void free_revo3_motor_status_data(CRevo3MotorStatusData *data);

/// Read all 21 Revo3 motor status codes.
/// `out_statuses` must point to an array of at least 21 u16 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_status(DeviceHandler *handle, uint8_t slave_id, uint16_t *out_statuses);

/// Set all Revo3 touch modules enabled/disabled via bitmask.
/// Bits 0~10 correspond to modules 0~10.
void revo3_set_all_touch_modules_enabled(DeviceHandler *handle,
                                         uint8_t slave_id,
                                         uint16_t enabled_bits);

/// Set Revo3 touch data output type.
/// data_type: 0=Pressure Array, 1=Force Summary
void revo3_set_touch_data_type(DeviceHandler *handle, uint8_t slave_id, uint16_t data_type);

/// Read Revo3 touch data output type.
/// Returns 0=Pressure Array, 1=Force Summary, or -1 on failure.
int revo3_get_touch_data_type(DeviceHandler *handle, uint8_t slave_id);

/// Read Revo3 touch summary (42 pad force values).
/// `out_summary` must point to an array of at least 42 u16 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_touch_summary(DeviceHandler *handle, uint8_t slave_id, uint16_t *out_summary);

/// Read Revo3 touch module pressure data.
/// `out_data` must point to a buffer large enough (at least 57 u16 for the largest module).
/// `out_count` will be set to the number of values written.
/// Returns 0 on success, -1 on failure.
int revo3_get_touch_module_data(DeviceHandler *handle,
                                uint8_t slave_id,
                                uint8_t module_id,
                                uint16_t *out_data,
                                uint16_t *out_count);

/// Single joint control.
/// mode: ControlMode (0=Position, 2=Current, 4=Impedance, 5=Damping)
void revo3_single_joint_control(DeviceHandler *handle,
                                uint8_t slave_id,
                                uint16_t joint_id,
                                uint16_t mode,
                                float param);

/// Multi-joint synchronous control (21 joints).
/// `params` must point to an array of at least 21 f32 values.
void revo3_multi_joint_control(DeviceHandler *handle,
                               uint8_t slave_id,
                               uint16_t mode,
                               const float *params);

/// Grouped MIT params: set all 21 Kp values (registers 1300–1320).
/// `kp_values` must point to at least 21 f32 values.
void revo3_set_all_mit_kp(DeviceHandler *handle, uint8_t slave_id, const float *kp_values);

/// Grouped MIT params: set all 21 Kd values.
/// `kd_values` must point to at least 21 f32 values.
void revo3_set_all_mit_kd(DeviceHandler *handle, uint8_t slave_id, const float *kd_values);

/// Grouped MIT params: set all 21 position values.
/// `positions` must point to at least 21 f32 values.
void revo3_set_all_mit_positions(DeviceHandler *handle, uint8_t slave_id, const float *positions);

/// Grouped MIT params: set all 21 velocity values.
/// `velocities` must point to at least 21 f32 values.
void revo3_set_all_mit_velocities(DeviceHandler *handle, uint8_t slave_id, const float *velocities);

/// Grouped MIT params: set all 21 torque values (mA).
/// `torques` must point to at least 21 f32 values.
void revo3_set_all_mit_torques(DeviceHandler *handle, uint8_t slave_id, const float *torques);

/// Move a single joint using quintic polynomial trajectory (Non-blocking).
/// Returns 0 on success, -1 on failure.
int revo3_move_joint(DeviceHandler *handle,
                     uint8_t slave_id,
                     uint16_t joint_id,
                     float target_pos,
                     float duration,
                     float dt);

/// Move a single joint using quintic polynomial trajectory and block until completion.
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_wait(DeviceHandler *handle,
                          uint8_t slave_id,
                          uint16_t joint_id,
                          float target_pos,
                          float duration,
                          float dt);

/// Move a single joint with custom Kp/Kd gains (Non-blocking).
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_with_gains(DeviceHandler *handle,
                                uint8_t slave_id,
                                uint16_t joint_id,
                                float target_pos,
                                float duration,
                                float dt,
                                float kp,
                                float kd);

/// Move a single joint with custom Kp/Kd gains and block until completion.
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_with_gains_wait(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     uint16_t joint_id,
                                     float target_pos,
                                     float duration,
                                     float dt,
                                     float kp,
                                     float kd);

/// Move a single joint to `target_pos` with a specified `speed` (rpm) (Non-blocking).
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_with_speed(DeviceHandler *handle,
                                uint8_t slave_id,
                                uint16_t joint_id,
                                float target_pos,
                                float speed,
                                float dt);

/// Move a single joint to `target_pos` with a specified `speed` (rpm) and wait until completed (Blocking).
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_with_speed_wait(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     uint16_t joint_id,
                                     float target_pos,
                                     float speed,
                                     float dt);

/// Move a single joint with a specified `speed` (rpm) and custom Kp/Kd gains (Non-blocking).
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_with_speed_and_gains(DeviceHandler *handle,
                                          uint8_t slave_id,
                                          uint16_t joint_id,
                                          float target_pos,
                                          float speed,
                                          float dt,
                                          float kp,
                                          float kd);

/// Move a single joint with a specified `speed` (rpm) and custom Kp/Kd gains and wait until completed (Blocking).
/// Returns 0 on success, -1 on failure.
int revo3_move_joint_with_speed_and_gains_wait(DeviceHandler *handle,
                                               uint8_t slave_id,
                                               uint16_t joint_id,
                                               float target_pos,
                                               float speed,
                                               float dt,
                                               float kp,
                                               float kd);

/// Move all joints using quintic polynomial trajectories (Non-blocking).
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand(DeviceHandler *handle,
                    uint8_t slave_id,
                    const float *target_positions,
                    uint16_t num_joints,
                    float duration,
                    float dt);

/// Move all joints using quintic polynomial trajectories and block until completion.
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_wait(DeviceHandler *handle,
                         uint8_t slave_id,
                         const float *target_positions,
                         uint16_t num_joints,
                         float duration,
                         float dt);

/// Move all joints with custom Kp/Kd gains (Non-blocking).
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_with_gains(DeviceHandler *handle,
                               uint8_t slave_id,
                               const float *target_positions,
                               uint16_t num_joints,
                               float duration,
                               float dt,
                               float kp,
                               float kd);

/// Move all joints with custom Kp/Kd gains and block until completion.
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_with_gains_wait(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    const float *target_positions,
                                    uint16_t num_joints,
                                    float duration,
                                    float dt,
                                    float kp,
                                    float kd);

/// Move all joints to `target_positions` with a uniform `speed` (rpm) (Non-blocking).
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_with_speed(DeviceHandler *handle,
                               uint8_t slave_id,
                               const float *target_positions,
                               uint16_t num_joints,
                               float speed,
                               float dt);

/// Move all joints to `target_positions` with a uniform `speed` (rpm) and block until completion.
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_with_speed_wait(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    const float *target_positions,
                                    uint16_t num_joints,
                                    float speed,
                                    float dt);

/// Move all joints with uniform `speed` and custom Kp/Kd gains (Non-blocking).
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_with_speed_and_gains(DeviceHandler *handle,
                                         uint8_t slave_id,
                                         const float *target_positions,
                                         uint16_t num_joints,
                                         float speed,
                                         float dt,
                                         float kp,
                                         float kd);

/// Move all joints with uniform `speed` and custom Kp/Kd gains and block until completion.
/// `target_positions` must point to an array of `num_joints` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_hand_with_speed_and_gains_wait(DeviceHandler *handle,
                                              uint8_t slave_id,
                                              const float *target_positions,
                                              uint16_t num_joints,
                                              float speed,
                                              float dt,
                                              float kp,
                                              float kd);

/// Get all 21 motor temperatures.
/// `out_temps` must point to an array of at least 21 u16 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_temperatures(DeviceHandler *handle, uint8_t slave_id, uint16_t *out_temps);

/// Set global protection current (mA).
void revo3_set_global_protect_current(DeviceHandler *handle, uint8_t slave_id, float current_ma);

/// Enable or disable software e-stop.
void revo3_set_software_e_stop(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Get broadcast ID usage state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_use_broadcast_id(DeviceHandler *handle, uint8_t slave_id);

/// Enable or disable using the broadcast ID.
void revo3_set_use_broadcast_id(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Get auto calibration state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_auto_calibration(DeviceHandler *handle, uint8_t slave_id);

/// Get global protection current (mA). Returns 0 on success, -1 on error.
int revo3_get_global_protect_current(DeviceHandler *handle, uint8_t slave_id, float *out_current);

/// Get per-joint protection currents for all 21 joints (mA).
/// `out_currents` must point to an array of at least 21 f32 values.
/// Returns 0 on success, -1 on error.
int revo3_get_all_joint_protect_currents(DeviceHandler *handle,
                                         uint8_t slave_id,
                                         float *out_currents);

/// Get per-joint position limits for all 21 joints.
/// `out_min_positions` and `out_max_positions` must point to arrays of at least 21 f32 values.
/// Returns 0 on success, -1 on error.
int revo3_get_all_joint_position_limits(DeviceHandler *handle,
                                        uint8_t slave_id,
                                        float *out_min_positions,
                                        float *out_max_positions);

/// Get per-joint speed limits for all 21 joints.
/// `out_min_speeds` and `out_max_speeds` must point to arrays of at least 21 f32 values.
/// Returns 0 on success, -1 on error.
int revo3_get_all_joint_speed_limits(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     float *out_min_speeds,
                                     float *out_max_speeds);

/// Get max continuous current (mA). Returns 0 on success, -1 on error.
int revo3_get_max_continuous_current(DeviceHandler *handle, uint8_t slave_id, float *out_current);

/// Get auto clear motor errors state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_auto_clear_motor_error(DeviceHandler *handle, uint8_t slave_id);

/// Enable or disable auto clear motor errors.
void revo3_set_auto_clear_motor_error(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Get teaching mode state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_teaching_mode(DeviceHandler *handle, uint8_t slave_id);

/// Enter or exit teaching mode.
void revo3_set_teaching_mode(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Get touch screen switch state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_touch_screen(DeviceHandler *handle, uint8_t slave_id);

/// Enable or disable the touch screen switch.
void revo3_set_touch_screen(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Reset finger parameters to defaults.
void revo3_reset_finger_defaults(DeviceHandler *handle, uint8_t slave_id);

/// Set the zero position with explicit motor offsets (degrees).
///
/// `offsets_deg` must point to an array containing exactly 21 f32 degree offset
/// values, which are written to registers starting from 60.
void revo3_set_zero_position(DeviceHandler *handle, uint8_t slave_id, const float *offsets_deg);

/// Set the current position as zero calibration.
///
/// Recommended Calibration Workflow:
/// 1. Disable the motors to allow free manual movements of the fingers.
/// 2. Manually pose the hand into the desired zero-reference pose.
/// 3. Enable the motors to lock the pose.
/// 4. Call this API to register the current feedback positions as zero.
void revo3_set_current_position_as_zero(DeviceHandler *handle, uint8_t slave_id);

/// Get the zero position offsets (degrees).
/// `out_offsets` must point to a buffer of sufficient size to store all motor
/// offsets (e.g. 13, 16, or 21 f32 values depending on the hand SKU).
/// Returns 0 on success, -1 on error.
int revo3_get_zero_position(DeviceHandler *handle, uint8_t slave_id, float *out_offsets);

/// Factory reset.
void revo3_factory_reset(DeviceHandler *handle, uint8_t slave_id);

/// Software reboot.
void revo3_reboot(DeviceHandler *handle, uint8_t slave_id);

/// Get RS485 Baudrate (returns Baudrate enum value 1/2/3/5, or -1 on error)
int revo3_get_rs485_baudrate(DeviceHandler *handle, uint8_t slave_id);

/// Set RS485 Baudrate using Baudrate enum values 1/2/3/5
void revo3_set_rs485_baudrate(DeviceHandler *handle, uint8_t slave_id, Baudrate baudrate);

/// Get CANFD Baudrate (returns BaudrateCAN enum value 1/2/4/5, or -1 on error)
int revo3_get_canfd_baudrate(DeviceHandler *handle, uint8_t slave_id);

/// Set CANFD Baudrate using BaudrateCAN enum values 1/2/4/5
void revo3_set_canfd_baudrate(DeviceHandler *handle, uint8_t slave_id, BaudrateCAN baudrate);

/// Set buzzer switch.
void revo3_set_buzzer_switch(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Get buzzer switch state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_buzzer_switch(DeviceHandler *handle, uint8_t slave_id);

/// Set vibration switch.
void revo3_set_vibration_switch(DeviceHandler *handle, uint8_t slave_id, bool enabled);

/// Get vibration switch state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_vibration_switch(DeviceHandler *handle, uint8_t slave_id);

/// Get software e-stop state. Returns 1 for true, 0 for false, -1 on error.
int revo3_get_software_e_stop(DeviceHandler *handle, uint8_t slave_id);

/// Set per-joint protection current (mA).
void revo3_set_joint_protect_current(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     uint16_t joint_id,
                                     float current_ma);

/// Set per-joint position limits (degrees).
void revo3_set_joint_position_limits(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     uint16_t joint_id,
                                     float min_pos,
                                     float max_pos);

/// Set per-joint speed limits.
void revo3_set_joint_speed_limits(DeviceHandler *handle,
                                  uint8_t slave_id,
                                  uint16_t joint_id,
                                  float min_speed,
                                  float max_speed);

/// Get hardware version string.
/// Returns a C string pointer; call `free_string` to release.
/// Returns NULL on failure.
const char *revo3_get_hardware_version(DeviceHandler *handle, uint8_t slave_id);

/// Get motor online status (bitmask).
/// Returns bitmask as u32, or 0 on failure.
uint32_t revo3_get_motor_online_status(DeviceHandler *handle, uint8_t slave_id);

/// Decode a Revo3 motor status bitmask into a human-readable C string.
/// Returns a C string pointer; call `free_string` to release.
const char *revo3_motor_status_desc(uint16_t status);

/// Read complete system status (registers 1800–1804).
/// Returns NULL on failure. Call `free_revo3_system_status` to release.
/// Internal sampling rate: 5Hz. Host should poll at ≤5Hz.
CRevo3SystemStatus *revo3_get_system_status(DeviceHandler *handle, uint8_t slave_id);

/// Read system current (mA). Returns 0xFFFF on failure.
uint16_t revo3_get_system_current(DeviceHandler *handle, uint8_t slave_id);

/// Read system voltage (V). Returns 0xFFFF on failure.
uint16_t revo3_get_system_voltage(DeviceHandler *handle, uint8_t slave_id);

/// Read system power (W). Returns 0xFFFF on failure.
uint16_t revo3_get_system_power(DeviceHandler *handle, uint8_t slave_id);

/// Read system temperature (°C). Returns 0xFFFF on failure.
uint16_t revo3_get_system_temperature(DeviceHandler *handle, uint8_t slave_id);

/// Free system status data returned by `revo3_get_system_status`.
void free_revo3_system_status(CRevo3SystemStatus *status);

void free_string(const char *s);

/// Get Slave ID (Returns slave id or -1 on error)
int revo3_get_slave_id(DeviceHandler *handle, uint8_t slave_id);

/// Set Slave ID
void revo3_set_slave_id(DeviceHandler *handle, uint8_t slave_id, uint8_t new_slave_id);

/// Unlock factory-only write registers.
void factory_set_key(DeviceHandler *handle, uint8_t slave_id, const char *operation_key);

/// Program the device serial number.
void factory_set_device_sn(DeviceHandler *handle, uint8_t slave_id, const char *sn);

/// Set the first-order Low-Pass Filter (LPF) coefficient for servo control.
/// `alpha` must be in the range (0.0, 1.0]. Default is 1.0 (no filtering).
void revo3_set_servo_lpf_alpha(DeviceHandler *handle, float alpha);

/// Get the current first-order Low-Pass Filter (LPF) coefficient.
float revo3_get_servo_lpf_alpha(DeviceHandler *handle);

/// Set the high-frequency real-time servo smoothing filter mode.
/// `mode`: 0 = None, 1 = First-order LPF, 2 = Second-order Critically Damped system.
void revo3_set_servo_filter_mode(DeviceHandler *handle, uint8_t mode);

/// Get the current high-frequency real-time servo smoothing filter mode.
/// Returns: 0 = None, 1 = First-order LPF, 2 = Second-order Critically Damped system.
uint8_t revo3_get_servo_filter_mode(DeviceHandler *handle);

/// Set the natural frequency (omega) for the second-order critically damped servo filter.
/// Higher values mean faster tracking but less smoothing. Default is 20.0.
void revo3_set_servo_damping_omega(DeviceHandler *handle, float omega);

/// Get the current natural frequency (omega) for the second-order filter.
float revo3_get_servo_damping_omega(DeviceHandler *handle);

/// High-frequency real-time servo control for a single joint using custom gains.
void revo3_servo_joint_with_gains(DeviceHandler *handle,
                                  uint8_t slave_id,
                                  uint16_t joint_id,
                                  float target_pos,
                                  float target_vel,
                                  float kp,
                                  float kd);

/// High-frequency real-time servo control for a single joint using default gains.
void revo3_servo_joint(DeviceHandler *handle,
                       uint8_t slave_id,
                       uint16_t joint_id,
                       float target_pos,
                       float target_vel);

/// High-frequency real-time servo control for all 21 joints using custom gains.
/// `target_positions` and `target_velocities` must each point to an array of at least 21 f32 values.
void revo3_servo_hand_with_gains(DeviceHandler *handle,
                                 uint8_t slave_id,
                                 const float *target_positions,
                                 const float *target_velocities,
                                 float kp,
                                 float kd);

/// High-frequency real-time servo control for all 21 joints using default gains.
/// `target_positions` and `target_velocities` must each point to an array of at least 21 f32 values.
void revo3_servo_hand(DeviceHandler *handle,
                      uint8_t slave_id,
                      const float *target_positions,
                      const float *target_velocities);

/// High-frequency real-time servo control for a single non-thumb finger using custom gains.
/// `target_positions`, `target_velocities`, `kp`, and `kd` must each point to an array of at least 4 f32 values.
/// `finger_index`: 1 = Index, 2 = Middle, 3 = Ring, 4 = Pinky.
void revo3_servo_finger_with_gains(DeviceHandler *handle,
                                   uint8_t slave_id,
                                   uint16_t finger_index,
                                   const float *target_positions,
                                   const float *target_velocities,
                                   const float *kp,
                                   const float *kd);

/// High-frequency real-time servo control for a single non-thumb finger using default gains.
/// `target_positions` and `target_velocities` must each point to an array of at least 4 f32 values.
/// `finger_index`: 1 = Index, 2 = Middle, 3 = Ring, 4 = Pinky.
void revo3_servo_finger(DeviceHandler *handle,
                        uint8_t slave_id,
                        uint16_t finger_index,
                        const float *target_positions,
                        const float *target_velocities);

/// High-frequency real-time servo control for the thumb using custom gains.
/// `target_positions`, `target_velocities`, `kp`, and `kd` must each point to an array of at least 5 f32 values.
void revo3_servo_thumb_with_gains(DeviceHandler *handle,
                                  uint8_t slave_id,
                                  const float *target_positions,
                                  const float *target_velocities,
                                  const float *kp,
                                  const float *kd);

/// High-frequency real-time servo control for the thumb using default gains.
/// `target_positions` and `target_velocities` must each point to an array of at least 5 f32 values.
void revo3_servo_thumb(DeviceHandler *handle,
                       uint8_t slave_id,
                       const float *target_positions,
                       const float *target_velocities);

/// Read all Revo3 touch modules enabled bitmask.
/// Returns bitmask (bits 0~10), or 0xFFFF on failure.
uint16_t revo3_get_all_touch_modules_enabled(DeviceHandler *handle, uint8_t slave_id);

/// Calibrate zero drift for all Revo3 touch modules (tare/zero calibration).
void revo3_calibrate_touch_zero(DeviceHandler *handle, uint8_t slave_id);

/// Calibrate zero drift for a single Revo3 touch module.
void revo3_calibrate_touch_zero_single(DeviceHandler *handle, uint8_t slave_id, uint8_t module_id);

/// MIT control for a single joint.
/// τ = Kp * (pos_ref − pos) + Kd * (vel_ref − vel) + τ_ff.
/// Recommended usage:
/// - Speed control: set kp=0, use kd with target velocity, and keep torque_ff=0.
/// - Position control: use kp and kd, set velocity=0, and keep torque_ff=0.
/// - Current control: set kp=0 and kd=0, then use torque_ff as target current.
void revo3_joint_mit_control(DeviceHandler *handle,
                             uint8_t slave_id,
                             uint16_t joint_id,
                             float kp,
                             float kd,
                             float position,
                             float velocity,
                             float torque_ff);

/// Set all 5 MIT parameters for a single joint in the interleaved register block.
/// Recommended usage:
/// - Speed control: set kp=0, use kd with target velocity, and keep torque_ff=0.
/// - Position control: use kp and kd, set velocity=0, and keep torque_ff=0.
/// - Current control: set kp=0 and kd=0, then use torque_ff as target current.
void revo3_set_joint_mit_params(DeviceHandler *handle,
                                uint8_t slave_id,
                                uint16_t joint_id,
                                float kp,
                                float kd,
                                float position,
                                float velocity,
                                float torque_ff);

/// MIT control for all 21 joints using the interleaved hand register block.
/// All arrays must point to at least 21 f32 values.
/// Recommended usage:
/// - Speed control: set Kp values to 0, use Kd with target velocities, and keep torques at 0.
/// - Position control: use Kp and Kd, set velocities to 0, and keep torques at 0.
/// - Current control: set Kp and Kd to 0, then use torques as target currents.
void revo3_hand_mit_control(DeviceHandler *handle,
                            uint8_t slave_id,
                            const float *kp_values,
                            const float *kd_values,
                            const float *positions,
                            const float *velocities,
                            const float *torques);

/// Same as revo3_hand_mit_control but without retry (for high-freq control).
void revo3_hand_mit_control_without_retry(DeviceHandler *handle,
                                          uint8_t slave_id,
                                          const float *kp_values,
                                          const float *kd_values,
                                          const float *positions,
                                          const float *velocities,
                                          const float *torques);

/// Set all 5 grouped MIT parameter arrays in a single write (105 registers).
/// Recommended usage:
/// - Speed control: set Kp values to 0, use Kd with target velocities, and keep torques at 0.
/// - Position control: use Kp and Kd, set velocities to 0, and keep torques at 0.
/// - Current control: set Kp and Kd to 0, then use torques as target currents.
void revo3_set_all_mit_params(DeviceHandler *handle,
                              uint8_t slave_id,
                              const float *kp_values,
                              const float *kd_values,
                              const float *positions,
                              const float *velocities,
                              const float *torques);

/// Same as revo3_set_all_mit_params but without retry.
void revo3_set_all_mit_params_without_retry(DeviceHandler *handle,
                                            uint8_t slave_id,
                                            const float *kp_values,
                                            const float *kd_values,
                                            const float *positions,
                                            const float *velocities,
                                            const float *torques);

/// Start GUI-style servo drag streaming for one joint.
///
/// Use this for slider, joystick, or VR targets where the caller updates only
/// when the target changes. The SDK owns one background stream, keeps the latest
/// target, and sends position-mode MIT commands with zero target velocity.
/// The stream remains active until revo3_stop_servo_drag is called, even if
/// the target is idle. The idle_timeout_ms argument controls unchanged-target
/// refresh cadence; it does not stop the stream.
///
/// filter_mode: 0=None, 1=FirstOrderLpf, 2=SecondOrderCriticallyDamped.
void revo3_start_servo_drag(DeviceHandler *handle,
                            uint8_t slave_id,
                            uint16_t joint_id,
                            float target_pos,
                            float kp,
                            float kd,
                            float vel_cap_rpm,
                            uint64_t interval_ms,
                            uint64_t idle_timeout_ms,
                            uint8_t filter_mode,
                            float omega);

/// Update the latest target for an active servo drag stream.
void revo3_update_servo_drag(DeviceHandler *handle,
                             uint8_t slave_id,
                             uint16_t joint_id,
                             float target_pos);

/// Stop servo drag streaming and hold the final target with zero velocity.
void revo3_stop_servo_drag(DeviceHandler *handle,
                           uint8_t slave_id,
                           uint16_t joint_id,
                           float final_pos);

/// Enter teaching (backdrive) mode for a single joint.
/// `out_positions` must point to a buffer of at least `max_samples` f32 values.
/// `out_count` will be set to the number of samples actually recorded.
/// Returns 0 on success, -1 on failure.
int revo3_teach_joint(DeviceHandler *handle,
                      uint8_t slave_id,
                      uint16_t joint_id,
                      float dt,
                      float duration,
                      float *out_positions,
                      uint32_t max_samples,
                      uint32_t *out_count);

/// Enter teaching (backdrive) mode for all joints.
/// `out_positions` must point to a buffer of at least `max_samples * num_joints` f32 values
/// (stored as row-major: [sample0_j0, sample0_j1, ..., sample1_j0, ...]).
/// `out_count` will be set to the number of samples actually recorded.
/// Returns 0 on success, -1 on failure.
int revo3_teach_hand(DeviceHandler *handle,
                     uint8_t slave_id,
                     float dt,
                     float duration,
                     uint16_t num_joints,
                     float *out_positions,
                     uint32_t max_samples,
                     uint32_t *out_count);

/// Replay a recorded single-joint trajectory.
/// `positions` must point to an array of `count` f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_replay_joint(DeviceHandler *handle,
                       uint8_t slave_id,
                       uint16_t joint_id,
                       const float *positions,
                       uint32_t count,
                       float dt,
                       float kp,
                       float kd);

/// Replay a recorded full-hand trajectory.
/// `positions` must point to an array of `num_frames * num_joints` f32 values (row-major).
/// Returns 0 on success, -1 on failure.
int revo3_replay_hand(DeviceHandler *handle,
                      uint8_t slave_id,
                      const float *positions,
                      uint32_t num_frames,
                      uint16_t num_joints,
                      float dt,
                      float kp,
                      float kd);

/// [RESERVED] Single joint admittance control.
/// Returns 0 on success, -1 on failure (currently always -1).
int revo3_admittance_joint(DeviceHandler *handle,
                           uint8_t slave_id,
                           uint16_t joint_id,
                           float equilibrium_pos,
                           float mass,
                           float damping,
                           float stiffness,
                           float dt,
                           float duration);

/// [RESERVED] Full hand admittance control.
/// All arrays must point to at least `num_joints` f32 values.
/// Returns 0 on success, -1 on failure (currently always -1).
int revo3_admittance_hand(DeviceHandler *handle,
                          uint8_t slave_id,
                          const float *equilibrium_positions,
                          const float *mass_values,
                          const float *damping_values,
                          const float *stiffness_values,
                          uint16_t num_joints,
                          float dt,
                          float duration);

/// Finger control (non-thumb, 4 params).
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `params` must point to an array of 4 f32 values (degrees/rpm/mA).
void revo3_finger_control(DeviceHandler *handle,
                          uint8_t slave_id,
                          uint8_t finger_id,
                          uint16_t mode,
                          const float *params);

/// Thumb control (5 params).
/// `params` must point to an array of 5 f32 values (degrees/rpm/mA).
void revo3_thumb_control(DeviceHandler *handle,
                         uint8_t slave_id,
                         uint16_t mode,
                         const float *params);

/// Four-finger MIT control.
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `params` must point to an array of 20 f32 values: 4 joints × 5 MIT params.
/// Recommended usage per joint:
/// - Speed control: set kp=0, use kd with target velocity, and keep torque_ff=0.
/// - Position control: use kp and kd, set velocity=0, and keep torque_ff=0.
/// - Current control: set kp=0 and kd=0, then use torque_ff as target current.
void revo3_finger_mit_control(DeviceHandler *handle,
                              uint8_t slave_id,
                              uint8_t finger_id,
                              const float *params);

/// Thumb MIT control.
/// `params` must point to an array of 25 f32 values: 5 joints × 5 MIT params.
/// Recommended usage per joint:
/// - Speed control: set kp=0, use kd with target velocity, and keep torque_ff=0.
/// - Position control: use kp and kd, set velocity=0, and keep torque_ff=0.
/// - Current control: set kp=0 and kd=0, then use torque_ff as target current.
void revo3_thumb_mit_control(DeviceHandler *handle, uint8_t slave_id, const float *params);

/// Get single motor temperature.
/// Returns temperature as u16, or 0xFFFF on failure.
uint16_t revo3_get_motor_temperature(DeviceHandler *handle, uint8_t slave_id, uint16_t motor_id);

/// Get motor SN string.
/// Returns a C string pointer; call `free_string` to release.
/// Returns NULL on failure.
const char *revo3_get_motor_sn(DeviceHandler *handle, uint8_t slave_id, uint16_t motor_id);

/// Get all motor SN strings.
/// `out_sns` must point to an array of at least 21 char pointers.
/// Each returned string must be released with `free_string`.
/// Returns 0 on success, -1 on failure.
int revo3_get_all_motor_sns(DeviceHandler *handle, uint8_t slave_id, const char **out_sns);

/// Get all motor firmware versions (21 values).
/// `out_versions` must point to an array of at least 21 u16 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_motor_fw_versions(DeviceHandler *handle, uint8_t slave_id, uint16_t *out_versions);

/// Get all motor firmware versions as raw u16 values.
/// `out_versions` must point to an array of at least 21 u16 values.
/// Returns 0 on success, -1 on failure.
int revo3_get_motor_fw_versions_raw(DeviceHandler *handle,
                                    uint8_t slave_id,
                                    uint16_t *out_versions);

/// Move a specific finger with quintic trajectory control (Non-blocking).
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `target_positions` must point to an array of 4 f32 values: [Abd, MCP, PIP, DIP].
/// Returns 0 on success, -1 on failure.
int revo3_move_finger(DeviceHandler *handle,
                      uint8_t slave_id,
                      uint8_t finger_id,
                      const float *target_positions,
                      float duration,
                      float dt);

/// Move a specific finger with quintic trajectory control and block until completion.
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `target_positions` must point to an array of 4 f32 values: [Abd, MCP, PIP, DIP].
/// Returns 0 on success, -1 on failure.
int revo3_move_finger_wait(DeviceHandler *handle,
                           uint8_t slave_id,
                           uint8_t finger_id,
                           const float *target_positions,
                           float duration,
                           float dt);

/// Move a specific finger with custom Kp/Kd gains and quintic trajectory control (Non-blocking).
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `target_positions` must point to an array of 4 f32 values: [Abd, MCP, PIP, DIP].
/// Returns 0 on success, -1 on failure.
int revo3_move_finger_with_gains(DeviceHandler *handle,
                                 uint8_t slave_id,
                                 uint8_t finger_id,
                                 const float *target_positions,
                                 float duration,
                                 float dt,
                                 float kp,
                                 float kd);

/// Move a specific finger with custom Kp/Kd gains and block until completion.
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `target_positions` must point to an array of 4 f32 values: [Abd, MCP, PIP, DIP].
/// Returns 0 on success, -1 on failure.
int revo3_move_finger_with_gains_wait(DeviceHandler *handle,
                                      uint8_t slave_id,
                                      uint8_t finger_id,
                                      const float *target_positions,
                                      float duration,
                                      float dt,
                                      float kp,
                                      float kd);

/// Move a specific finger with joint-specific Kp/Kd gains (4 joints) and quintic trajectory control (Non-blocking).
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `target_positions` must point to an array of 4 f32 values: [Abd, MCP, PIP, DIP].
/// `kp` must point to an array of 4 f32 values.
/// `kd` must point to an array of 4 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_finger_with_joint_gains(DeviceHandler *handle,
                                       uint8_t slave_id,
                                       uint8_t finger_id,
                                       const float *target_positions,
                                       float duration,
                                       float dt,
                                       const float *kp,
                                       const float *kd);

/// Move a specific finger with joint-specific Kp/Kd gains (4 joints) and block until completion.
/// finger_id: 1=index, 2=middle, 3=ring, 4=pinky
/// `target_positions` must point to an array of 4 f32 values: [Abd, MCP, PIP, DIP].
/// `kp` must point to an array of 4 f32 values.
/// `kd` must point to an array of 4 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_finger_with_joint_gains_wait(DeviceHandler *handle,
                                            uint8_t slave_id,
                                            uint8_t finger_id,
                                            const float *target_positions,
                                            float duration,
                                            float dt,
                                            const float *kp,
                                            const float *kd);

/// Move thumb with quintic trajectory control (Non-blocking).
/// `target_positions` must point to an array of 5 f32 values: [CMC_rotation, MCP, IP, CMC_abd, CMC_flex].
/// Returns 0 on success, -1 on failure.
int revo3_move_thumb(DeviceHandler *handle,
                     uint8_t slave_id,
                     const float *target_positions,
                     float duration,
                     float dt);

/// Move thumb with custom Kp/Kd gains and quintic trajectory control (Non-blocking).
/// `target_positions` must point to an array of 5 f32 values: [CMC_rotation, MCP, IP, CMC_abd, CMC_flex].
/// Returns 0 on success, -1 on failure.
int revo3_move_thumb_with_gains(DeviceHandler *handle,
                                uint8_t slave_id,
                                const float *target_positions,
                                float duration,
                                float dt,
                                float kp,
                                float kd);

/// Move thumb with joint-specific Kp/Kd gains (5 joints) and quintic trajectory control (Non-blocking).
/// `target_positions` must point to an array of 5 f32 values: [CMC_rotation, MCP, IP, CMC_abd, CMC_flex].
/// `kp` must point to an array of 5 f32 values.
/// `kd` must point to an array of 5 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_thumb_with_joint_gains(DeviceHandler *handle,
                                      uint8_t slave_id,
                                      const float *target_positions,
                                      float duration,
                                      float dt,
                                      const float *kp,
                                      const float *kd);

/// Move thumb using quintic polynomial trajectory and wait for completion (Blocking).
/// `target_positions` must point to an array of 5 f32 values: [CMC_rotation, MCP, IP, CMC_abd, CMC_flex].
/// Returns 0 on success, -1 on failure.
int revo3_move_thumb_wait(DeviceHandler *handle,
                          uint8_t slave_id,
                          const float *target_positions,
                          float duration,
                          float dt);

/// Move thumb with custom Kp/Kd gains and block until completion (Blocking).
/// `target_positions` must point to an array of 5 f32 values: [CMC_rotation, MCP, IP, CMC_abd, CMC_flex].
/// Returns 0 on success, -1 on failure.
int revo3_move_thumb_with_gains_wait(DeviceHandler *handle,
                                     uint8_t slave_id,
                                     const float *target_positions,
                                     float duration,
                                     float dt,
                                     float kp,
                                     float kd);

/// Move thumb with joint-specific Kp/Kd gains (5 joints) and block until completion (Blocking).
/// `target_positions` must point to an array of 5 f32 values: [CMC_rotation, MCP, IP, CMC_abd, CMC_flex].
/// `kp` must point to an array of 5 f32 values.
/// `kd` must point to an array of 5 f32 values.
/// Returns 0 on success, -1 on failure.
int revo3_move_thumb_with_joint_gains_wait(DeviceHandler *handle,
                                           uint8_t slave_id,
                                           const float *target_positions,
                                           float duration,
                                           float dt,
                                           const float *kp,
                                           const float *kd);

}  // extern "C"

#endif  // REVO3_SDK_H
