/*=============================================================================
	CAHostTimeBase.h

	$Log: CAHostTimeBase.h,v $
	Revision 1.12  2004/10/09 02:38:17  jcm10
	when tracking the time, print out stuff as 64 bit ints
	
	Revision 1.11  2004/08/26 23:35:42  jcm10
	more windows cleanup
	
	Revision 1.10  2004/08/25 23:06:04  dwyatt
	bring back GetCurrentTime on Mac so we don't break tons of code that doesn't need to go to Windows
	
	Revision 1.9  2004/08/23 06:23:25  jcm10
	make it build on Windows
	
	Revision 1.8  2003/11/20 22:56:53  dwyatt
	__COREAUDIO_USE_FLAT_INCLUDES__
	
	Revision 1.7  2003/08/29 04:13:15  jcm10
	disable the Asserts so that it can be used in the debug macros themselves
	
	Revision 1.6  2003/03/27 02:46:16  jcm10
	no need to initialize things on X when getting the current time
	
	Revision 1.5  2002/07/11 01:24:18  jcm10
	add HostDeltaToNanos()
	
	Revision 1.4  2002/06/13 06:10:50  dwyatt
	back out changes - causes conflicts
	
	Revision 1.3  2002/06/13 02:57:36  dwyatt
	make those new constants long long
	
	Revision 1.2  2002/06/13 01:22:13  dwyatt
	add convenience constants for nano/micro/milli/second conversions
	
	Revision 1.1  2002/05/18 01:06:09  bills
	moved files to public utils
	
	Revision 1.10  2002/04/18 02:19:52  jcm10
	clean up the header inclusion
	
	Revision 1.9  2002/03/29 03:27:16  jcm10
	make sure things are initialized before doling out info
	
	Revision 1.8  2002/03/04 23:50:48  jcm10
	make GetCurrentTime be inlined too
	
	Revision 1.7  2002/03/04 20:57:41  bills
	fixes for platform, inlining and lazy init
	
	Revision 1.6  2002/03/02 20:44:39  bills
	add DeltaHostToMics conversion and do initialization at load:)
	
	Revision 1.5  2001/03/14 23:48:38  jcm10
	Lots of changes to support the new driver API
	
	Revision 1.4  2001/03/01 03:07:37  jcm10
	make the code fully cross-platform and use the mach routines for getting the timebase on X
	
	Revision 1.3  2001/01/08 23:51:04  jcm10
	remove #pragma once, since gcc claims it to be obsolete and issues an annoying warning to that effect when all warnings are enabled
	
	Revision 1.2  2000/10/04 19:58:55  jcm10
	Add conversion routines to CAHostTimeBase
	
	Revision 1.1  2000/09/24 01:11:57  jcm10
	first checked in
	
	Revision 0.0  2000/01/01 12:34:56  jcm10
	created
		
	$NoKeywords: $
=============================================================================*/
#if !defined(__CAHostTimeBase_h__)
#define __CAHostTimeBase_h__

//=============================================================================
//	Includes
//=============================================================================

#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
	#include <CoreAudio/CoreAudioTypes.h>
#else
	#include <CoreAudioTypes.h>
#endif

#if TARGET_OS_MAC
	#include <mach/mach_time.h>
#elif TARGET_OS_WIN32
	#include <windows.h>
#else
	#error	Unsupported operating system
#endif

#include "CADebugMacros.h"

//=============================================================================
//	CAHostTimeBase
//
//	This class provides platform independent access to the host's time base.
//=============================================================================

#if CoreAudio_Debug
//	#define Log_Host_Time_Base_Parameters	1
//	#define Track_Host_TimeBase				1
#endif

class	CAHostTimeBase
{

public:
	static UInt64	ConvertToNanos(UInt64 inHostTime);
	static UInt64	ConvertFromNanos(UInt64 inNanos);

	static UInt64	GetTheCurrentTime();
#if TARGET_OS_MAC
	static UInt64	GetCurrentTime() { return GetTheCurrentTime(); }
#endif
	static UInt64	GetCurrentTimeInNanos();

	static Float64	GetFrequency() { if(!sIsInited) { Initialize(); } return sFrequency; }
	static UInt32	GetMinimumDelta() { if(!sIsInited) { Initialize(); } return sMinDelta; }

	static UInt64	AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
	static SInt64	HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);

private:
	static void		Initialize();
	
	static bool		sIsInited;
	
	static Float64	sFrequency;
	static UInt32	sMinDelta;
	static UInt32	sToNanosNumerator;
	static UInt32	sToNanosDenominator;
	static UInt32	sFromNanosNumerator;
	static UInt32	sFromNanosDenominator;
	static bool		sUseMicroseconds;
#if Track_Host_TimeBase
	static UInt64	sLastTime;
#endif
};

inline UInt64	CAHostTimeBase::GetTheCurrentTime()
{
	UInt64 theTime = 0;

	#if TARGET_OS_MAC
		theTime = mach_absolute_time();
	#elif TARGET_OS_WIN32
		LARGE_INTEGER theValue;
		QueryPerformanceCounter(&theValue);
		theTime = *((UInt64*)&theValue);
	#endif
	
	#if	Track_Host_TimeBase
		if(sLastTime != 0)
		{
			if(theTime <= sLastTime)
			{
				DebugMessageN2("CAHostTimeBase::GetTheCurrentTime: the current time is earlier than the last time, now: %qd, then: %qd", theTime, sLastTime);
			}
			sLastTime = theTime;
		}
		else
		{
			sLastTime = theTime;
		}
	#endif

	return theTime;
}

inline UInt64	CAHostTimeBase::ConvertToNanos(UInt64 inHostTime)
{
	if(!sIsInited)
	{
		Initialize();
	}
	
	Float64 theNumerator = static_cast<Float64>(sToNanosNumerator);
	Float64 theDenominator = static_cast<Float64>(sToNanosDenominator);
	Float64 theHostTime = static_cast<Float64>(inHostTime);

	Float64 thePartialAnswer = theHostTime / theDenominator;
	Float64 theFloatAnswer = thePartialAnswer * theNumerator;
	UInt64 theAnswer = static_cast<UInt64>(theFloatAnswer);

	//Assert(!((theNumerator > theDenominator) && (theAnswer < inHostTime)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");
	//Assert(!((theDenominator > theNumerator) && (theAnswer > inHostTime)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");

	return theAnswer;
}

inline UInt64	CAHostTimeBase::ConvertFromNanos(UInt64 inNanos)
{
	if(!sIsInited)
	{
		Initialize();
	}

	Float64 theNumerator = static_cast<Float64>(sToNanosNumerator);
	Float64 theDenominator = static_cast<Float64>(sToNanosDenominator);
	Float64 theNanos = static_cast<Float64>(inNanos);

	Float64 thePartialAnswer = theNanos / theNumerator;
	Float64 theFloatAnswer = thePartialAnswer * theDenominator;
	UInt64 theAnswer = static_cast<UInt64>(theFloatAnswer);

	//Assert(!((theDenominator > theNumerator) && (theAnswer < inNanos)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");
	//Assert(!((theNumerator > theDenominator) && (theAnswer > inNanos)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");

	return theAnswer;
}


inline UInt64	CAHostTimeBase::GetCurrentTimeInNanos()
{
	return ConvertToNanos(GetTheCurrentTime());
}

inline UInt64	CAHostTimeBase::AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
{
	UInt64 theAnswer;
	
	if(inStartTime <= inEndTime)
	{
		theAnswer = inEndTime - inStartTime;
	}
	else
	{
		theAnswer = inStartTime - inEndTime;
	}
	
	return ConvertToNanos(theAnswer);
}

inline SInt64	CAHostTimeBase::HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
{
	SInt64 theAnswer;
	SInt64 theSign = 1;
	
	if(inStartTime <= inEndTime)
	{
		theAnswer = inEndTime - inStartTime;
	}
	else
	{
		theAnswer = inStartTime - inEndTime;
		theSign = -1;
	}
	
	return theSign * ConvertToNanos(theAnswer);
}

#endif
