/* Copyright (C) 2021-2022  Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include <igloo/tap.h>
#include <igloo/error.h>
#include <igloo/time.h>

static const time_t vec[] = {
    0, 1, -1, 2147483647, -2147483648
};

static const struct {
    uint64_t sec;
    uint32_t nsec;
} vec_interval[] = {
    {0, 0},
    {1, 0},
    {0, 1}
};

static void test_align(igloo_ctime_t t) {
    union {
        igloo_ctime_t ctime;
        void *vp;
    } ctime = {.ctime = t};

    igloo_tap_test("marked correctly as non-ro", ((uintmax_t)ctime.vp) & 0x1);
}

static void test(time_t t)
{
    igloo_ctime_t ctime;
    time_t r;

    igloo_tap_test_success("igloo_ctime_from_time_t", igloo_ctime_from_time_t(&ctime, t, igloo_CLOCK_REALTIME));
    igloo_tap_test("!igloo_ctime_is_null", !igloo_ctime_is_null(ctime));
    igloo_tap_test("!igloo_ctime_is_interval", !igloo_ctime_is_interval(ctime));
    igloo_tap_test("igloo_ctime_is_absolute", igloo_ctime_is_absolute(ctime));
    igloo_tap_test_success("igloo_ctime_to_time_t", igloo_ctime_to_time_t(&r, ctime));
    igloo_tap_test("t == r", t == r);
    test_align(ctime);
}

static void test_now(void)
{
    igloo_ctime_t ctime;
    time_t now = time(NULL);
    time_t r;

    igloo_tap_test_success("igloo_ctime_from_now", igloo_ctime_from_now(&ctime, igloo_CLOCK_REALTIME));
    igloo_tap_test_success("igloo_ctime_to_time_t", igloo_ctime_to_time_t(&r, ctime));
    igloo_tap_test("r == now", r == now || r == (now - 1));
    test_align(ctime);
}

static void test_interval(uint64_t sec, uint32_t nsec)
{
#ifdef __MINGW32__ /* There is no igloo_CLOCK_MONOTONIC on win* */
    static const igloo_clock_t clocks[] = {igloo_CLOCK_MOST_MONOTONIC,                        igloo_CLOCK_REALTIME};
#else
    static const igloo_clock_t clocks[] = {igloo_CLOCK_MOST_MONOTONIC, igloo_CLOCK_MONOTONIC, igloo_CLOCK_REALTIME};
#endif
    igloo_ctime_t ctime;
    time_t r;
    size_t i;

    for (i = 0; i < (sizeof(clocks)/sizeof(*clocks)); i++) {
        char buf[80];

        snprintf(buf, sizeof(buf), "igloo_ctime_from_interval(..., clock=%u)", (unsigned int)clocks[i]);

        igloo_tap_test_success(buf, igloo_ctime_from_interval(&ctime, sec, nsec, clocks[i]));
        igloo_tap_test("!igloo_ctime_is_null", !igloo_ctime_is_null(ctime));
        igloo_tap_test("igloo_ctime_is_interval", igloo_ctime_is_interval(ctime));
        igloo_tap_test("!igloo_ctime_is_absolute", !igloo_ctime_is_absolute(ctime));
        igloo_tap_test_success("igloo_ctime_to_time_t", igloo_ctime_to_time_t(&r, ctime));
        igloo_tap_test("sec == r", (time_t)sec == r);
        test_align(ctime);
        igloo_tap_test_success("igloo_ctime_sleep", igloo_ctime_sleep(ctime));
    }
}

static void test_null(void)
{
    igloo_ctime_t ctime;
    time_t r;

    igloo_tap_test_success("igloo_ctime_from_null", igloo_ctime_from_null(&ctime));
    igloo_tap_test("igloo_ctime_is_null", igloo_ctime_is_null(ctime));
    igloo_tap_test("!igloo_ctime_is_interval", !igloo_ctime_is_interval(ctime));
    igloo_tap_test("!igloo_ctime_is_absolute", !igloo_ctime_is_absolute(ctime));
    igloo_tap_test_error("igloo_ctime_to_time_t", igloo_ERROR_DOM, igloo_ctime_to_time_t(&r, ctime));
    test_align(ctime);
}

static void test_time_t_vectors(void)
{
    size_t i;

    for (i = 0; i < (sizeof(vec)/sizeof(*vec)); i++)
        test(vec[i]);
}

static void test_interval_vectors(void)
{
    size_t i;

    for (i = 0; i < (sizeof(vec_interval)/sizeof(*vec_interval)); i++)
        test_interval(vec_interval[i].sec, vec_interval[i].nsec);
}

int main(void)
{
    igloo_tap_init();
    igloo_tap_exit_on(igloo_TAP_EXIT_ON_FIN, NULL);

    igloo_tap_test("sizeof(igloo_ctime_t) == 16", sizeof(igloo_ctime_t) == 16);

    igloo_tap_group_run("null", test_null);
    igloo_tap_group_run("time_t vectors", test_time_t_vectors);
    igloo_tap_group_run("interval vectors", test_interval_vectors);
    igloo_tap_group_run("now", test_now);

    igloo_tap_fin();

    return EXIT_FAILURE; // return failure as we should never reach this point!
}
