/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: sgx/src/sgx/core/time/gameclock.cpp

*/

#include "sgx/core/time/gameclock.hpp"

CGameClock *CGameClock::sm_pSingleton = NULL;

CGameClock *
CGameClock::get()
{
    if (sm_pSingleton == NULL) {
        sm_pSingleton = new CGameClock();
    }

    return sm_pSingleton;
}


CGameClock::CGameClock()
{
    m_fLastTime = 0;
    m_fFractionalSpeed = 1.0f;
    m_fGivenTime = 0;
    m_fConstantPeriod = 1.0f/60.0f;
    m_fClampedMin = 1.0f/15.0f;
    m_fClampedMax = 1.0f/60.0f;
    m_ClockMode = Realtime;
	m_fLastTelaps = 0;
	m_MaxTimers = MAX_TIMERS;
}

tREAL32 
CGameClock::applyRealTimeToClock(const tREAL32 current_time)
{
tREAL32 time_elapsed;

    time_elapsed = current_time - m_fLastTime;

    switch(m_ClockMode)
    {
    case	Realtime:
        time_elapsed *= m_fFractionalSpeed;
        break;

    case	AsGiven:
        time_elapsed = m_fGivenTime;
        break;

    case	Constant:
        time_elapsed = m_fConstantPeriod;
        break;

    case	Clamped:
        if (time_elapsed < m_fClampedMax) {
            time_elapsed = m_fClampedMax;
        } else if (time_elapsed > m_fClampedMin) {
            time_elapsed = m_fClampedMin;
        }
        time_elapsed *= m_fFractionalSpeed;
        break;
    }

    m_fLastTime = current_time;
	m_fLastTelaps = time_elapsed;

    return time_elapsed;
}

void 
CGameClock::setModeRealtime()
{
    m_ClockMode = Realtime;
}

void 
CGameClock::setModeConstant(const tREAL32 period)
{
    m_fConstantPeriod = period;
    m_ClockMode = Constant;
}

void 
CGameClock::setModeConstantFPS(const tREAL32 fps)
{
    m_fConstantPeriod = 1.0f/fps;
    m_ClockMode = Constant;
}

void 
CGameClock::setModeClamped(const tREAL32 min_fps, const tREAL32 max_fps)
{
    m_fClampedMin = 1.0f/min_fps;
    m_fClampedMax = 1.0f/max_fps;
    m_ClockMode = Clamped;
}

void 
CGameClock::setModeClampedTelaps(const tREAL32 min_telaps, const tREAL32 max_telaps)
{
    m_fClampedMin = min_telaps;
    m_fClampedMax = max_telaps;
    m_ClockMode = Clamped;
}

tREAL32 
CGameClock::update(const tREAL32 telaps)
{
	tREAL32 realTelaps = applyRealTimeToClock(telaps + m_fLastTime);
	for(tUINT32 i=0;i<m_MaxTimers;++i) {
		m_Timer[i].update(realTelaps);
	}
	return realTelaps;
}

tREAL32 
CGameClock::getTime()
{
    return m_fLastTime;
}

tREAL32 
CGameClock::getLastTelaps()
{
    return m_fLastTelaps;
}

tREAL32 
CGameClock::getClampedMin()
{
    return m_fClampedMin;
}

tREAL32 
CGameClock::getClampedMax()
{
    return m_fClampedMax;
}

//
// User triggerable timers and alarms
//
CClockTimer::CClockTimer() :
m_bRunning(FALSE),
m_bPaused(FALSE) {
}

void
CClockTimer::start(const tREAL32 period, sgxGameClockCallbackHandler *pHandler, const tUINT32 maxRepeats) {
	m_bRunning = TRUE;
	m_bPaused = FALSE;

	m_fTimecumNext = 0;
	m_pHandler = pHandler;

	m_iRepeatCount = 0;
	m_fPeriod = period;
	m_fFrequencyFPS = 1.0f/period;
	m_iRepeatStop = maxRepeats;

}

void
CClockTimer::startFPS(const tREAL32 fps, sgxGameClockCallbackHandler *pHandler, const tUINT32 maxRepeats) {
	start(1.0f/fps, pHandler, maxRepeats);
}

void
CClockTimer::stop() {
	m_bRunning = FALSE;
}

void
CClockTimer::pause() {
	m_bPaused = TRUE;
}

void
CClockTimer::resume() {
	m_bPaused = FALSE;
}
	
tBOOL
CClockTimer::isRunning() const {
	return m_bRunning && !m_bPaused;
}
	
tBOOL
CClockTimer::isValid() const {
	return m_bRunning;
}
	
sgxGameClockCallbackHandler *
CClockTimer::getHandler() const {
	return m_pHandler;
}


void
CClockTimer::update(const tREAL32 telaps) {
	if (isRunning()) {
		m_fTimecumNext += telaps;

		if (m_fTimecumNext >= m_fPeriod) {
			m_fTimecumNext -= m_fPeriod;
			if (++m_iRepeatCount == m_iRepeatStop && m_iRepeatStop != 0) {
				stop();
			}
			if (m_pHandler) {
				if (!m_pHandler->onGameClockTimer(this)) {
					stop();
				}
			}
		}
	}
}


CClockTimer *
CGameClock::getFreeTimer() {
	for(tUINT32 i=0;i<m_MaxTimers;++i) {
		if (!m_Timer[i].isValid()) {
			return &m_Timer[i];
		}
	}
	return NULL;
}

void
CGameClock::releaseAllTimers(const sgxGameClockCallbackHandler *pWithHandler) {
	for(tUINT32 i=0;i<m_MaxTimers;++i) {
		if (m_Timer[i].isValid() && (pWithHandler==NULL || m_Timer[i].getHandler() == pWithHandler)) {
			m_Timer[i].stop();
		}
	}
}


tBOOL	
sgxGameClockCallbackHandler::onGameClockTimer(CClockTimer *pTimer) {
	SGX_UNUSED(pTimer);
	return TRUE;
}


