/**************************************************************************
 *{@C
 *	Copyright:	1988-2020 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;
}
