#include <time.h>

#include "macro.h"
#include "times.h"

// 1  s = 1000 ms
// 1 ms = 1000 us
// 1 us = 1000 ns

/*
 * The maximum number of seconds that can be stored in the time_t value is 2147483647 –- a little over 68 years.
 *
 * struct timespec
 * {
 *     time_t tv_sec; // seconds
 *     long tv_nsec;  // nanoseconds
 * };
 */

struct
{
    struct timespec real_time_spec;
    uint64_t real_time_msec;
    uint64_t real_time_sec;

    struct timespec monotonic_time_spec;
    uint64_t monotonic_time_msec;
    uint64_t monotonic_time_sec;
} global_time;

void stellar_update_time_cache()
{
    uint64_t time_msec;
    uint64_t time_sec;
    /*
     * CLOCK_MONOTONIC
     *
     * On Linux, that point corresponds to the number of sec‐
     * onds that the system has been running since it was booted.
     *
     * The CLOCK_MONOTONIC clock is not affected by discontinuous
     * jumps in the system time (e.g., if the system administrator
     * manually changes the clock), but is affected by the incremen‐
     * tal adjustments performed by adjtime(3) and NTP.
     */
    clock_gettime(CLOCK_MONOTONIC, &global_time.monotonic_time_spec);

    time_msec = global_time.monotonic_time_spec.tv_sec * 1000 +
                global_time.monotonic_time_spec.tv_nsec / 1000000;
    time_sec = global_time.monotonic_time_spec.tv_sec +
               global_time.monotonic_time_spec.tv_nsec / 1000000000;

    ATOMIC_SET(&global_time.monotonic_time_msec, time_msec);
    ATOMIC_SET(&global_time.monotonic_time_sec, time_sec);

    clock_gettime(CLOCK_REALTIME, &global_time.real_time_spec);

    time_msec = global_time.real_time_spec.tv_sec * 1000 +
                global_time.real_time_spec.tv_nsec / 1000000;
    time_sec = global_time.real_time_spec.tv_sec +
               global_time.real_time_spec.tv_nsec / 1000000000;

    ATOMIC_SET(&global_time.real_time_msec, time_msec);
    ATOMIC_SET(&global_time.real_time_sec, time_sec);
}

uint64_t stellar_get_monotonic_time_sec()
{
    return ATOMIC_READ(&global_time.monotonic_time_sec);
}

uint64_t stellar_get_monotonic_time_msec()
{
    return ATOMIC_READ(&global_time.monotonic_time_msec);
}

uint64_t stellar_get_real_time_sec()
{
    return ATOMIC_READ(&global_time.real_time_sec);
}

uint64_t stellar_get_real_time_msec()
{
    return ATOMIC_READ(&global_time.real_time_msec);
}
