#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>

#include "timeutil.h"

typedef int (*timeutil_timer_callback) (void *);

struct timer_thread_context {
    struct timespec spec;
    timeutil_timer_callback callback;
    void *user;
};

static void
timer_thread_sighandler (int signo)
{
    pthread_exit (NULL);
}

static void *
timer_thread (void *arg)
{
    // copy our context to a local variable 
    struct timer_thread_context ttc = *((struct timer_thread_context*) arg);

    signal (SIGUSR1, timer_thread_sighandler);
    while (ttc.callback (ttc.user)) {
        struct timespec req=ttc.spec, rem;
        timeutil_nanosleep_fully (&req, &rem);
    }
    pthread_exit (0);
}

timeutil_timer_t
timeutil_timer_create (struct timespec spec, int (*callback) (void *), void *user)
{
    struct timer_thread_context ttc = {
        .spec = spec,
        .callback = callback,
        .user = user,
    };

    timeutil_timer_t timer;
    pthread_create (&timer.th, NULL, timer_thread, &ttc);
    pthread_detach (timer.th);

    return timer;
}


void
timeutil_timer_destroy (timeutil_timer_t *timer)
{
    pthread_kill (timer->th, SIGUSR1);
}


struct timespec
timeutil_hz_to_timespec (double hz)
{
    double dt = 1.0 / hz;
    struct timespec spec;
    spec.tv_sec  = dt;
    spec.tv_nsec = (dt - spec.tv_sec)*1E9;
    return spec;
}

int
timeutil_nanosleep_fully (const struct timespec *req, struct timespec *rem)
{
    struct timespec temp_rem;
    if (nanosleep (req, rem) == -1)
        if (errno == EINTR)
            return timeutil_nanosleep_fully (rem, &temp_rem);
        else
            return -1;
    else
        return 0;
}

int
timeutil_nsleep (int64_t nsec)
{
    struct timespec rem;
    struct timespec req = {
        .tv_sec = nsec / 1000000000,
        .tv_nsec = nsec % 1000000000,
    };
    return timeutil_nanosleep_fully (&req, &rem);
}
