/*
 * zTimer.c
 * 
 * The Zen API 
 * Copyright (C) 2009-2010 by LMI Technologies Inc.
 * 
 * Licensed under The MIT License.
 * Redistributions of files must retain the above copyright notice.
 */
#include <z/zzTimer.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>

ZAPI_EXPORT(zStatus) zTimer_Construct(zTimer* timer)
{
    zTimerClass* obj = zNULL; 
    
    zCHECK(zCalloc(&obj, 1, sizeof(zTimerClass))); 

    zCHECK(zLock_Construct(&obj->lock)); 
    zCHECK(zStopwatch_Construct(&obj->stopwatch)); 
    zCHECK(zSemaphore_Construct(&obj->signal, 0)); 
    zCHECK(zThread_Construct(&obj->thread)); 

    zCHECK(zThread_Start(obj->thread, zTimer_ThreadEntry, obj)); 
   
    zHANDLE(timer, obj); 
    return zOK; 
}

ZAPI_EXPORT(zStatus) zTimer_Destroy(zTimer timer)
{
    zTimerClass* obj = zHANDLE_POINTER(timer); 

    if (!zIS_NULL(obj))
    {
        if (!zIS_NIL(obj->thread))
        {
            obj->quit = zTRUE; 
            zCHECK(zSemaphore_Post(obj->signal)); 

            zCHECK(zThread_Join(obj->thread)); 
            zCHECK(zThread_Destroy(obj->thread)); 
        }

        if (!zIS_NIL(obj->stopwatch))   zCHECK(zStopwatch_Destroy(obj->stopwatch)); 
        if (!zIS_NIL(obj->lock))        zCHECK(zLock_Destroy(obj->lock)); 
        if (!zIS_NIL(obj->signal))      zCHECK(zSemaphore_Destroy(obj->signal)); 

        zCHECK(zFree(obj)); 
    }

    return zOK; 
}

ZAPI_EXPORT(zStatus) zTimer_Start(zTimer timer, z64u period, zTimerElapsedCallback callback, zHandle context)
{
    zTimerClass* obj = zHANDLE_POINTER(timer); 

    zCHECK(zLock_Enter(obj->lock)); 
    {
        obj->enabled = zTRUE; 
        obj->period = period; 
        obj->callback = callback; 
        obj->context = context; 

        zCHECK(zStopwatch_Start(obj->stopwatch, obj->period)); 
    }
    zCHECK(zLock_Exit(obj->lock)); 

    zCHECK(zSemaphore_Post(obj->signal)); 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zTimer_Stop(zTimer timer)
{
    zTimerClass* obj = zHANDLE_POINTER(timer); 

    zCHECK(zLock_Enter(obj->lock)); 
    {
        obj->enabled = zFALSE; 
    }
    zCHECK(zLock_Exit(obj->lock)); 

    return zOK; 
}

ZAPI_EXPORT(zBool) zTimer_Enabled(zTimer timer)
{
    zTimerClass* obj = zHANDLE_POINTER(timer);
    zBool enabled = zFALSE; 

    zCHECK(zLock_Enter(obj->lock)); 
    {
        enabled = obj->enabled; 
    }
    zCHECK(zLock_Exit(obj->lock)); 

    return enabled; 
}

ZAPI_EXPORT(zStatus) ZAPI_CALL zTimer_ThreadEntry(zTimer timer)
{
    zTimerClass* obj = zHANDLE_POINTER(timer); 
    zStatus waitResult = zOK;  
    zStatus callbackResult = zOK; 

    do
    {
        z64u timeout = zINFINITE; 

        zCHECK(zLock_Enter(obj->lock)); 
        {
            if (obj->enabled)
            {       
                if (zStopwatch_IsExpired(obj->stopwatch))
                {
                    callbackResult = obj->callback(obj->context, timer);
                    zStopwatch_Start(obj->stopwatch, obj->period); 
                }

                if (obj->enabled)
                {
                    timeout = zStopwatch_Remaining(obj->stopwatch); 
                }
            }
        }
        zCHECK(zLock_Exit(obj->lock)); 

        if ((waitResult = zSemaphore_Wait(obj->signal, timeout)) != zERROR_TIMEOUT)
        {
            zCHECK(waitResult); 
        }
    }
    while (!obj->quit && zSUCCESS(callbackResult)); 

    return zOK; 
}
