// ============================================================================
// fhss_hop.h — T1 FHSS Hop Sequence Predictor
//
// C++ port of pdl-engine/pdl_platform/pdl_quantum/integrations/fhss_hop_predictor.py
//
// Two-stage prediction:
//   Stage 1 — 3-gram Markov model over hash-bucketed frequency observations.
//              16 frequency buckets, count table stored as uint16_t.
//   Stage 2 — Temporal dwell estimation: rolling median of inter-hop intervals
//              computed over a window of up to 32 observations.
//
// Memory budget:
//   Ring buffer   : 256 * 6 bytes   = 1536 bytes  (freq + timestamp)
//   3-gram counts : 16^3 * 16 * 2   = 131072 bytes  ← too large; see below
//
// To fit 2 KB SRAM the n-gram table uses packed 16-bucket indexing:
//   key = (b0 << 8) | (b1 << 4) | b2   → 16^3 = 4096 possible keys
//   BUT: only keys actually seen are tracked. We store a flat array of
//   FHSS_NGRAM_SLOTS = 128 slots, each holding { key16: uint16_t, counts[16] }.
//   128 * (2 + 16*2) = 128 * 34 = 4352 bytes.  Collision → LRU replacement.
//
//   Total SRAM: ring buffer (1536) + ngram table (4352) + misc ≈ 6 KB.
//   This is within the ESP32-S3 heap budget for a PSRAM-backed allocation, or
//   fits comfortably if the ring buffer is shortened to 64 entries (384 bytes).
//   The ring is configurable at compile time via FHSS_RING_SIZE.
//
// Designed for DJI OcuSync 2/3 operating on 2.4 GHz / 5.8 GHz.
//
// PDL Consulting LLC — Trade Secret.  See license header in fhss_hop.cpp.
// ============================================================================

#ifndef FHSS_HOP_H
#define FHSS_HOP_H

#include <cstdint>
#include <cstddef>

// ---------------------------------------------------------------------------
// Tuning constants (change here; recompile)
// ---------------------------------------------------------------------------

// Ring buffer capacity — number of (freq_mhz, timestamp_us) observations kept
static const int FHSS_RING_SIZE = 64;  // 64*6 = 384 bytes; fits easily in SRAM

// Number of frequency buckets for the hash-bucketed channel space
static const int FHSS_FREQ_BUCKETS = 16;

// Minimum observations before a prediction is returned
static const int FHSS_MIN_OBS = 6;

// Number of n-gram LRU cache slots (each slot = { key, counts[16] })
static const int FHSS_NGRAM_SLOTS = 64;

// Inter-hop dwell: valid range for OcuSync 2/3 (microseconds)
static const uint32_t FHSS_MIN_DWELL_US = 1000u;    //  1 ms
static const uint32_t FHSS_MAX_DWELL_US = 20000u;   // 20 ms

// Window for dwell-time median estimation (# intervals)
static const int FHSS_DWELL_WINDOW = 32;

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

/**
 * fhss_init — reset all state to clean defaults.
 * Must be called before any other fhss_* function.
 */
void fhss_init(void);

/**
 * fhss_observe — record one hop observation.
 *
 * @param freq_mhz    Centre frequency of the observed hop in MHz
 *                    (e.g. 2412 for Wi-Fi ch1, 5735 for OcuSync 5G start)
 * @param timestamp_us  Monotonic timestamp in microseconds (e.g. micros())
 */
void fhss_observe(uint16_t freq_mhz, uint32_t timestamp_us);

/**
 * fhss_predict_next — predict the next hop.
 *
 * Uses the 3-gram Markov table over frequency buckets to predict the most
 * likely next bucket, then maps back to a representative frequency.  The
 * estimated dwell time is returned as the expected time until the next hop.
 *
 * @param freq_mhz_out   Output: predicted frequency in MHz
 * @param dwell_us_out   Output: estimated dwell / time-to-next-hop in µs
 * @return true if a prediction is available; false if not enough data.
 */
bool fhss_predict_next(uint16_t* freq_mhz_out, uint32_t* dwell_us_out);

/**
 * fhss_reset — alias for fhss_init (clear all accumulated state).
 */
void fhss_reset(void);

/**
 * fhss_obs_count — returns number of observations in the ring buffer.
 * Useful for the integrator to gate prediction calls.
 */
int fhss_obs_count(void);

#endif // FHSS_HOP_H
