#ifndef BLE_ODID_SCANNER_H
#define BLE_ODID_SCANNER_H

// ============================================================================
// ble_odid_scanner.h — BLE 5 LE ASTM F3411 OpenDroneID scanner for T1
//
// Ported from PDL Witness's ble_droneid.c (same parsing logic — Service Data
// UUID 0xFFFA / 128-bit UUID / DJI manufacturer data) and adapted to attach
// a passive scan onto the NimBLE host T1 already brings up for
// ble_companion.cpp, instead of bringing up its own host.
//
// This closes the gap where T1 (despite being named "RID Sensor") had no
// code path to actually decode a drone's Remote ID broadcast — the existing
// TFLite drone_classifier only classifies RF spectral shape (OcuSync/FHSS/
// WiFi/Bluetooth energy signatures), and network_id.cpp only catalogs
// generic WiFi APs. Neither reads a Basic ID serial, GPS position, or
// operator location out of an actual ODID message.
//
// The pure parsing functions below have zero Arduino/NimBLE dependency and
// are unit-tested in the native env (see test/test_ble_odid/). The NimBLE
// integration (ble_odid_scanner_init) is gated by PDL_BLE_COMPANION_ENABLED
// because it requires NimBLEDevice to already be initialized by
// ble_companion_init() — this module does not call NimBLEDevice::init()
// itself, since only one module may own host bring-up.
// ============================================================================

#include <cstdint>
#include <cstddef>

// Vendor classes — mirrors PDL Witness's WiFi/BLE convention so both
// transports can feed the same downstream tracker/report format.
enum OdidVendorClass : uint8_t {
    ODID_VENDOR_UNKNOWN = 0,
    ODID_VENDOR_DJI     = 1,
    ODID_VENDOR_AUTEL   = 2,
    ODID_VENDOR_SKYDIO  = 3,
    ODID_VENDOR_PARROT  = 4,
};

// Decoded ASTM F3411 Location message (message_type 0x1) fields.
struct OdidLocation {
    bool     valid;
    double   lat_deg;
    double   lon_deg;
    float    alt_m;         // geodetic altitude, meters
    float    speed_ms;
    uint16_t heading_deg;
};

// Result of parsing one BLE advertisement payload.
struct OdidParseResult {
    bool         hit;            // true if this advert looked like an ODID/RID broadcast
    uint8_t      vendor;         // OdidVendorClass
    bool         has_serial;     // true if a Basic ID serial string was extracted
    char         serial[21];     // ASCII serial, NUL-terminated (ASTM F3411 max 20 chars)
    OdidLocation location;       // location.valid=false if no Location message in this advert
    int32_t      company_id;     // -1 if no manufacturer-specific data present
};

// ---------------------------------------------------------------------------
// Pure parsing — no BLE-stack dependency, safe to call from native tests.
// ---------------------------------------------------------------------------

// Walk the AD-structure list of one BLE advertisement payload (as returned
// by NimBLEAdvertisedDevice::getPayload()/getPayloadLength(), or the raw
// event->disc.data buffer on ESP-IDF native NimBLE) looking for a
// Service-Data UUID 0xFFFA block (BT4 legacy form), a 128-bit Service-Data
// block shaped like an ODID message (BT5 extended form), or DJI
// manufacturer-specific data. Classifies vendor and extracts a Basic ID
// serial / Location fix when the message type present allows it.
OdidParseResult odid_parse_advert(const uint8_t* data, size_t len);

// Decode an ASTM F3411 Location message body (message_type == 0x1) into
// lat/lon/alt/speed/heading. body must point at the message header byte
// (the first byte of the 25-byte Location message). Returns false if
// body_len < 25 or the header's message type isn't Location.
bool odid_decode_location(const uint8_t* body, size_t body_len, OdidLocation* out);

// Extract a Basic ID serial (message_type == 0x0) into out[21] (20 chars +
// NUL). Returns false if odid_len < 22 or the header's message type isn't
// Basic ID. Trailing NUL/space padding is trimmed.
bool odid_extract_basic_id_serial(const uint8_t* odid, size_t odid_len, char out[21]);

// ---------------------------------------------------------------------------
// NimBLE-Arduino integration (real hardware only).
// ---------------------------------------------------------------------------
#ifdef PDL_BLE_COMPANION_ENABLED

// Start passive BT5 LE scanning for OpenDroneID broadcasts. MUST be called
// AFTER ble_companion_init() (NimBLEDevice::init() must already have run —
// this module attaches a scan to the existing host, it does not bring one
// up). Safe to call once; a second call is a no-op.
void ble_odid_scanner_init();

struct OdidScannerStats {
    uint32_t adverts_seen;
    uint32_t rid_hits;
    uint32_t location_hits;
    uint32_t blue_force_matches;
};

// Snapshot module stats. out must be non-null.
void ble_odid_scanner_get_stats(OdidScannerStats* out);

// Test-only injection: feeds a synthetic BLE advert payload through the
// exact same parse -> classify -> blue-force -> FFPS pipeline a real
// over-the-air advert would take. Returns true if it parsed as an ODID hit.
// Used by the on-device serial test command (see main.cpp), not by any
// production code path.
bool ble_odid_scanner_test_inject(const uint8_t* payload, size_t len, int8_t rssi);

#endif // PDL_BLE_COMPANION_ENABLED

#endif // BLE_ODID_SCANNER_H
