/*===========================================================================
 *
 * Profile.H - Dave Humphrey (uesp@m0use.net), Thursday, November 23, 2000
 *
 *=========================================================================*/
#ifndef __PROFILE_H
#define __PROFILE_H

	/* Required Includes */
#include "genutil.h"
#include "dbllist.h"

#if defined(_WIN32)
  #include "windows.h"
#endif


/*===========================================================================
 *
 * Begin Defines
 *
 *=========================================================================*/

	/* Macros for inserting profiles, only valid in debug builds */
#if defined(_DEBUG)

	/* Start/end a named profile section */
  #define START_PROFILE(Name)	profile_t Name##Profile; \
				SetStartProfile(Name##Profile);
  #define   END_PROFILE(Name)	SetEndProfile(Name##Profile); \
				OutputProfile(Name##Profile, #Name);    

	/* Increment/decrement the tab level output in the log file */
  #define PROFILE_INC() IncreaseProfileTabs()
  #define PROFILE_DEC() DecreaseProfileTabs()

	/* Define macros for profiling functions */
  #define START_FUNC_PROFILE()    static CProfileRecord* pFuncProfile = ProfileList.AddTailData(CProfileRecord(__FUNC__)); \
				  profile_t FuncProfile;  \
				  SetStartProfile(FuncProfile);

  #define END_FUNC_PROFILE()	  SetEndProfile(FuncProfile); \
				  pFuncProfile->AddProfileTime(FuncProfile);

	/* Profile a function from the call point */ 
  #define PROFILE_FUNC2(Func,Name) static CProfileRecord* p##Name##Profile = ProfileList.AddTailData(CProfileRecord(#Name)); \
				  profile_t Name##Profile;  \
				  SetStartProfile(Name##Profile); \
				  Func; \
				  SetEndProfile(Name##Profile); \
				  p##Name##Profile->AddProfileTime(Name##Profile); 

  #define PROFILE_FUNC(Func)    { static CProfileRecord* pFuncProfile = ProfileList.AddTailData(CProfileRecord(#Func)); \
				  profile_t FuncProfile;  \
				  SetStartProfile(FuncProfile); \
				  Func; \
				  SetEndProfile(FuncProfile); \
				  pFuncProfile->AddProfileTime(FuncProfile); }


	/* Define profile macros to nothing in non-debug builds */
#else
  #define START_PROFILE(Name)
  #define END_PROFILE(Name)
  #define PROFILE_INC()
  #define PROFILE_DEC()
  #define START_FUNC_PROFILE()
  #define END_FUNC_PROFILE()
  #define PROFILE_FUNC2(Func,Name) Func;
  #define PROFILE_FUNC(Func) Func;
#endif
  
/*===========================================================================
 *		End of Defines
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Type and Structure Definitions
 *
 *=========================================================================*/

	/* For recording the type of time counter used */
  typedef enum {
    PROFILE_PERFORMANCE_COUNTER,
    PROFILE_CLOCK_COUNTER,
    PROFILE_NONE
   } profile_type_t;


	/* Type for recording profile times */
  typedef struct profile_time_t {
    union {
      clock_t       ClockCount;

      #if defined(_WIN32)
        LARGE_INTEGER TimerCount;
      #endif
     };

    profile_type_t CountType;

		/* Overloaded operators */
    profile_time_t& operator =(const long& Value);
    profile_time_t& operator =(const profile_time_t& Value);
    profile_time_t& operator +=(const profile_time_t& Value);
    profile_time_t& operator -=(const profile_time_t& Value);
    int operator ==(const long& Value);
    int operator ==(const profile_time_t& Value);
    int operator >(const profile_time_t& Value);
    int operator <(const profile_time_t& Value);
    operator float(void);
    friend profile_time_t operator +(const profile_time_t& Value1, const profile_time_t& Value2);
    friend profile_time_t operator -(const profile_time_t& Value1, const profile_time_t& Value2);
    friend profile_time_t operator +(const profile_time_t& Value1);
    friend profile_time_t operator -(const profile_time_t& Value1);
   } profile_time_t;

	/* Main profile type */	
  typedef struct {
    profile_time_t StartTime;
    profile_time_t EndTime;
   } profile_t;

/*===========================================================================
 *		End of Type and Structure Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Class CProfileRecord Definition
 *
 * Defines a profile record which holds information about a profile point
 * in the source code.
 *
 *=========================================================================*/
class CProfileRecord {

  /*---------- Begin Protected Class Members --------------------*/
protected:
  char* pName;		/* The name of the function or profile */
  long  ProfileCount;	/* Number of times the function has been profiled */

  profile_time_t ProfileTime;	/* Total time the function has run */
  profile_time_t MinTime;	/* The max/min profile times */
  profile_time_t MaxTime;


  /*---------- Begin Protected Class Methods --------------------*/
protected:


  /*---------- Begin Public Class Methods -----------------------*/
public:

	/* Class Constructors/Destructors */
  CProfileRecord();
  CProfileRecord(const char* pString);
  virtual ~CProfileRecord() { Destroy(); }
  virtual void Destroy (void);

	/* Adds a profile time to the net time/count for the record */
  void AddProfileTime  (const profile_t Profile);

	/* Get class members */
  char* GetName			(void) const { return (pName); }
  char* GetNiceName		(void) const { return ((pName == NULL) ? "" : pName); }
  long  GetProfileCount		(void) const { return (ProfileCount); }
  profile_time_t GetProfileTime (void) const { return (ProfileTime); }
  profile_time_t GetMaxTime	(void) const { return (MaxTime); }
  profile_time_t GetMinTime	(void) const { return (MinTime); }

	/* Output profile information to a file stream */
  boolean OutputProfile (FILE* pFileHandle = NULL);

	/* Set class members */
  virtual void SetName (const char* pString) { DestroyPointerArray(pName); pName = CreateString(pString); }

	/* Overloaded operators */
  CProfileRecord& operator =(const CProfileRecord& Record);

 };

	/* A linked list of profile records */
typedef CDblListTemplate<CProfileRecord> CProfileList;
/*===========================================================================
 *		End of Class CProfileRecord Definition
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Function Prototypes
 *
 *=========================================================================*/

	/* Retrieve a time count */
  void GetProfileTime (profile_time_t& ProfileTime);

	/* Output profile statistics to the profile log file */
  void OutputProfile (profile_t& Profile, const char* pString);

	/* Outputs the current profile list to a file stream */
  boolean OutputProfileList (FILE* pFileHandle = NULL);

/*===========================================================================
 *		End of Function Prototypes
 *=========================================================================*/


/*===========================================================================
 *
 * Begin External Variables
 *
 *=========================================================================*/

	/* The default destination for all profiling output */
  extern CLogFile ProfileLog;

	/* A list of all current function profiles */
  extern CProfileList ProfileList;

/*===========================================================================
 *		End of External Variables
 *=========================================================================*/

/*===========================================================================
 *
 * Begin Inline Functions
 *
 *=========================================================================*/

	/* Profile time copy operator for long values */
inline profile_time_t& profile_time_t::operator =(const long& Value) { 

  #if defined(_WIN32)
    CountType = PROFILE_PERFORMANCE_COUNTER;
    TimerCount.QuadPart = (LONGLONG) Value;
  #else
    CountType = PROFILE_CLOCK_COUNTER;
    ClockCount = (clock_t) Value;
  #endif

  return (*this);
 }

	/* Profile time copy operator for a profile time value */
inline profile_time_t& profile_time_t::operator =(const profile_time_t& Value) { 
  this->ClockCount = Value.ClockCount;
  this->CountType = Value.CountType;

  #if defined(_WIN32)
    this->TimerCount = Value.TimerCount;
  #endif

  return (*this);
 }


	/* Profile time compare operator for a long value */
inline int profile_time_t::operator ==(const long& Value) { 

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        return (int)(TimerCount.QuadPart == (LONGLONG) Value);
      #else
        return (int)(Value);
      #endif
    case PROFILE_CLOCK_COUNTER:
      return (int)(ClockCount == (clock_t) Value);
    default:
      return (1);
   }
 }    

	/* Profile time greater than operator for two profile times */
inline int profile_time_t::operator >(const profile_time_t& Value) { 
  if (CountType != Value.CountType) return (1);

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        return (int)(TimerCount.QuadPart > Value.TimerCount.QuadPart);
      #else
        return (int)(1);
      #endif
    case PROFILE_CLOCK_COUNTER:
      return (int)(ClockCount > Value.ClockCount);
    default:
      return (1);
   }
 }  
 
	/* Profile time less than operator for two profile times */
inline int profile_time_t::operator <(const profile_time_t& Value) { 
  if (CountType != Value.CountType) return (1);

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        return (int)(TimerCount.QuadPart < Value.TimerCount.QuadPart);
      #else
        return (int)(1);
      #endif
    case PROFILE_CLOCK_COUNTER:
      return (int)(ClockCount < Value.ClockCount);
    default:
      return (1);
   }
 }    
   

	/* Profile time compare operator for two profile times */
inline int profile_time_t::operator ==(const profile_time_t& Value) { 
  if (CountType != Value.CountType) return (1);

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        return (int)(TimerCount.QuadPart == Value.TimerCount.QuadPart);
      #else
        return (int)(1);
      #endif
    case PROFILE_CLOCK_COUNTER:
      return (int)(ClockCount == Value.ClockCount);
    default:
      return (1);
   }
 }  

	/* Profile time addition operator for two profile times */
inline profile_time_t operator +(const profile_time_t& Value1, const profile_time_t& Value2) { 
  profile_time_t TempTime;

	/* Ignore if timer types are not equivalent */
  if (Value1.CountType != Value2.CountType) return (Value1);
  TempTime.CountType = Value1.CountType;

  switch (Value1.CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        TempTime.TimerCount.QuadPart = Value1.TimerCount.QuadPart + Value2.TimerCount.QuadPart;
        return (TempTime);
      #else
        return (Value1);
      #endif
    case PROFILE_CLOCK_COUNTER:
      TempTime = Value1.ClockCount + Value2.ClockCount;
      return (TempTime);
    default:
      return (Value1);
   }
 }    

	/* Profile time subtraction operator for two profile times */
inline profile_time_t operator -(const profile_time_t& Value1, const profile_time_t& Value2) { 
  profile_time_t TempTime;

	/* Ignore if timer types are not equivalent */
  if (Value1.CountType != Value2.CountType) return (Value1);
  TempTime.CountType = Value1.CountType;

  switch (Value1.CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        TempTime.TimerCount.QuadPart = Value1.TimerCount.QuadPart - Value2.TimerCount.QuadPart;
        return (TempTime);
      #else
        return (Value1);
      #endif
    case PROFILE_CLOCK_COUNTER:
      TempTime = Value1.ClockCount - Value2.ClockCount;
      return (TempTime);
    default:
      return (Value1);
   }
 }

	/* Profile time unary positive operator for two profile times */
inline profile_time_t operator +(const profile_time_t& Value) { 
  return (Value);
 }

	/* Profile time unary negative operator for two profile times */
inline profile_time_t operator -(const profile_time_t& Value) { 
  profile_time_t TempTime;
  TempTime.CountType = Value.CountType;

  switch (Value.CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        TempTime.TimerCount.QuadPart = -Value.TimerCount.QuadPart;
        return (TempTime);
      #else
        return (Value1);
      #endif
    case PROFILE_CLOCK_COUNTER:
      TempTime = -Value.ClockCount;
      return (TempTime);
    default:
      return (TempTime);
   }
 }

	/* Profile time addition assignment operator for a profile time */
inline profile_time_t& profile_time_t::operator +=(const profile_time_t& Value) { 

	/* Ignore if timer types are not equivalent */
  if (CountType != Value.CountType) return (*this);

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        TimerCount.QuadPart += Value.TimerCount.QuadPart;
      #endif
      break;
    case PROFILE_CLOCK_COUNTER:
      ClockCount += Value.ClockCount;
      break;
   }
  return (*this);
 }    

	/* Profile time substraction assignment operator for a profile time */
inline profile_time_t& profile_time_t::operator -=(const profile_time_t& Value) { 

	/* Ignore if timer types are not equivalent */
  if (CountType != Value.CountType) return (*this);

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        TimerCount.QuadPart -= Value.TimerCount.QuadPart;
      #endif
      break;
    case PROFILE_CLOCK_COUNTER:
      ClockCount -= Value.ClockCount;
      break;
   }

  return (*this);
 }    

	/* Convert a profile time to a float value */
inline profile_time_t::operator float(void) {

  switch (CountType) {
    case PROFILE_PERFORMANCE_COUNTER:
      #if defined(_WIN32)
        return (float) TimerCount.QuadPart;
      #elif
        return (float) 0.0;
      #endif
    case PROFILE_CLOCK_COUNTER:
      return (float) ClockCount;
    default:
      return (float) 0.0;
   }
 }

	/* Used to start and end a profile session */
inline void SetStartProfile (profile_t& Profile) { GetProfileTime(Profile.StartTime); }
inline void SetEndProfile   (profile_t& Profile) { GetProfileTime(Profile.EndTime); }

	/* Increase/descrease the number of padding tabs in the profile log */
inline void DecreaseProfileTabs (void) { ProfileLog.DecrementTabs(); }
inline void IncreaseProfileTabs (void) { ProfileLog.IncrementTabs(); }


/*===========================================================================
 *		End of Inline Functions
 *=========================================================================*/

#endif
/*===========================================================================
 *		End of File Profile.H
 *=========================================================================*/