/******************************************************************************
 *{@C
 *      Copyright:      2005-2016 Paul Obermeier (obermeier@tcl3d.org)
 *
 *                      See the file "Tcl3D_License.txt" for information on
 *                      usage and redistribution of this file, and for a
 *                      DISCLAIMER OF ALL WARRANTIES.
 *
 *      Module:         Tcl3D -> tcl3dOgl
 *      Filename:       tcl3dUtilStopWatch.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    The system clock is used to simulate a simple
 *                      stop watch which runs in real time (not in process
 *                      virtual time). 
 *
 *      Exported functions:
 *                      tcl3dNewSwatch
 *                      tcl3dDeleteSwatch
 *                      tcl3dStopSwatch
 *                      tcl3dStartSwatch
 *                      tcl3dResetSwatch
 *                      tcl3dLookupSwatch
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#include "tcl3dUtilStopWatch.h"

#if defined (_WIN32)
    #include <windows.h>
    #include <time.h>
    #include <sys/timeb.h>
#else
    #include <sys/time.h>
#endif

/* Read the current system time in seconds since some
   point in the past (usually system boot time). */

static double systemtime (void)
{
    #if defined (_WIN32)
        static LARGE_INTEGER ticksPerSecond;
        static int initialized = 0;
        LARGE_INTEGER currentTime;
        if (!initialized) {
            QueryPerformanceFrequency (&ticksPerSecond);
            initialized = 1;
        }
        QueryPerformanceCounter (&currentTime);
        return (double) currentTime.QuadPart /
               (double) ticksPerSecond.QuadPart;
    #else
        struct timeval tv;
        struct timezone tz;
        (void)gettimeofday (&tv, &tz);
        return (double)tv.tv_sec + (double)tv.tv_usec * 0.000001;
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dNewSwatch
 *
 *      Usage:          Create a new stopwatch.
 *
 *      Synopsis:       swatch tcl3dNewSwatch (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:       tcl3dDeleteSwatch
 *
 ***************************************************************************/

swatch tcl3dNewSwatch (void)
{
    SwatchStruct *pSwatch;

    pSwatch = (SwatchStruct *) malloc (sizeof (SwatchStruct));
    if (!pSwatch) {
        return NULL;
    }
    pSwatch->clockrunning = 0;
    pSwatch->numseconds   = 0.0;
    pSwatch->laststart    = 0.0;
    return pSwatch;
}

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

void tcl3dDeleteSwatch (swatch sw)
{
    if (sw) {
        free (sw);
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dStopSwatch
 *
 *      Usage:          Stop a stopwatch.
 *
 *      Synopsis:       void tcl3dStopSwatch (swatch sw)
 *
 *      Description:    The stopwatch "sw" is stopped, but not reset.
 *
 *      Return value:   None.
 *
 *      See also:       tcl3dStartSwatch
 *                      tcl3dResetSwatch
 *                      tcl3dLookupSwatch
 *
 ***************************************************************************/

void tcl3dStopSwatch (swatch sw)
{
    if (sw->clockrunning) {
        sw->numseconds += systemtime() - sw->laststart;
        sw->clockrunning = 0;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dStartSwatch
 *
 *      Usage:          Start a stopwatch.
 *
 *      Synopsis:       void tcl3dStartSwatch (swatch sw)
 *
 *      Description:    The stopwatch "sw" continues to run. It is not reset.
 *
 *      Return value:   None.
 *
 *      See also:       tcl3dStopSwatch
 *                      tcl3dResetSwatch
 *                      tcl3dLookupSwatch
 *
 ***************************************************************************/

void tcl3dStartSwatch (swatch sw)
{
    if (!sw->clockrunning) {
        sw->laststart = systemtime ();
        sw->clockrunning = 1;
    }
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dResetSwatch
 *
 *      Usage:          Reset a stopwatch.
 *
 *      Synopsis:       void tcl3dResetSwatch (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:       tcl3dStopSwatch
 *                      tcl3dStartSwatch
 *                      tcl3dLookupSwatch
 *
 ***************************************************************************/

void tcl3dResetSwatch (swatch sw)
{
    if (sw->clockrunning) {
        sw->laststart = systemtime ();
    }
    sw->numseconds = 0.0;
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dLookupSwatch
 *
 *      Usage:          Read current time from a stopwatch.
 *
 *      Synopsis:       double tcl3dLookupSwatch (const 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 tcl3dResetSwatch.
 *
 *      See also:       tcl3dStopSwatch
 *                      tcl3dStartSwatch
 *                      tcl3dResetSwatch
 *
 ***************************************************************************/

double tcl3dLookupSwatch (const swatch sw)
{
    if (sw->clockrunning) {
        return (double)(sw->numseconds + systemtime() - sw->laststart);
    }
    return (double)sw->numseconds;
}
