/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkTimer.txx
	Version:		$Revision: 1761 $
	Date:			$Date: 2011-09-23 17:25:41 +0200 (Fr, 23 Sep 2011) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
	See RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#include "ritkTimer.h"

#include <math.h>

namespace ritk
{

template<class TVal>
Timer<TVal>
::Timer()
{
	// By default, one single pass
	m_NumPassesToAverageElapsedTime = 1; 

	// Initialize timer
	m_Clock = itk::RealTimeClock::New();
	m_TimerInitialized = true;

	// Reset the timer
	ResetTimer();
}


template<class TVal>
Timer<TVal>
::~Timer()
{
}


//----------------------------------------------------------------------------
template<class TVal>
void 
Timer<TVal>
::SetNumberOfPassesToAverageRuntime(unsigned int num)
{
	m_NumPassesToAverageElapsedTime = num;
}


//----------------------------------------------------------------------------
template<class TVal>
void 
Timer<TVal>
::GetElapsedTime(TVal *AverageElapsedTime, TVal *ElapsedTimeStdDeviation)
{
	// Lock to ensure thread safety
	m_Mutex.Lock();

	// Update average elapsed time
	m_ElapsedTime[0] = m_AccumulatedElapsedTime/m_LastElapsedTimes.size();

	// Update elapsed time standard deviation
	TVal ElapsedTimeStdDev = 0;
	for ( unsigned int i = 0; i < m_LastElapsedTimes.size(); i++ )
	{
		TVal d = m_LastElapsedTimes[i]-m_ElapsedTime[0];
		ElapsedTimeStdDev += d*d;
	}
	ElapsedTimeStdDev /= m_LastElapsedTimes.size()-1;
	ElapsedTimeStdDev = sqrt(ElapsedTimeStdDev);
	m_ElapsedTime[1] = ElapsedTimeStdDev;

	// Convert to ms
	TVal ms_scale = 1000.0;

	// Simply copy the averaged elapsed time
	*AverageElapsedTime = m_ElapsedTime[0]*ms_scale;

	// Optional, copy the elapsed time's standard deviation
	if ( ElapsedTimeStdDeviation )
		*ElapsedTimeStdDeviation = m_ElapsedTime[1]*ms_scale;

	// Unlock
	m_Mutex.Unlock();
}


//----------------------------------------------------------------------------
template<class TVal>
bool 
Timer<TVal>
::StartTimer(bool Reset)
{
	if ( !m_TimerInitialized )
		return false;
	
	if ( Reset )
		ResetTimer();

	// Lock to ensure thread safety
	m_Mutex.Lock();

	// Updat members
	m_TimerStarted = true;
	m_CurrentTimeStamp = m_Clock->GetTimeStamp();

	// Unlock and signal success
	m_Mutex.Unlock();
	return true;
}


//----------------------------------------------------------------------------
template<class TVal>
void 
Timer<TVal>
::StopTimer()
{
	// If the timer was not started properly return
	if ( !m_TimerStarted )
		return;

	// Lock to ensure thread safety
	m_Mutex.Lock();

	// Get timestamp
	itk::RealTimeClock::TimeStampType t = m_Clock->GetTimeStamp();

	// Compute elapsed time in ms
	TVal ElapsedTime = t-m_CurrentTimeStamp;

	// Add to the accu
	AddElapsedTime(ElapsedTime);

	

	// Update the timer flag
	m_TimerStarted = false;

	// Unlock
	m_Mutex.Unlock();
}


//----------------------------------------------------------------------------
template<class TVal>
void 
Timer<TVal>
::AddElapsedTime(TVal ElapsedTime)
{
	// Update average elapsed time
	m_LastElapsedTimes.push_back(ElapsedTime);
	m_AccumulatedElapsedTime += ElapsedTime;
	if ( m_LastElapsedTimes.size() > m_NumPassesToAverageElapsedTime )
	{
		m_AccumulatedElapsedTime -= m_LastElapsedTimes.front();
		m_LastElapsedTimes.pop_front();
	}
}


//----------------------------------------------------------------------------
template<class TVal>
void 
Timer<TVal>
::ResetTimer()
{
	// Update the timer flag
	m_TimerStarted = false;

	// Reset members
	m_AccumulatedElapsedTime = 0;
	m_LastElapsedTimes.clear();
	m_ElapsedTime[0] = 0;
	m_ElapsedTime[1] = 0;

}

} // end namespace ritk
