///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Contains profiling code.
 *	\file		IceProfiler.h
 *	\author		Pierre Terdiman
 *	\date		April, 4, 2000
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEPROFILER_H__
#define __ICEPROFILER_H__

	ICECORE_API	void SetBaseTime(udword time);
	ICECORE_API	udword GetBaseTime();

	//! This function initializes the profiler by counting the cpuid overhead.
	//! This is done 3 times on purpose, since cpuid takes a longer time to execute the first times it's called.
	//! "cpuid" is used before rdtsc to prevent out-of-sequence execution from producing wrong results.
	//!	\see		StartProfile
	//!	\see		EndProfile
	__forceinline void InitProfiler()
	{
		udword cyc, Base;
		_asm{
			cpuid
			rdtsc
			mov		cyc, eax
			cpuid
			rdtsc
			sub		eax, cyc
			mov		Base, eax

			cpuid
			rdtsc
			mov		cyc, eax
			cpuid
			rdtsc
			sub		eax, cyc
			mov		Base, eax

			cpuid
			rdtsc
			mov		cyc, eax
			cpuid
			rdtsc
			sub		eax, cyc
			mov		Base, eax
		}
		SetBaseTime(Base);
	}

	//!	This function starts recording the number of cycles elapsed.
	//!	\param	val		address of a 32 bits value where the system should store the result.
	//!	\see	EndProfile
	//!	\see	InitProfiler
	__forceinline void	StartProfile(udword& val)
	{
		__asm{
			cpuid
			rdtsc
			mov		ebx, val
			mov		[ebx], eax
		}
	}

	//!	This function ends recording the number of cycles elapsed.
	//!	\param		address to store the number of cycles elapsed since the last StartProfile.
	//!	\see		StartProfile
	//!	\see	InitProfiler
	__forceinline void	EndProfile(udword& val)
	{
		__asm{
			cpuid
			rdtsc
			mov		ebx, val
			sub		eax, [ebx]
			mov		[ebx], eax
		}
		val-=GetBaseTime();
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	User-callback, called by the profiler on starting/ending profiling a given process.
	 *	\param		bool		[in] true when profile starts, false when profile ends
	 *	\param		userdata	[in] user-defined data
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	typedef void	(*PROFILE_CALLBACK)	(bool begin, udword userdata);

	struct ICECORE_API ProfileData
	{
		udword		Order;				//!< Insertion order
		char*		Label;				//!< User-defined label
		udword		UserData;			//!< User-defined tag
		udword		NbCycles;			//!< Number of elapsed cycles
		float		Percents;			//!< NbCycles * 100 / TotalNbCycles
		udword		RecursionLevel;		//!< Level of recursion
	};

	class ICECORE_API Profiler
	{
		public:
		// Constructor/Destructor
											Profiler();
											~Profiler();
		// Start of frame call
						void				Init();
		// Profiling
						bool				StartProfile(const char* label=null);
						bool				EndProfile(const char* label=null);
		// End of frame call
						ProfileData*		GetResults(udword& nbrecords, udword& totalnbcycles);
		// Callback control
		__forceinline	void				SetUserData(udword data)				{ mUserData	= data;		}
		__forceinline	void				SetCallback(PROFILE_CALLBACK callback)	{ mCallback	= callback;	}
		__forceinline	void				SetCallbackEvent(const char* label)		{ mEvent	= label;	}

		protected:
		// User callback
						udword				mUserData;
						PROFILE_CALLBACK	mCallback;
						const char*			mEvent;

						LIFOStack			mCyclesCounters;
						LIFOStack			mOrder;
						Container			mRecorder;
						udword				mNbRecords;
						udword				mRecursionLevel;
						udword				mMaxRecursionLevel;
						bool				mIsReady;
						bool				mEndingPending;
	};

#endif // __ICEPROFILER_H__
