/* gweather-private.c - Overall weather server functions
 *
 * SPDX-FileCopyrightText: The GWeather authors
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include "gweather-private.h"

/* sign, 3 digits, separator, 4 decimals, nul-char */
#define DEGREES_STR_SIZE (1 + 3 + 1 + 4 + 1)

static GWeatherDb *world_db;

void
_gweather_location_reset_world (void)
{
    guint i;

    g_return_if_fail (world_db != NULL);

    /* At this point, we had a leak if the caches are not completely empty. */
    for (i = 0; i < world_db->locations->len; i++) {
        if (G_UNLIKELY (g_ptr_array_index (world_db->locations, i) != NULL)) {
            g_warning ("Location with index %u and name %s is still referenced!",
                       i,
                       gweather_location_get_name (g_ptr_array_index (world_db->locations, i)));
            g_assert_not_reached ();
        }
    }
    for (i = 0; i < world_db->timezones->len; i++) {
        if (G_UNLIKELY (g_ptr_array_index (world_db->timezones, i) != NULL)) {
            g_warning ("Timezone with index %u and tzid %s is still referenced!",
                       i,
                       g_time_zone_get_identifier (g_ptr_array_index (world_db->timezones, i)));
            g_assert_not_reached ();
        }
    }
}

static gpointer
ensure_world (gpointer dummy G_GNUC_UNUSED)
{
    g_autoptr (GError) error = NULL;
    g_autofree char *filename = NULL;
    g_autoptr (GMappedFile) map = NULL;
    const char *locations_path;
    GWeatherDb *db = NULL;

    locations_path = g_getenv ("LIBGWEATHER_LOCATIONS_PATH");
    if (locations_path != NULL) {
        filename = g_strdup (locations_path);
        if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
            g_warning ("User specified database %s does not exist", filename);
            g_clear_pointer (&filename, g_free);
        }
    }

    if (filename == NULL) {
        filename = g_build_filename (GWEATHER_BIN_LOCATION_DIR, "Locations.bin", NULL);
    }

    map = g_mapped_file_new (filename, FALSE, &error);
    if (map == NULL) {
        g_critical ("Failed to open database %s: %s", filename, error->message);
        return NULL;
    }

    db = g_new0 (GWeatherDb, 1);
    db->world = db_world_from_data (g_mapped_file_get_contents (map), g_mapped_file_get_length (map));
    /* This is GWthDB01 */
    if (db_world_get_magic (db->world) != 0x5747687442443130) {
        g_free (db);
        return NULL;
    }

    db->map = g_steal_pointer (&map);

    db->locations_ref = db_world_get_locations (db->world);
    db->timezones_ref = db_world_get_timezones (db->world);

    db->locations = g_ptr_array_new ();
    db->timezones = g_ptr_array_new ();

    g_ptr_array_set_size (db->locations, db_arrayof_location_get_length (db->locations_ref));
    g_ptr_array_set_size (db->timezones, db_world_timezones_get_length (db->timezones_ref));

    world_db = db;

    return db;
}

/*< private >
 * gweather_location_ensure_world:
 *
 * Ensures that the locations database is available.
 */
void
gweather_location_ensure_world (void)
{
    static GOnce ensure_world_once = G_ONCE_INIT;

    g_once (&ensure_world_once, ensure_world, NULL);
}

/*< private >
 * gweather_get_db:
 *
 * Retrieves a pointer to the locations database.
 *
 * Returns: (transfer none): the root of the locations database
 */
GWeatherDb *
gweather_get_db (void)
{
    gweather_location_ensure_world ();

    return world_db;
}

char *
_radians_to_degrees_str (gdouble radians)
{
    char *str;
    double degrees;

    str = g_malloc0 (DEGREES_STR_SIZE);
    /* Max 4 decimals */
    degrees = (double) ((int) (RADIANS_TO_DEGREES (radians) * 10000)) / 10000;
    /* Too many digits */
    g_return_val_if_fail (degrees <= 1000 || degrees >= -1000, NULL);
    return g_ascii_formatd (str, G_ASCII_DTOSTR_BUF_SIZE, "%g", degrees);
}
