// ============================================================================
// sensor_location.cpp — Fixed lat/lon persistence via NVS
// ============================================================================

#include "sensor_location.h"
#include "gps_module.h"

// ---------------------------------------------------------------------------
// Platform-specific NVS layer
// ---------------------------------------------------------------------------
#ifdef ARDUINO
#include <nvs_flash.h>
#include <nvs.h>

static nvs_handle_t s_nvs_handle = 0;
static bool         s_nvs_open   = false;
#endif // ARDUINO

// ---------------------------------------------------------------------------
// In-RAM shadow — valid regardless of platform
// ---------------------------------------------------------------------------
static int32_t s_lat = 0;
static int32_t s_lon = 0;

// NVS key names (max 15 chars per ESP-IDF requirement)
static const char* NVS_NAMESPACE = "pdl_loc";
static const char* NVS_KEY_LAT   = "lat";
static const char* NVS_KEY_LON   = "lon";

// ---------------------------------------------------------------------------
bool location_init() {
#ifdef ARDUINO
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES ||
        err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        // NVS partition was truncated or version-changed — erase and reinit.
        nvs_flash_erase();
        err = nvs_flash_init();
    }
    if (err != ESP_OK) {
        return false;
    }

    err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &s_nvs_handle);
    if (err != ESP_OK) {
        return false;
    }
    s_nvs_open = true;

    // Load persisted values into the RAM shadow; silently keep zero on ENOENT
    int32_t lat_tmp = 0;
    int32_t lon_tmp = 0;
    nvs_get_i32(s_nvs_handle, NVS_KEY_LAT, &lat_tmp);
    nvs_get_i32(s_nvs_handle, NVS_KEY_LON, &lon_tmp);
    s_lat = lat_tmp;
    s_lon = lon_tmp;

    return true;
#else
    // Native test environment: nothing to open, shadow already zero-init
    return true;
#endif
}

// ---------------------------------------------------------------------------
bool location_set(int32_t lat_fixed, int32_t lon_fixed) {
    // Update RAM shadow first so reads are always consistent
    s_lat = lat_fixed;
    s_lon = lon_fixed;

#ifdef ARDUINO
    if (!s_nvs_open) {
        return false;
    }
    esp_err_t err_lat = nvs_set_i32(s_nvs_handle, NVS_KEY_LAT, lat_fixed);
    esp_err_t err_lon = nvs_set_i32(s_nvs_handle, NVS_KEY_LON, lon_fixed);
    if (err_lat != ESP_OK || err_lon != ESP_OK) {
        return false;
    }
    esp_err_t err_commit = nvs_commit(s_nvs_handle);
    return (err_commit == ESP_OK);
#else
    return true;
#endif
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
static bool coords_usable(int32_t lat_fixed, int32_t lon_fixed) {
    // Both-zero means "unset", which is a legitimate state for a GPS-less unit
    // that has never been given a position — but it is not a POSITION, so it
    // is not usable for a report.
    if (lat_fixed == 0 && lon_fixed == 0) return false;
    if (lat_fixed >  900000000L || lat_fixed <  -900000000L) return false;
    if (lon_fixed > 1800000000L || lon_fixed < -1800000000L) return false;
    return true;
}

bool location_resolve_report(int32_t evt_lat_fixed, int32_t evt_lon_fixed,
                             double* out_lat, double* out_lon) {
    if (!out_lat || !out_lon) return false;

    // 1. The event already carries a real position (decoded drone position,
    //    or a relayed peer's own position).
    if (coords_usable(evt_lat_fixed, evt_lon_fixed)) {
        *out_lat = (double)evt_lat_fixed / 1e7;
        *out_lon = (double)evt_lon_fixed / 1e7;
        return true;
    }

    // 2. Fall back to this unit's own position — live GPS if held, else the
    //    SET_LOCATION static value.
    int32_t slat = 0, slon = 0;
    location_get(&slat, &slon);
    if (coords_usable(slat, slon)) {
        *out_lat = (double)slat / 1e7;
        *out_lon = (double)slon / 1e7;
        return true;
    }

    // Neither available: this unit genuinely does not know where it is.
    return false;
}

// ---------------------------------------------------------------------------
void location_get(int32_t* lat, int32_t* lon) {
    if (gps_has_fix()) {
        gps_get_location(lat, lon);
        return;
    }
    // No live fix (module still acquiring, none wired on this unit, or the
    // last fix went stale) — fall back to the static NVS-persisted value.
    if (lat) *lat = s_lat;
    if (lon) *lon = s_lon;
}
