#ifndef RADIO_SCHEDULER_H
#define RADIO_SCHEDULER_H

// ============================================================================
// radio_scheduler.{h,cpp}
//
// Single SX1262 time-slice scheduler.  The Heltec WiFi LoRa V4 has ONE radio
// physically wired to the SX1262 antenna front-end and that radio is shared
// between three logical roles:
//
//   * RADIO_MODE_PDL          — the existing PDL custom mesh (sync 0x34,
//                               SF10/125kHz, 915 MHz). This is the default
//                               and primary role; the legacy mesh_publish.cpp
//                               TX/RX paths now route through this scheduler
//                               so they only TX/RX while the active mode is
//                               PDL.
//   * RADIO_MODE_MESHTASTIC   — Meshtastic LongFast US915 (SF11/250kHz/CR4-5,
//                               sync 0x2B, frequency derived from the channel
//                               hash for the "LongFast" preset).  Configured
//                               by meshtastic_set_params().
//   * RADIO_MODE_RNODE        — Reticulum RNode LoRa transport. Default
//                               SF7/125kHz/CR4-5, sync 0x14, no fixed
//                               preamble.  Configured by rnode_set_params().
//
// A fourth synthetic mode RADIO_MODE_OFF parks the radio (sleep) — used
// during WiFi STA association so the SPI bus and DIO pins don't contend
// with WiFi+BT-coex bursts.
//
// Default round-robin weights are 80/15/5 PDL/Meshtastic/RNode and are read
// from NVS at init: namespace "pdl_radio", keys pdl_pct/mesh_pct/rnode_pct.
// If the three values do not sum to 100 we fall back to 100/0/0 so the
// existing PDL mesh always works.
//
// Mode transitions are non-preemptive on TX: when a mode change is due
// the scheduler waits for any in-flight transmit to finish (TX queue drain),
// reconfigures the SX1262 with the new params, and re-arms RX before the
// caller can issue protocol I/O. This keeps the legacy mesh_publish.cpp
// API unchanged — its TX call is bounced through the scheduler, which only
// returns success after a clean PDL-mode reconfigure if needed.
//
// All public APIs are safe to call from the main loop. The scheduler does
// NOT spawn its own task; the embedder calls radio_sched_tick() from the
// top of the main loop (and ideally also from the LoRa RX path right
// before mesh_receive()).
// ============================================================================

#include <cstddef>
#include <cstdint>

#ifdef ARDUINO
#include <RadioLib.h>
#endif

// ----- Mode enum -----
enum RadioMode : uint8_t {
    RADIO_MODE_PDL        = 0,
    RADIO_MODE_MESHTASTIC = 1,
    RADIO_MODE_RNODE      = 2,
    RADIO_MODE_OFF        = 3,
    RADIO_MODE_COUNT      = 4
};

// ----- NVS layout -----
#define RADIO_SCHED_NVS_NS   "pdl_radio"
#define RADIO_SCHED_NVS_PDL  "pdl_pct"
#define RADIO_SCHED_NVS_MESH "mesh_pct"
#define RADIO_SCHED_NVS_RN   "rnode_pct"

// ----- Default weights (sum = 100) -----
#define RADIO_SCHED_DEFAULT_PDL_PCT    80
#define RADIO_SCHED_DEFAULT_MESH_PCT   15
#define RADIO_SCHED_DEFAULT_RNODE_PCT   5

// Length of one full round-robin period.  Each mode gets pct/100 of this
// window before we rotate.  100 ms keeps the worst-case starvation under
// 100 ms per protocol while still amortising radio reconfigure cost.
#define RADIO_SCHED_PERIOD_MS         1000

// ---------------------------------------------------------------------------
// Lifecycle
// ---------------------------------------------------------------------------

// Initialise the scheduler. Reads NVS weights, validates sum, and arms the
// initial mode (PDL).  Must be called AFTER mesh_init() because mesh_init
// is the canonical owner of the SX1262 instance. Safe to call once per
// boot.
void radio_sched_init();

// Pump the scheduler — call from the top of the main loop, BEFORE any
// protocol I/O. Internally this:
//   1. checks whether the current dwell window has expired
//   2. if so, drains any pending TX, reconfigures the SX1262 to the next
//      mode's params (frequency / BW / SF / CR / sync), and re-arms RX
//   3. updates current-mode bookkeeping
void radio_sched_tick();

// Force a one-shot mode lease — used when an upper layer (e.g. an outbound
// Meshtastic packet) needs a guaranteed slice. Returns true if granted; the
// scheduler will hold the requested mode until duration_ms has elapsed
// regardless of the round-robin schedule. Only one lease may be active at
// a time.
bool radio_sched_request_mode(RadioMode mode, uint32_t duration_ms);

// Currently-active mode (post-tick).
RadioMode radio_sched_current_mode();

// Per-mode TX gate. Returns true iff the scheduler is currently in `mode`.
// Callers (PDL/Meshtastic/RNode TX paths) must check this before dispatching.
// If false, the upper layer should queue the packet and retry on the next
// matching window.
bool radio_sched_can_tx(RadioMode mode);

// Re-read NVS weights (issued by command MSG_TYPE_RADIO_MODE).
// Re-validates sum, falls back to 100/0/0 if invalid.
void radio_sched_reload_weights();

// ---------------------------------------------------------------------------
// Hooks the scheduler invokes during a mode change.  Each hook reconfigures
// the SX1262 for its protocol's PHY params using the canonical RadioLib
// API.  These are implemented inside the protocol modules themselves so the
// scheduler stays radio-agnostic.
//
// Each hook returns 0 on success or a non-zero RadioLib error code.
// ---------------------------------------------------------------------------
int radio_pdl_set_params();
int radio_mesh_set_params();
int radio_rnode_set_params();
int radio_off_set_params();

// ---------------------------------------------------------------------------
// Statistics — useful for the health_report module.
// ---------------------------------------------------------------------------
struct RadioSchedStats {
    uint32_t mode_ms[RADIO_MODE_COUNT];   // cumulative time-in-mode
    uint32_t mode_switches;               // total mode transitions
    uint32_t reconfig_failures;           // SX1262 reconfigure errors
    uint32_t tx_drained_packets;          // packets drained on transition
};
void radio_sched_get_stats(RadioSchedStats* out);

// Native unit-test helper — advance virtual time by `ms` ms. Has no effect
// on Arduino; only used by test_interop.
#ifdef UNIT_TEST
extern "C" void radio_sched_test_advance_ms(uint32_t ms);
extern "C" void radio_sched_test_reset();
extern "C" void radio_sched_test_set_weights(uint8_t pdl, uint8_t mesh, uint8_t rnode);
#endif

#endif // RADIO_SCHEDULER_H
