#ifndef WFX_FEATURES_H
#define WFX_FEATURES_H

// ============================================================================
// WaveForge Expose — L0/L1 Feature Emitter (T1 Firmware)
//
// Computes per-cycle AMC feature vectors from the 128-bin RSSI spectrum proxy
// (WiFi 2.4 GHz) and from SX1262 RSSI/SNR observations (LoRa 915 MHz), then
// serialises them into a MSG_TYPE_WFX_FEATURES (0x10) LoRa packet so the T2
// gateway L1 estimator (waveforge_radar/expose/l1_estimator.py) can do
// cumulant-based modulation classification it cannot do without these vectors.
//
// Reference: Swami & Sadler 2000, "Hierarchical Digital Modulation
//            Classification Using Cumulants".
// ============================================================================

#include <cstdint>
#include <cstddef>
#include <cstdbool>

// ---------------------------------------------------------------------------
// Feature vector — wire format (packed, 60 bytes payload).
// Must match the Python dataclass WfxFeatureVec in l1_estimator.py.
// ---------------------------------------------------------------------------
struct WfxFeatureVec {
    uint32_t sensor_id;
    uint32_t timestamp_ms;

    // 2nd-order cumulants C20 and C21 (real only — spectrum proxy is real-valued)
    float c20_real, c20_imag;
    float c21;

    // 4th-order cumulants C40, C42 — primary modulation discriminators
    float c40_real, c40_imag;
    float c42;

    // RSSI statistics (rolling window, per band)
    float rssi_mean, rssi_std;

    // SNR statistics (rolling window, per band)
    float snr_mean, snr_std;

    // Inter-arrival jitter and duty cycle
    float ia_jitter_ms;   // standard deviation of inter-packet arrival times [ms]
    float occupancy;      // fraction of rolling window slots with a packet [0,1]

    // Which RF band this vector was computed over
    // 0 = 2.4 GHz WiFi (spectrum proxy via promiscuous RSSI)
    // 1 = 915 MHz LoRa (SX1262 RSSI/SNR per received packet)
    uint8_t  band_id;
    uint8_t  reserved[3];
} __attribute__((packed));

// Compile-time size assertion — keep in sync with Python side.
// Layout: 2×uint32 (8) + 12×float (48) + 4×uint8 (4) = 60 bytes.
// If this breaks, update the Python WfxFeatureVec dataclass accordingly.
static_assert(sizeof(WfxFeatureVec) == 60,
              "WfxFeatureVec size mismatch — update Python side");

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

// Call once in setup() to zero all internal state.
void wfx_features_init();

// Feed an IQ sample block (not available on bare ESP32 without a real SDR;
// reserved for future hardware upgrade — no-op in current implementation).
void wfx_features_observe_iq(const float* i_samples, const float* q_samples,
                              size_t n, uint8_t band_id);

// Feed one received-packet observation (RSSI + SNR + arrival timestamp).
// Call this after every successful mesh_receive() — see WFX_INTEGRATION_NOTES.md.
void wfx_features_observe_rssi_packet(int16_t rssi, int16_t snr,
                                      uint32_t ts_ms, uint8_t band_id);

// Compute a full feature vector from the current 128-bin spectrum proxy.
// Returns false if the rolling packet buffer is empty (no observations yet).
// Call this after capture_rf_spectrum() succeeds — see WFX_INTEGRATION_NOTES.md.
bool wfx_features_compute(const float* spectrum, size_t bins,
                          WfxFeatureVec* out);

// Serialise 'v' into a 64-byte MSG_TYPE_WFX_FEATURES LoRa packet and transmit
// via the existing mesh_publish_raw() call.  Returns false on TX failure.
bool wfx_features_publish(const WfxFeatureVec& v);

#endif // WFX_FEATURES_H
