#pragma once

#include "rm_control/common/result.h"
#include "rm_control/interfaces/tool_modbus_bus.h"

#include <array>
#include <cstddef>
#include <cstdint>

namespace rm_control::interfaces {

inline constexpr std::size_t kO6JointCount = 6U;

enum class DexterousHandSide {
    Left,
    Right,
};

using O6JointPose = std::array<std::uint16_t, kO6JointCount>;

/**
 * @brief 一次 O6 动作所需的六关节位置、速度和转矩命令。
 *
 * @reason 把三个必须一致下发的寄存器组放入单一输入，适配器可以先完整校验，再按
 *         “转矩读回、速度读回、最后位置”顺序执行，避免使用设备残留配置。
 *
 * @author 唐永康
 * @date 2026-07-11
 *
 * @safety 该结构体自身不触发动作；传给 CommandMotion 后目标位置可能立即驱动 O6。
 * @note 三组值均按拇指弯曲、拇指横摆、食指、中指、无名指、小指排列，范围 0..255。
 */
struct O6MotionCommand {
    O6JointPose target_positions{};
    O6JointPose joint_speed_commands{};
    O6JointPose joint_torque_commands{};
};

/**
 * @brief 经 O6 协议字段校验后的灵巧手 readiness 快照。
 *
 * @reason 将总线状态、身份、六关节位置、转矩、速度、温度和故障放在同一结果中，
 *         状态机只在完整快照合规时才能把 `ready` 作为真实硬件门控事实。
 *
 * @author 唐永康
 * @date 2026-07-11
 *
 * @safety 只有 `ReadReadiness` 成功返回的快照才允许 `ready=true`。
 * @note 位置、转矩和速度为 O6 无量纲 0..255，温度为摄氏度整数。
 */
struct DexterousHandReadiness {
    bool ready = false;
    DexterousHandSide side = DexterousHandSide::Right;
    std::uint8_t device_id = 0;
    ToolModbusBusStatus tool_bus_status;
    std::uint16_t freedom = 0;
    std::uint16_t hand_version = 0;
    std::array<std::uint16_t, 3U> hand_number_parts{};
    char direction = '\0';
    O6JointPose joint_positions{};
    O6JointPose joint_torques{};
    O6JointPose joint_speeds{};
    O6JointPose temperatures_celsius{};
    O6JointPose fault_codes{};
};

/**
 * @brief 飞行操纵业务使用的 O6 灵巧手抽象接口。
 *
 * @reason 状态机只依赖 readiness 和完整六关节动作语义，不包含 LinkerHand 或 RealMan
 *         厂商头文件。
 *
 * @author 唐永康
 * @date 2026-07-11
 *
 * @safety readiness 为只读；动作命令可能触发真实握夹动作并必须受上层门控。
 * @note 当前接口只覆盖项目目标 O6，不承诺其他灵巧手型号兼容性。
 */
class IDexterousHand {
public:
    /**
     * @brief 销毁灵巧手接口对象。
     *
     * @reason 允许通过抽象接口安全释放具体 O6 适配器。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 析构函数无返回值且不得抛出异常。
     *
     * @calls 具体适配器析构函数。
     * @safety 只释放资源，不发送新的 O6 动作命令。
     * @note 总线所有权由具体依赖组装决定。
     */
    virtual ~IDexterousHand() = default;

    /**
     * @brief 读取并验证当前 O6 总线、身份、位置、转矩、速度、温度和故障状态。
     *
     * @reason 单一成功结果可以作为硬件门控输入，避免业务层把“总线已连接”误当成“O6 可用”。
     *
     * @author 唐永康
     * @date 2026-07-11
     *
     * @return 所有强制字段合规时返回 ready 快照；任一读取、维度或安全值非法时失败。
     *
     * @calls IToolModbusBus::GetStatus 和 ReadInputRegister。
     * @safety 只读，不触发 O6 或机械臂运动。
     * @note 调用方应在每次握夹前重新读取，不能永久缓存 readiness。
     */
    virtual common::Result<DexterousHandReadiness> ReadReadiness() = 0;

    /**
     * @brief 校验并下发 O6 六关节位置、速度和转矩动作命令。
     *
     * @reason 单一命令入口强制适配器先写转矩并读回、再写速度并读回，只有两组精确一致
     *         后才允许写位置，防止使用未知的残留速度或转矩执行抓握。
     *
     * @author 唐永康
     * @date 2026-07-11
     *
     * @param command 六关节位置、速度和转矩命令，每项有效范围 0..255；调用期间只读。
     * @return 三组命令按安全顺序完成时成功；输入越界、读写失败或读回不一致时返回错误。
     *
     * @calls IToolModbusBus::WriteHoldingRegisters；动作后的状态由 ReadReadiness 读取。
     * @safety 可能触发真实握夹动作，必须由状态机在 readiness 和 Live 门控通过后调用。
     * @note 不自动重试；转矩或速度任一步失败时禁止下发位置，成功也不替代动作后状态验证。
     */
    virtual common::Result<void> CommandMotion(
        const O6MotionCommand &command) = 0;
};

} // namespace rm_control::interfaces
