#pragma once

#include <string>

namespace fgc {

// Per-axis lifecycle state, from the firmware ST line's state char (B/R/H/A/E).
enum class AxisState { Boot, Reset, Homing, Ready, Error, Unknown };

// One axis segment of a firmware `ST` status line:
//   ST Y:<state>,<xactual>,<xenc>,<drv_hex>,<sg>,<cs>,<pwm>,<flags> [P:...]
// Positions are absolute encoder counts; degrees are derived via Geometry.
struct AxisTelemetry {
    AxisState state      = AxisState::Unknown;
    long      xactual    = 0;       // TMC step counter (commanded position)
    long      xenc       = 0;       // encoder position (actual shaft position)
    unsigned  drv_status = 0;       // DRV_STATUS register (decoded from the 8-digit hex)
    int       sg         = 0;       // SG_RESULT
    int       cs         = 0;       // CS_ACTUAL
    int       pwm         = 0;      // PWM_SCALE_SUM
    bool      standstill = false;   // flag 'S' (DRV_STST)
    bool      stall      = false;   // flag 's'
    bool      overtemp   = false;   // flag 'o'/'O'
    bool      endstop_l  = false;   // flag 'L'
    bool      endstop_r  = false;   // flag 'R'

    bool moving() const { return state == AxisState::Homing || !standstill; }
    bool ready() const { return state == AxisState::Ready; }
};

// Telemetry snapshot from the motor controller: one segment per axis.
struct MotorTelemetry {
    AxisTelemetry yaw;
    AxisTelemetry pitch;
    bool          pitch_present = false;  // was a P: segment seen?
};

// Abstraction over the gimbal's motor controller. Implemented by
// SerialMotorController (Boost.Asio serial link) and MockMotorController
// (simulated, for development without hardware).
class IMotorController {
public:
    virtual ~IMotorController() = default;

    // Begin/*end* background telemetry reading.
    virtual void start() = 0;
    virtual void stop()  = 0;

    // Send a command line to the controller (e.g. "HOME", "MOVE Y 20000").
    // Implementations append the newline terminator the firmware requires.
    virtual void sendCommand(const std::string& cmd) = 0;

    // Latest telemetry snapshot (thread-safe in implementations).
    virtual MotorTelemetry telemetry() = 0;

    // The most recently completed firmware DUMP block (the text between
    // "DUMP BEGIN" and "DUMP END", inclusive), or "" if none has been captured.
    // Thread-safe in implementations.
    virtual std::string lastDump() = 0;

    // The most recently completed firmware DIAG block (the "DG ..." lines from
    // "DG BEGIN" through "DG DONE"), or "" if none. diagSeq() increments on each
    // completed capture so callers can detect a fresh result. Thread-safe.
    virtual std::string lastDiag() = 0;
    virtual unsigned    diagSeq() const = 0;

    // Whether the underlying link is usable.
    virtual bool connected() const = 0;
};

}  // namespace fgc
