/*
 * zStopwatch.c
 * 
 * The Zen API 
 * Copyright (C) 2008-2011 by LMI Technologies Inc.
 * 
 * Licensed under The MIT License.
 * Redistributions of files must retain the above copyright notice.
 */ 
#include <z/zzStopwatch.h>

ZAPI_EXPORT(zStatus) zStopwatch_Construct(zStopwatch* stopwatch)
{
    zStopwatchClass* obj = zNULL; 
    z64u frequency = zStopwatch_SystemCounterFrequency(); 

    if (frequency == 0)
    {
        return zERROR; 
    }

    zCHECK(zCalloc(&obj, 1, sizeof(zStopwatchClass))); 

    obj->frequency = frequency;     
    obj->isStopped = zTRUE; 
    obj->startTime = 0; 
    obj->expiryTime = 0; 
    obj->stopTime = 0; 

    zHANDLE(stopwatch, obj); 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zStopwatch_Destroy(zStopwatch stopwatch)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 

    if (!zIS_NULL(obj))
    {
        zFree(obj); 
    }

    return zOK; 
}

ZAPI_EXPORT(zStatus) zStopwatch_Start(zStopwatch stopwatch, z64u totalTime)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 

    obj->startTime = zStopwatch_Current(stopwatch); 

    if (totalTime == zINFINITE)
    {
        obj->expiryTime = zINFINITE; 
    }
    else
    {
        obj->expiryTime = obj->startTime + totalTime; 
    }

    obj->isStopped = zFALSE; 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zStopwatch_Stop(zStopwatch stopwatch)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 

    if (!obj->isStopped)
    {
        obj->stopTime = zStopwatch_Current(stopwatch); 
        obj->isStopped = zTRUE; 
    }

    return zOK; 
}

ZAPI_EXPORT(zBool) zStopwatch_IsExpired(zStopwatch stopwatch)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 

    if (obj->expiryTime == zINFINITE)
    {
        return zFALSE; 
    }
    else
    {
        z64u count = (!obj->isStopped) ? zStopwatch_Current(stopwatch) : obj->stopTime;   
        return count > obj->expiryTime; 
    }
}

ZAPI_EXPORT(z64u) zStopwatch_Elapsed(zStopwatch stopwatch)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 
    z64s count = (!obj->isStopped) ? zStopwatch_Current(stopwatch) : obj->stopTime; 

    return count - obj->startTime; 
}

ZAPI_EXPORT(z64u) zStopwatch_Remaining(zStopwatch stopwatch)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 

    if (obj->expiryTime == zINFINITE)
    {
        return zINFINITE; 
    }
    else
    {
        z64u count = (!obj->isStopped) ? zStopwatch_Current(stopwatch) : obj->stopTime; 
        return (count >= obj->expiryTime) ? 0 : (obj->expiryTime - count);
    }
}

ZAPI_EXPORT(z64u) zStopwatch_Current(zStopwatch stopwatch)
{
    zStopwatchClass* obj = zHANDLE_POINTER(stopwatch); 
    z64u frequency = obj->frequency;
    z64u count = zStopwatch_SystemCounter(); 
      
    return zSTOPWATCH_SCALE*(count/frequency) + zSTOPWATCH_SCALE*(count%frequency)/frequency; 
}

ZAPI_EXPORT(z64u) zStopwatch_Now()
{
    z64u frequency = zStopwatch_SystemCounterFrequency(); 
    z64u count = zStopwatch_SystemCounter(); 
      
    return zSTOPWATCH_SCALE*(count/frequency) + zSTOPWATCH_SCALE*(count%frequency)/frequency; 
}

#if defined(ZAPI_WINDOWS)

ZAPI_EXPORT(z64u) zStopwatch_SystemCounter()
{
    z64u count; 

    if (!QueryPerformanceCounter((LARGE_INTEGER*)&count))
    {
        return 0; 
    }
    
    return count; 
}

ZAPI_EXPORT(z64u) zStopwatch_SystemCounterFrequency()
{
    z64u frequency; 
    
    if (!QueryPerformanceFrequency((LARGE_INTEGER*)&frequency))
    {
        return 0; 
    }
    
    return frequency; 
}

#elif defined (ZAPI_LINUX)

ZAPI_EXPORT(z64u) zStopwatch_SystemCounter()
{
    struct timespec tp; 
    
    if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
    {
        return 0;                  
    }

    return ZAPI_64U(1000000)*tp.tv_sec + tp.tv_nsec/ZAPI_64U(1000); 
}

ZAPI_EXPORT(z64u) zStopwatch_SystemCounterFrequency()
{
    return ZAPI_64U(1000000);
}

#endif
