/**
 * \addtogroup rig
 * @{
 */

/**
 * \file src/sleep.c
 * \brief Replacements for sleep and usleep
 * \author Michael Black W9MDB
 * \date 2020
 */
/*
 *  Hamlib Interface - Replacements for sleep and usleep
 *  Copyright (c) 2020 by Michael Black W9MDB
 *
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 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
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/**
 * \brief provide sleep and usleep replacements
 * \note parameters are same as man page for each
 *
 */
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include "hamlib/config.h"
#include "sleep.h"

#ifdef  __cplusplus
extern "C" {
#endif

extern double monotonic_seconds();

int hl_usleep(rig_useconds_t usec)
{
    double sleep_time = usec / 1e6;
    struct timespec tv1, tv2;
    double start_at = monotonic_seconds();
    double end_at = start_at + sleep_time;
    double delay = sleep_time;
    double lasterr = 0;

    if (sleep_time > .001) { delay -= .0001; }
    else if (sleep_time > .0001) { delay -= .00005; }

    tv1.tv_sec = (time_t) delay;
    tv1.tv_nsec = (long)((delay - tv1.tv_sec) * 1e+9);
    tv2.tv_sec = 0;
    tv2.tv_nsec = 10;
//    rig_debug(RIG_DEBUG_CACHE,"usec=%ld, sleep_time=%f, tv1=%ld,%ld\n", usec, sleep_time, (long)tv1.tv_sec,
//           (long)tv1.tv_nsec);

#ifdef __WIN32__
    timeBeginPeriod(1);
    nanosleep(&tv1, NULL);

    while ((lasterr = end_at - monotonic_seconds()) > 0)
    {
        nanosleep(&tv2, NULL);
    }

    timeEndPeriod(1);
#else
    nanosleep(&tv1, NULL);

    while (((lasterr = end_at - monotonic_seconds()) > 0))
    {
        nanosleep(&tv2, NULL);
    }

#endif
    return 0;
}

#if 0
// In order to stop the usleep warnings in cppcheck we provide our own interface
// So this will use system usleep or our usleep depending on availability of nanosleep
// This version of usleep can handle > 1000000 usec values
int hl_usleep(rig_useconds_t usec)
{
    int retval = 0;
    rig_debug(RIG_DEBUG_ERR, "%s: usec=%ld\n", __func__, usec);

    if (usec <= 1000) { return 0; } // dont' sleep if only 1ms is requested -- speeds things up on Windows

    while (usec > 1000000)
    {
        if (retval != 0) { return retval; }

        retval = usleep(1000000);
        usec -= 1000000;
    }

#ifdef HAVE_NANOSLEEP
    struct timespec t, tleft;
    t.tv_sec = usec / 1e6;
    t.tv_nsec = (usec - (t.tv_sec * 1e6)) * 1e3;
    return nanosleep(&t, &tleft);
#else
    return usleep(usec);
#endif
}
#endif


#ifdef HAVE_NANOSLEEP
#ifndef HAVE_SLEEP
/**
 * \brief sleep
 * \param secs is seconds to sleep
 */
unsigned int sleep(unsigned int secs)
{
    int retval;
    struct timespec t;
    struct timespec tleft;
    t.tv_sec = secs;
    t.tv_nsec = 0;
    retval = nanosleep(&t, &tleft);

    if (retval == -1) { return tleft.tv_sec; }

    return 0;
}
#endif


#if 0
/**
 * \brief microsecond sleep
 * \param usec is microseconds to sleep
 * This does not have the same 1000000 limit as POSIX usleep
 */
int usleep(rig_useconds_t usec)
{
    int retval;
    unsigned long sec = usec / 1000000ul;
    unsigned long nsec = usec * 1000ul - (sec * 1000000000ul);
    struct timespec t;
    t.tv_sec = sec;
    t.tv_nsec = nsec;
    retval = nanosleep(&t, NULL);

    // EINTR is the only error return usleep should need
    // since EINVAL should not be a problem
    if (retval == -1) { return EINTR; }

    return 0;
}
#endif

#endif // HAVE_NANOSLEEP
#ifdef __cplusplus
}
#endif
/** @} */

#ifdef TEST
#include "misc.h"
double get_elapsed_time(struct tm start, struct tm end) {
    // Convert struct tm to time_t
    time_t start_seconds = mktime(&start);
    time_t end_seconds = mktime(&end);

    double elapsed_time = difftime(end_seconds, start_seconds);
    return elapsed_time;
}


int main()
{
    struct tm start_time, end_time;
    time_t rawtime;

    for (int i = 0; i < 11; ++i)
    {
        char buf[256];
        time(&rawtime);
        hl_usleep(1000000 * 1000); // test 1s sleep
        date_strget(buf, sizeof(buf), 0);
        printf("%s\n", buf);
        time(&rawtime);
    }

    return 0;
}
#endif
