//////////////////////////////////////////////////////////////////////////////
//
// Asura_Profiling_GUIMenu.h
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// ASURA_USE_GUIMENU_PROFILING == 1 Enables profiling GUIMenu tasks 
// ASURA_USE_GUIMENU_PROFILING == 0 All macro profiling commands still exist for compiling but are empty.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifdef ASURA_USE_GUIMENU_PROFILING

#include "Core/Asura_Platform_Timers.h"	// for Asura_TickCount
#include "Debugging/Asura_Assert.h"

#endif 

#ifndef ASURA_PROFILING_GUIMENU_H___
#define ASURA_PROFILING_GUIMENU_H___ 1

enum Asura_GUIMenu_InstanceCounter
{
	ASURA_GUIMENU_COUNTER__GUIMENU_SYSTEM = 0, // First in list must be 0
	ASURA_GUIMENU_COUNTER__GUIMENU_ROOT,
	ASURA_GUIMENU_COUNTER__GUIMENU_PAGE	,
	ASURA_GUIMENU_COUNTER__ELEMENT_ANIMATION,
	ASURA_GUIMENU_COUNTER__ELEMENT_BASE,
	ASURA_GUIMENU_COUNTER__ELEMENT_IMAGE,
	ASURA_GUIMENU_COUNTER__ELEMENT_PATH,
	ASURA_GUIMENU_COUNTER__ELEMENT_STATUSTEXT,
	ASURA_GUIMENU_COUNTER__ELEMENT_TEXT,
	ASURA_GUIMENU_COUNTER__ELEMENT_FMV,
	ASURA_GUIMENU_COUNTER__WIDGET_ANIMATINGIMAGE,
	ASURA_GUIMENU_COUNTER__WIDGET_BASE,
	ASURA_GUIMENU_COUNTER__WIDGET_BUTTON,
	ASURA_GUIMENU_COUNTER__WIDGET_CHECKBOX,
	ASURA_GUIMENU_COUNTER__WIDGET_GROUP,
	ASURA_GUIMENU_COUNTER__WIDGET_LISTBOX,
	ASURA_GUIMENU_COUNTER__WIDGET_NUMERIC,
	ASURA_GUIMENU_COUNTER__WIDGET_PROGRESSBAR,
	ASURA_GUIMENU_COUNTER__WIDGET_SLIDER,
	ASURA_GUIMENU_COUNTER__WIDGET_TEXTBOX,
	ASURA_GUIMENU_COUNTER__WIDGET_TEXTLISTBOX,

	ASURA_GUIMENU_COUNTER__MAXCOUNTERS,  // Must always be last in list
};

enum Asura_GUIMenu_ProfileSection
{
	ASURA_GUIMENU_PROFILE__UPDATE	= 0, // First in list must be 0
	ASURA_GUIMENU_PROFILE__RENDER,

	ASURA_GUIMENU_PROFILE__MAXSECTIONS,  // Must always be last in list
};

enum Asura_GUIMenu_ProfileTask
{
	ASURA_GUIMENU_PROFILE__GUIMENU_SYSTEM	= 0, // First in list must be 0
	ASURA_GUIMENU_PROFILE__GUIMENU_ROOT,
	ASURA_GUIMENU_PROFILE__GUIMENU_PAGE,
	ASURA_GUIMENU_PROFILE__ELEMENT_ANIMATION,
	ASURA_GUIMENU_PROFILE__ELEMENT_BASE,
	ASURA_GUIMENU_PROFILE__ELEMENT_IMAGE,
	ASURA_GUIMENU_PROFILE__ELEMENT_PATH,
	ASURA_GUIMENU_PROFILE__ELEMENT_STATUSTEXT,
	ASURA_GUIMENU_PROFILE__ELEMENT_TEXT,
	ASURA_GUIMENU_PROFILE__ELEMENT_FMV,
	ASURA_GUIMENU_PROFILE__WIDGET_ANIMATINGIMAGE,
	ASURA_GUIMENU_PROFILE__WIDGET_BASE,
	ASURA_GUIMENU_PROFILE__WIDGET_BUTTON,
	ASURA_GUIMENU_PROFILE__WIDGET_CHECKBOX,
	ASURA_GUIMENU_PROFILE__WIDGET_GROUP,
	ASURA_GUIMENU_PROFILE__WIDGET_LISTBOX,
	ASURA_GUIMENU_PROFILE__WIDGET_NUMERIC,
	ASURA_GUIMENU_PROFILE__WIDGET_PROGRESSBAR,
	ASURA_GUIMENU_PROFILE__WIDGET_SLIDER,
	ASURA_GUIMENU_PROFILE__WIDGET_TEXTBOX,
	ASURA_GUIMENU_PROFILE__WIDGET_TEXTLISTBOX,
	ASURA_GUIMENU_PROFILE__HANDLE_MENUPAGE_UPDATE,
	ASURA_GUIMENU_PROFILE__INPUT_ACTIVITY,

	ASURA_GUIMENU_PROFILE__MAXTASKS,  // Must always be last in list
};

#ifdef ASURA_USE_GUIMENU_PROFILING

// C L A S S E S /////////////////////////////////////////////////////////////

// Maximum call-stack depth that is supported by the profiler
static const u_int uASURA_GUIMENU_PROFILE_MAX_STACK_DEPTH	= 70;
// Maximum number of frames of historical data.
// Historical data is used to calculate a smoothed average time.
static const u_int uMAX_NUMBER_RESULTS_FRAMES				= 20;

class Asura_Profiling_GUIMenu
{
public:
	// Initialise the GUIMenu profiling system
	static void Initialise();
	// Reset the system when Core::Reset is called
	static void Reset();

	// Record the instantiation of a GUIMenu type
	static void DebugCounterInstance( Asura_GUIMenu_InstanceCounter eType );
	// Record the destruction of a GUIMenu type
	static void DebugCounterDestroyInstance( Asura_GUIMenu_InstanceCounter eType );
	// Record the size of a GUIMenu type
	static void DebugCounterClassSize( Asura_GUIMenu_InstanceCounter eType, u_int uSize );
	// Display the recorded counters and calculate an estimate for the amount of memory used.
	static void DisplayDebugCounters();

	// Start profiling a new section.
	// Calls to StartProfileTask will belong to the current section.
	// If no section is currently set then StartProfileTask and EndProfileTask
	// won't record any results.
	static void StartSection(Asura_GUIMenu_ProfileSection eSection);
	// End profiling of the current section.
	static void EndSection();

	// Start profiling a task. Records the start time of the task
	// Must be called between a StartSection and EndSection call otherwise no results will be stored
	static void StartProfileTask( Asura_GUIMenu_ProfileTask eTask);
	// End profiling the last task. Calculates the duration and store the results
	// Must be called between a StartSection and EndSection call otherwise no results will be stored
	static void EndProfileTask();
	// Display the profiling timing results.
	static void DisplayProfileData();

private:

	static void UpdateDisplayStates();
	static void EnableRecording(bool bEnable);

	// Entry objects are held in the s_axProfileStack call stack.
	// They contain the start time, task id, and section id. Also contain
	// how long any child tasks have taken to run.
	class Asura_Profile_Entry
	{
	public:
		Asura_Profile_Entry() {};

		// Record the start time, task id and current section id.
		void Start(Asura_TickCount iNowTicks, Asura_GUIMenu_ProfileTask eTask, Asura_GUIMenu_ProfileSection eSection );

		// Calculate the inclusive ticks for this task. 
		// That is the duration of the task took minus how long its children took to run
		Asura_TickCount InclusiveTicks(Asura_TickCount iNowTicks);

		// Calculate the total duration of the task including any child tasks
		Asura_TickCount ExclusiveTicks(Asura_TickCount iNowTicks);

		Asura_TickCount m_uStartTime; // Time the section was started
		Asura_TickCount m_uChildTime; // Time the children took to run
		Asura_GUIMenu_ProfileTask m_eTask; // Task type of thing being profiled;
		Asura_GUIMenu_ProfileSection m_eSection; // Section type;
	};

	// Record the results of a task.
	// Contains the inclusive, exclusive and call count for a task
	class Asura_Profile_Results
	{
	public:

		void Init()
		{
			m_iInclusiveTime = 0;
			m_iExclusiveTime = 0;
			m_uCallCount = 0;
		}

		Asura_TickCount m_iInclusiveTime;
		Asura_TickCount m_iExclusiveTime;
		u_int			m_uCallCount;
	};

	// Record the results for a task within a section.
	// Holds the total profile results for a task, the last uMAX_NUMBER_RESULTS_FRAMES frames 
	// of data and the peak timings
	class Asura_Profile_Results_Entry
	{
	public:

		void Init();

		void FrameInit();
		void RecordResults(Asura_TickCount iInclusiveTicks, Asura_TickCount iExclusiveTicks);

		Asura_Profile_Results GetCurrentFrameResults() { return m_xFrame[m_uCurrentFrameIndex]; }
		Asura_Profile_Results GetAverageFrameResults();

		void UpdatePeakTimes(Asura_TickCount iNowTicks);

		Asura_Profile_Results m_xTotal;
		Asura_Profile_Results m_xFrame[uMAX_NUMBER_RESULTS_FRAMES];

		u_int m_uCurrentFrameIndex;
		u_int m_uNumberFrames;

		Asura_TickCount m_iIncPeakTime;
		u_int m_uIncPeakFrame;
		Asura_TickCount m_iExcPeakTime;
		u_int m_uExcPeakFrame;
		Asura_TickCount m_iLastTimePeakChanged;
	};

	// Holds the timing data for a section
	// Holds the total time for a section and the last 
	// uMAX_NUMBER_RESULTS_FRAMES number of frames of timing data
	class Asura_Profile_Section
	{
	public:

		void BeginSection(Asura_TickCount iNowTicks);
		void EndSection(Asura_TickCount iNowTicks);

		Asura_TickCount GetCurrentFrameDuration();
		Asura_TickCount GetAverageFrameDuration();

		Asura_TickCount m_iFrameStartTime;
		Asura_TickCount m_iFrameDuration[uMAX_NUMBER_RESULTS_FRAMES];

		Asura_TickCount m_iTotalDuration;
		u_int			m_uFrameCount;
	};

	// Keep track of the number of instances created
	static u_int s_auInstanceCount[ASURA_GUIMENU_COUNTER__MAXCOUNTERS];
	// Holds the size of each instance type
	static u_int s_auClassSize[ASURA_GUIMENU_COUNTER__MAXCOUNTERS];

	// Array of counter names used for display purposes
	static const char* s_aszCounterNames[];

	// Array of section names used for display purposes
	static const char* s_aszSectionNames[];

	// Array of task names used for display purposes.
	static const char* s_aszTaskNames[];

	// A stack of profile entry objects. As calls are made to StartProfileTask profile data 
	// is pushed to the stack.
	// As calls are made to EndProfileTask profile data is popped from the stack 
	static Asura_Profile_Entry s_axProfileStack[uASURA_GUIMENU_PROFILE_MAX_STACK_DEPTH];
	// Current depth of the stack
	static u_int s_uProfileDepth;

	// Holds the results of profiled tasks.
	// Each call to EndProfileTask calculates the results of task and adds them to the results array
	static Asura_Profile_Results_Entry s_axProfileTime[ASURA_GUIMENU_PROFILE__MAXTASKS][ASURA_GUIMENU_PROFILE__MAXSECTIONS];

	// Allows recording of profile data between calls to StartSection and EndSection.
	static bool s_bEnableRecording;

	// Holds details of the current section. Only one section can be active at a time.
	static Asura_GUIMenu_ProfileSection m_eCurrentSection;

	// Holds the timing results for a section
	static Asura_Profile_Section s_axSectionData[ASURA_GUIMENU_PROFILE__MAXSECTIONS];

	static bool s_bWasDisplayMemoryLastFrame;
	static bool s_bWasDisplayTimingsLastFrame;

	// Console Commands vars
	static bool s_bDisplayMemory;		// If true display the memory results
	static bool s_bDisplayTimings;		// If true display the timing results

	static u_int s_bMaxHistoricFrames;	// Number of frames used to calculate average times

	static float s_fPeakDelaySecs;		// Number of secs to keep peak times.
	static float s_fHighlightMilliSecs; // Number of milliseconds used as a threshold to
										// highlight lengthy timings.

	static u_int s_uTimingsX;			// X pos where the timings display is rendered
	static u_int s_uTimingsY;			// Y pos where the timings display is rendered

	static u_int s_uMemoryX;			// X pos where the memory display is rendered
	static u_int s_uMemoryY;			// Y pos where the memory display is rendered
};

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#define ASURA_GUIMENU_PROFILE_INITIALISE() Asura_Profiling_GUIMenu::Initialise();
#define ASURA_GUIMENU_PROFILE_COUNTINSTANCE(eType) Asura_Profiling_GUIMenu::DebugCounterInstance(eType);
#define ASURA_GUIMENU_PROFILE_COUNTDESTROYINSTANCE(eType) Asura_Profiling_GUIMenu::DebugCounterDestroyInstance(eType);
#define ASURA_GUIMENU_PROFILE_CLASSSIZE(eType,uSize) Asura_Profiling_GUIMenu::DebugCounterClassSize(eType,uSize);

#define ASURA_GUIMENU_PROFILE_START_SECTION(eSection) Asura_Profiling_GUIMenu::StartSection(eSection);
#define ASURA_GUIMENU_PROFILE_END_SECTION() Asura_Profiling_GUIMenu::EndSection();

#define ASURA_GUIMENU_PROFILE_START_TASK(eTask) Asura_Profiling_GUIMenu::StartProfileTask(eTask);
#define ASURA_GUIMENU_PROFILE_END_TASK() Asura_Profiling_GUIMenu::EndProfileTask();

#define ASURA_GUIMENU_PROFILE_DISPLAYCOUNTERS() Asura_Profiling_GUIMenu::DisplayDebugCounters();
#define ASURA_GUIMENU_PROFILE_DISPLAYPROFILEDATA() Asura_Profiling_GUIMenu::DisplayProfileData();

#else

#define ASURA_GUIMENU_PROFILE_INITIALISE()
#define ASURA_GUIMENU_PROFILE_COUNTINSTANCE(eType)
#define ASURA_GUIMENU_PROFILE_COUNTDESTROYINSTANCE(eType)
#define ASURA_GUIMENU_PROFILE_CLASSSIZE(eType,uSize)

#define ASURA_GUIMENU_PROFILE_START_SECTION(eSection)
#define ASURA_GUIMENU_PROFILE_END_SECTION()

#define ASURA_GUIMENU_PROFILE_START_TASK(eType)
#define ASURA_GUIMENU_PROFILE_END_TASK()

#define ASURA_GUIMENU_PROFILE_DISPLAYCOUNTERS()
#define ASURA_GUIMENU_PROFILE_DISPLAYPROFILEDATA()

#endif // ASURA_USE_GUIMENU_PROFILING

#endif // #ifndef ASURA_PROFILING_GUIMENU_H___
