/*  =========================================================================
    zclock - millisecond clocks and delays

    Copyright (c) the Contributors as noted in the AUTHORS file.
    This file is part of CZMQ, the high-level C binding for 0MQ:
    http://czmq.zeromq.org.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.
    =========================================================================
*/
/*
@header
    The zclock class provides essential sleep and system time functions,
    used to slow down threads for testing, and calculate timers for polling.
    Wraps the non-portable system calls in a simple portable API.
@discuss
    The Win32 Sleep() call defaults to 16ms resolution unless the system
    timer resolution is increased with a call to timeBeginPeriod() permitting
    1ms granularity.
@end
*/

#include "czmq_classes.h"

#if defined (__WINDOWS__)
//  --------------------------------------------------------------------------
//  Convert FILETIME "Contains a 64-bit value representing the number of
//  100-nanosecond intervals since January 1, 1601 (UTC)."

static int64_t
s_filetime_to_msec (const FILETIME *ft)
{
    //  As per Windows documentation for FILETIME, copy the resulting FILETIME
    //  structure to a ULARGE_INTEGER structure using memcpy (using memcpy
    //  instead of direct assignment can prevent alignment faults on 64-bit
    //  Windows).
    ULARGE_INTEGER dateTime;
    memcpy (&dateTime, ft, sizeof (dateTime));

    return (int64_t) (dateTime.QuadPart / 10000);
}

#endif


//  --------------------------------------------------------------------------
//  Sleep for a number of milliseconds

void
zclock_sleep (int msecs)
{
#if defined (__UNIX__)
    struct timespec t;
    t.tv_sec = msecs / 1000;
    t.tv_nsec = (msecs % 1000) * 1000000;
    nanosleep (&t, NULL);
#elif (defined (__WINDOWS__))
    //  Windows XP/2000:  A value of zero causes the thread to relinquish the
    //  remainder of its time slice to any other thread of equal priority that
    //  is ready to run. If there are no other threads of equal priority ready
    //  to run, the function returns immediately, and the thread continues
    //  execution. This behavior changed starting with Windows Server 2003.

#   if defined (NTDDI_VERSION) && defined (NTDDI_WS03) && (NTDDI_VERSION >= NTDDI_WS03)
    Sleep (msecs);
#   else
    if (msecs > 0)
        Sleep (msecs);
#   endif
#endif
}


//  --------------------------------------------------------------------------
//  Return current system clock as milliseconds. Note that this clock can
//  jump backwards (if the system clock is changed) so is unsafe to use for
//  timers and time offsets. Use zclock_mono for that instead.

int64_t
zclock_time (void)
{
#if defined (__UNIX__)
    struct timeval tv;
    gettimeofday (&tv, NULL);

    return (int64_t) ((int64_t) tv.tv_sec * 1000 + (int64_t) tv.tv_usec / 1000);
#elif (defined (__WINDOWS__))
    FILETIME ft;
    GetSystemTimeAsFileTime (&ft);
    return s_filetime_to_msec (&ft);
#endif
}


//  --------------------------------------------------------------------------
//  Return current monotonic clock in milliseconds. Use this when you compute
//  time offsets. The monotonic clock is not affected by system changes and
//  so will never be reset backwards, unlike a system clock.

int64_t
zclock_mono (void)
{
#if (defined (__UTYPE_OSX) || defined (__UTYPE_IOS))
    clock_serv_t cclock;
    mach_timespec_t mts;
    host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &cclock);
    clock_get_time (cclock, &mts);
    mach_port_deallocate (mach_task_self (), cclock);
    return (int64_t) ((int64_t) mts.tv_sec * 1000 + (int64_t) mts.tv_nsec / 1000000);
    
#elif defined (__UNIX__)
    struct timespec ts;
    clock_gettime (CLOCK_MONOTONIC, &ts);
    return (int64_t) ((int64_t) ts.tv_sec * 1000 + (int64_t) ts.tv_nsec / 1000000);
    
#elif (defined (__WINDOWS__))
    //  System frequency does not change at run-time, cache it
    static int64_t frequency = 0;
    if (frequency == 0) {
        LARGE_INTEGER freq;
        QueryPerformanceFrequency (&freq);
        // Windows documentation says that XP and later will always return non-zero
        assert (freq.QuadPart != 0);
        frequency = freq.QuadPart;
    }
    LARGE_INTEGER count;
    QueryPerformanceCounter (&count);
    return (int64_t) (count.QuadPart * 1000) / frequency;
#endif
}


//  --------------------------------------------------------------------------
//  Return current monotonic clock in microseconds. Use this when you compute
//  time offsets. The monotonic clock is not affected by system changes and
//  so will never be reset backwards, unlike a system clock.

int64_t
zclock_usecs (void)
{
#if (defined (__UTYPE_OSX) || defined (__UTYPE_IOS))
    clock_serv_t cclock;
    mach_timespec_t mts;
    host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &cclock);
    clock_get_time (cclock, &mts);
    mach_port_deallocate (mach_task_self (), cclock);
    return (int64_t) ((int64_t) mts.tv_sec * 1000000 + (int64_t) mts.tv_nsec / 1000);

#elif defined (__UNIX__)
    struct timespec ts;
    clock_gettime (CLOCK_MONOTONIC, &ts);
    return (int64_t) ((int64_t) ts.tv_sec * 1000000 + (int64_t) ts.tv_nsec / 1000);

#elif (defined (__WINDOWS__))
    //  System frequency does not change at run-time, cache it
    static int64_t frequency = 0;
    if (frequency == 0) {
        LARGE_INTEGER freq;
        QueryPerformanceFrequency (&freq);
        // Windows documentation says that XP and later will always return non-zero
        assert (freq.QuadPart != 0);
        frequency = freq.QuadPart;
    }
    LARGE_INTEGER count;
    QueryPerformanceCounter (&count);
    return (int64_t) (count.QuadPart * 1000000) / frequency;
#endif
}


//  --------------------------------------------------------------------------
//  Return formatted date/time as fresh string. Free using zstr_free().

char *
zclock_timestr (void)
{
    time_t curtime = time (NULL);
    struct tm *loctime = localtime (&curtime);
    char formatted [20];
    strftime (formatted, 20, "%Y-%m-%d %H:%M:%S", loctime);
    return strdup (formatted);
}


//  --------------------------------------------------------------------------
//  DEPRECATED in favor of zsys logging, see issue #519
//  Print formatted string to stdout, prefixed by date/time and
//  terminated with a newline.

void
zclock_log (const char *format, ...)
{
    time_t curtime = time (NULL);
    struct tm *loctime = localtime (&curtime);
    char formatted [20];
    strftime (formatted, 20, "%y-%m-%d %H:%M:%S ", loctime);
    printf ("%s", formatted);

    va_list argptr;
    va_start (argptr, format);
    vprintf (format, argptr);
    va_end (argptr);
    printf ("\n");
    fflush (stdout);
}


//  --------------------------------------------------------------------------
//  Self test of this class
void
zclock_test (bool verbose)
{
    printf (" * zclock: ");

    //  @selftest
    int64_t start = zclock_time ();
    zclock_sleep (10);
    assert ((zclock_time () - start) >= 10);
    start = zclock_mono ();
    int64_t usecs = zclock_usecs ();
    zclock_sleep (10);
    assert ((zclock_mono () - start) >= 10);
    assert ((zclock_usecs () - usecs) >= 10000);
    char *timestr = zclock_timestr ();
    if (verbose)
        puts (timestr);
    freen (timestr);

#if defined (__WINDOWS__)
    zsys_shutdown();
#endif
    //  @end

    printf ("OK\n");
}
