// =============================================================================
//  rudder_actuator_iface.h -- abstract rudder actuator interface (Sprint 4)
// =============================================================================
//
//  The PID inner loop knows only about IRudderActuator.  The concrete
//  implementation (hydraulic, electric-DC, servo, or test-rig motor) is
//  selected at build time via the AR_ACTUATOR_TYPE macro produced by the
//  .appack firmware_config.h.
//
//  Safety invariants (all implementations must honour):
//    1. angle_limit_deg  --  software limit loaded from NVS (or firmware_config.h
//                            default).  Commanded angles are clamped before
//                            reaching the physical output.
//    2. Limit switches   --  DI2 (port) / DI3 (starboard) block motion in
//                            the corresponding direction regardless of command.
//    3. Master power     --  DO3 must be HIGH before any motion.
//    4. STANDBY guard    --  implementations check current_mode() == STANDBY
//                            and refuse motion.
// =============================================================================

#pragma once

#include <cstdint>

namespace arautopilot::hal {

// ---------------------------------------------------------------------------
//  Actuator type enumeration (mirrors .appack firmware_config.h values)
// ---------------------------------------------------------------------------

enum class ActuatorType : uint8_t {
    HYDRAULIC_REVERSIBLE  = 0,  ///< Reversible hydraulic pump (Hynautic, Hypro …)
    ELECTRIC_DC           = 1,  ///< Reversible DC motor with mechanical end-stops
    SERVOMOTOR            = 2,  ///< Servomotor with built-in position feedback
    STERNDRIVE_ANALOG     = 3,  ///< Analog directional sterndrive
    TEST_RIG_MOTOR        = 4,  ///< HIL test rig: DC motor rotates compass platform
};

// ---------------------------------------------------------------------------
//  Abstract interface
// ---------------------------------------------------------------------------

class IRudderActuator {
public:
    virtual ~IRudderActuator() = default;

    /// One-time hardware initialisation (GPIOs, PWM channels …).
    virtual void init() = 0;

    /// Engage master power relay.  Required before command().
    virtual void power_on() = 0;

    /// Drop master power relay and stop all outputs immediately.
    virtual void power_off() = 0;

    /// Command the actuator toward *angle_deg* (positive = starboard).
    /// The implementation MUST clamp to [-angle_limit_deg, +angle_limit_deg]
    /// before driving any output.
    /// Returns true if the command was applied; false if an interlock blocked it.
    virtual bool command(float angle_deg) = 0;

    /// Estimated actual rudder angle (degrees) from the sensor/feedback.
    /// Returns 0.0 if no feedback is available.
    virtual float actual_angle_deg() const = 0;

    /// True if master power relay is currently energised.
    virtual bool is_powered() const = 0;

    /// True if either limit switch is asserted.
    virtual bool at_limit() const = 0;

    /// Current software angle limit (degrees, positive value).
    /// Can be updated at runtime via set_angle_limit_deg().
    virtual float angle_limit_deg() const = 0;

    /// Update the software angle limit.  Clamped to [15, 45] degrees.
    /// Persisted to NVS by the concrete implementation.
    virtual void set_angle_limit_deg(float limit_deg) = 0;

    /// Actuator type tag for diagnostics.
    virtual ActuatorType type() const = 0;
};

}  // namespace arautopilot::hal
