/**************************************************************************
 *{@C
 *      Copyright:      1988-2023 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         Utilities
 *      Filename:       UT_Swatch.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    The system clock is used to simulate a simple
 *                      stopwatch which runs in real time (not in process
 *                      virtual time). Do not rely too much on the precision
 *                      of the stopwatch at short intervals, since on some
 *                      machines the granularity of the system clock is up to
 *                      0.05 seconds. The clock's precision should get better
 *                      though, when longer time spans are measured.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      UT_GetWallClockTime
 *                      UT_GetSecondsSinceMidnight
 *                      UT_SwatchNew
 *                      UT_SwatchDelete
 *                      UT_SwatchStop
 *                      UT_SwatchRun
 *                      UT_SwatchReset
 *                      UT_SwatchLookup
 *
 **************************************************************************/

#include <stdio.h>

#include "UT_Compat.h"
#include "UT_Memory.h"

#include "UT_Swatch.h"

#if defined(OS_IS_SYS5_UNIX)
    #include <sys/types.h>
    #include <time.h>
    #include <sys/time.h>
    #include <sys/timeb.h>
#elif defined(OS_IS_WIN32)
    #define HIPERFTIMER
    #include <windows.h>
    #include <time.h>
    #include <sys/timeb.h>
#endif

typedef struct swatchStruct {
    Float64 numseconds;
    Float64 laststart;
    UT_Bool clockrunning;
#if defined(OS_IS_WIN32)
    LARGE_INTEGER ticksPerSecond;
#endif
} SwatchStruct;

/* Lookup a high resolution counter. */

static Float64 counterLookup (SwatchStruct *swatchId)
{
    #if defined(OS_IS_SYS5_UNIX)
        struct timeval tv;
        (void)gettimeofday (&tv, NULL);
        return (Float64)tv.tv_sec + (Float64)tv.tv_usec * 0.000001;
    #elif defined (OS_IS_WIN32)
        LARGE_INTEGER currentTime;
        QueryPerformanceCounter (&currentTime);
        return (Float64) currentTime.QuadPart /
               (Float64) swatchId->ticksPerSecond.QuadPart;
    #else
        #error "Code to read the system counter missing here"
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           UT_GetWallClockTime
 *
 *      Usage:          Get current wall clock time.
 *
 *      Synopsis:       Float64 UT_GetWallClockTime (void)
 *
 *      Description:    Get the current wall clock time. 
 *                      Resolution granularity is in milliseconds.
 *                      Unit of the returned time is in seconds.
 *
 *      Return value:   The time elapsed since the epoch.
 *                      Note that the epoch is defined differently on
 *                      Unix and Windows systems.
 *
 *      See also:       UT_GetSecondsSinceMidnight
 *
 ***************************************************************************/

Float64 UT_GetWallClockTime (void)
{
    struct timeb tb;
    ftime (&tb);
    return (Float64)tb.time + (Float64)tb.millitm * 0.001;
}

/***************************************************************************
 *[@e
 *      Name:           UT_GetSecondsSinceMidnight
 *
 *      Usage:          Get current time as seconds since midnight.
 *
 *      Synopsis:       Float64 UT_GetSecondsSinceMidnight (void)
 *
 *      Description:    Get current time as seconds since midnight. 
 *                      Resolution granularity is in milliseconds.
 *                      Unit of the returned time is in seconds.
 *
 *      Return value:   The time elapsed since midnight.
 *
 *      See also:       UT_GetWallClockTime
 *
 ***************************************************************************/

Float64 UT_GetSecondsSinceMidnight (void)
{
    struct tm   *lt;
    struct timeb tb;
    
    ftime (&tb);
    lt = localtime (&tb.time);
    return (Float64)(lt->tm_hour*3600 + lt->tm_min*60 + lt->tm_sec) + 
           (Float64)tb.millitm * 0.001;
}

/***************************************************************************
 *[@e
 *      Name:           UT_SwatchNew
 *
 *      Usage:          Create a new stopwatch.
 *
 *      Synopsis:       UT_Swatch UT_SwatchNew (void)
 *
 *      Description:    A new stopwatch is created. It's state is set to 
 *                      stopped and the time is set to 0.
 *
 *      Return value:   The new swatch identifier.
 *
 *      See also:       UT_SwatchDelete
 *
 ***************************************************************************/

UT_Swatch UT_SwatchNew (void)
{
    SwatchStruct *pSwatch;

    pSwatch = (SwatchStruct *) UT_MemPerm (SwatchStruct);
    if (!pSwatch) {
        return NULL;
    }
    pSwatch->clockrunning = UT_False;
    pSwatch->numseconds   = 0.0;
    pSwatch->laststart    = 0.0;
    #if defined (OS_IS_WIN32)
        QueryPerformanceFrequency (&pSwatch->ticksPerSecond);
    #endif
    return pSwatch;
}

/***************************************************************************
 *[@e
 *      Name:           UT_SwatchDelete
 *
 *      Usage:          Delete a stopwatch.
 *
 *      Synopsis:       void UT_SwatchDelete (UT_Swatch sw)
 *
 *      Description:    Delete the stop watch identified by "sw".
 *
 *      Return value:   None.
 *
 *      See also:       UT_SwatchNew
 *
 ***************************************************************************/

void UT_SwatchDelete (UT_Swatch sw)
{
    if (sw) {
        UT_MemFree (sw);
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_SwatchStop
 *
 *      Usage:          Stop a stopwatch.
 *
 *      Synopsis:       void UT_SwatchStop (UT_Swatch sw)
 *
 *      Description:    The stopwatch "sw" is stopped, but not reset.
 *
 *      Return value:   None.
 *
 *      See also:       UT_SwatchRun
 *                      UT_SwatchReset
 *                      UT_SwatchLookup
 *
 ***************************************************************************/

void UT_SwatchStop (UT_Swatch sw)
{
    if (sw->clockrunning) {
        sw->numseconds += counterLookup (sw) - sw->laststart;
        sw->clockrunning = UT_False;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_SwatchRun
 *
 *      Usage:          Start a stopwatch.
 *
 *      Synopsis:       void UT_SwatchRun (UT_Swatch sw)
 *
 *      Description:    The stopwatch "sw" continues to run. It is not reset.
 *
 *      Return value:   None.
 *
 *      See also:       UT_SwatchStop
 *                      UT_SwatchReset
 *                      UT_SwatchLookup
 *
 ***************************************************************************/

void UT_SwatchRun (UT_Swatch sw)
{
    if (!sw->clockrunning) {
        sw->laststart = counterLookup (sw);
        sw->clockrunning = UT_True;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_SwatchReset
 *
 *      Usage:          Reset a stopwatch.
 *
 *      Synopsis:       void UT_SwatchReset (UT_Swatch sw)
 *
 *      Description:    The stopwatch "sw" is reset to 0.0 seconds, but its
 *                      state (stopped or running) is not changed.
 *
 *      Return value:   None.
 *
 *      See also:       UT_SwatchStop
 *                      UT_SwatchRun
 *                      UT_SwatchLookup
 *
 ***************************************************************************/

void UT_SwatchReset (UT_Swatch sw)
{
    if (sw->clockrunning) {
        sw->laststart = counterLookup (sw);
    }
    sw->numseconds = 0.0;
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_SwatchLookup
 *
 *      Usage:          Read current time from a stopwatch.
 *
 *      Synopsis:       Float64 UT_SwatchLookup (const UT_Swatch sw)
 *
 *      Description:    The current time is read from stopwatch "sw".
 *
 *      Return value:   The number of seconds the stopwatch has been running
 *                      since the last call to UT_SwatchReset.
 *
 *      See also:       UT_SwatchStop
 *                      UT_SwatchRun
 *                      UT_SwatchReset
 *
 ***************************************************************************/

Float64 UT_SwatchLookup (const UT_Swatch sw)
{
    if (sw->clockrunning) {
        return (Float64)(sw->numseconds + counterLookup (sw) - sw->laststart);
    }
    return (Float64)sw->numseconds;
}
