#ifndef GPS_MODULE_H
#define GPS_MODULE_H

// ============================================================================
// gps_module.h — Quectel L76K GNSS (Heltec's official GPS add-on for the
// WiFi LoRa 32 V3/V4 family)
//
// Wired to this board's dedicated GNSS header (Heltec V4: "1.25-8P header
// for Quectel L76K + accelerometer"): UART1, GPIO39 = RX (ESP32 receives,
// wired to the L76K's TXD), GPIO38 = TX (ESP32 transmits, wired to the
// L76K's RXD), GPIO34 = GPS_EN held LOW to power the module, 9600 baud,
// standard NMEA 0183 output (multi-constellation $GN* / legacy $GP*
// sentences). Parsed with TinyGPS++.
//
// CONFIRMED 2026-07-26 via direct multimeter continuity from the JST-SH
// connector's individual wires to labeled board pads, then verified live:
// sustained real GPS UART traffic (thousands of bytes, ~2 checksum failures)
// on the very first attempt at this pairing. Everything tried before this —
// GPIO33/34 (the generic "Heltec V3 GPS" pins cited in most online guides),
// GPIO48/47 (per two independent Heltec/Meshtastic wiring references) —
// produced zero real traffic; those were reasonable guesses from
// documentation, but simply weren't correct for this unit's actual GNSS
// header wiring. A different Heltec V3/V4 unit could legitimately be wired
// differently — see the scanner fallback list in gps_module.cpp.
//
// This does NOT replace sensor_location.{h,cpp}'s NVS-persisted static
// position — that remains the fallback for when no live fix is available
// (cold start, indoors, module not yet acquired lock). location_get() now
// prefers a live GPS fix when one exists, falling back to the static value
// otherwise. See sensor_location.cpp.
// ============================================================================

#include <cstdint>
#include <cstddef>

// Initialize the GPS UART + NMEA parser. Call once from setup().
void gps_init();

// Feed any bytes that have arrived on the GPS UART into the parser. Call
// every main-loop tick — non-blocking, processes only what's already
// buffered.
void gps_tick();

// True once a valid GNSS fix has been acquired and is still fresh (see
// GPS_FIX_STALE_MS in gps_module.cpp).
bool gps_has_fix();

// ---------------------------------------------------------------------------
// Bench aid: force a specific RX/TX pair and baud immediately.
//
// The autoscan sweeps 28 candidate pairs at 4 s each (~112 s per full cycle)
// and you cannot choose which it tries — fine for a deployed unit finding a
// known-good wiring, painful when bench-testing an unknown one. This opens the
// requested pins right now and cancels any in-progress scan, so a wiring guess
// can be tested in seconds instead of minutes.
//
// Also accepts an explicit baud: gps_chars_processed counts RAW bytes before
// parsing, so a deliberately wrong baud still produces a climbing byte count.
// That is the useful "garbage reading" — it proves the electrical path works,
// independent of whether the data is intelligible yet.
//
// Returns false if either pin is outside the valid ESP32-S3 GPIO range.
// ---------------------------------------------------------------------------
bool gps_force_pins(int rx_pin, int tx_pin, uint32_t baud);

// Reset the byte counter — lets each bench attempt be measured from zero
// rather than against an accumulating total.
void gps_reset_stats();

// Loopback self-test. With a jumper from the configured TX pin to the RX pin,
// writes a known pattern and reads it back. Bytes returned = the UART, pin
// routing and read loop are all functional and the fault is external; nothing
// returned = the fault is in this firmware's UART setup.
bool gps_loopback_test(unsigned long* out_sent, unsigned long* out_received);

// ---------------------------------------------------------------------------
// Which pins is the UART ACTUALLY open on right now, and is the scan locked?
//
// Added because diagnosing a GPS on a wireless unit degenerated into arguing
// about pin diagrams: the wiring measured correct, voltages measured correct,
// and the byte count stayed 0, with no way to confirm what the firmware
// believed it was listening to without a USB cable. The scan log prints it
// every 4 s but is unreachable on a battery unit across the site.
//
// Surfaced in /status so the answer is observable, not inferred.
// ---------------------------------------------------------------------------
void gps_get_active_pins(int* rx, int* tx, bool* locked, int* candidate_idx,
                         int* candidate_count);

// Current fix as fixed-point coordinates (degrees * 1e7), matching
// DetectionEvent's lat_fixed/lon_fixed scale. Only meaningful if
// gps_has_fix() is true; both are set to 0 otherwise.
void gps_get_location(int32_t* lat_fixed, int32_t* lon_fixed);

// Real UTC epoch seconds derived from the GPS date+time, or 0 if unavailable
// (no valid GPS date/time yet). Used as a wall-clock source on units with no
// mesh time-master so detection timestamps land under the correct server day.
uint32_t gps_get_utc_epoch();

// ---------------------------------------------------------------------------
// Raw NMEA tap — the last few complete sentences, exactly as received.
//
// Added because the parsed counters cannot distinguish the two failure modes
// that matter in the field, and they look identical from /status:
//
//   (a) no RF reaching the antenna  -> GSV reports 0 satellites IN VIEW
//   (b) RF fine, fix not yet built  -> GSV lists satellites with real C/N0
//                                      values while GGA quality is still 0
//
// Both show satellites=0 and has_fix=false. Only the raw sentences separate
// them, and on a wireless unit there is no cable to watch them on. TDeck02 sat
// at 0 satellites with 200 bytes/s of live NMEA and the antenna was inspected
// by hand on the strength of a guess, which this makes unnecessary.
//
// Copies newline-separated sentences into out (always NUL-terminated) and
// returns the number of bytes written, excluding the terminator.
// ---------------------------------------------------------------------------
size_t gps_get_recent_nmea(char* out, size_t out_len);

// ---------------------------------------------------------------------------
// The baud rate the GPS UART is ACTUALLY clocked at right now, or 0 before
// gps_init().
//
// Not cosmetic. The T-Deck Plus ships with either a 9600-baud Quectel L76K or
// a 38400-baud u-blox MIA-M10Q on the same pins, so the correct baud is a
// per-unit runtime property that cannot be read off the board. Surfaced in
// /status so "which module is fitted" is answerable without a cable.
// ---------------------------------------------------------------------------
uint32_t gps_get_active_baud();

struct GpsStats {
    uint32_t chars_processed;
    uint32_t sentences_with_fix;
    uint32_t checksum_failures;
    bool     has_fix;
    float    hdop;
    uint8_t  satellites;
};

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

#endif // GPS_MODULE_H
