/*===========================================================================
 *
 * Profile.CPP - Dave Humphrey (uesp@m0use.net), 23 November 2000
 *
 *=========================================================================*/

	/* Include Files */
#include "profile.h"


/*===========================================================================
 *
 * Begin Global Variables
 *
 *=========================================================================*/

	/* Define and automatically open the profile log file */
  CLogFile ProfileLog;
  boolean  OpenLog = ProfileLog.Open("profile.log");

	/* List of current profiles */
  CProfileList ProfileList;

/*===========================================================================
 *		End of Global Variables
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CProfileRecord::CProfileRecord()"
/*===========================================================================
 *
 * Class CProfileRecord Constructor
 *
 *=========================================================================*/
CProfileRecord::CProfileRecord () {
  pName = NULL;
  ProfileCount = 0;
  ProfileTime = 0;
  MaxTime = 0;
  MinTime = 0;
 }
/*===========================================================================
 *		End of Class CProfileRecord Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CProfileRecord::CProfileRecord()"
/*===========================================================================
 *
 * Class CProfileRecord Constructor
 *
 *=========================================================================*/
CProfileRecord::CProfileRecord (const char* pString) {
  pName = CreateString(pString);
  ProfileCount = 0;
  ProfileTime = 0;
  MaxTime = 0;
  MinTime = 0;
 }
/*===========================================================================
 *		End of Class CProfileRecord Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CProfileRecord::Destroy()"
/*===========================================================================
 *
 * Class CProfileRecord Method - void Destroy (void)
 *
 *=========================================================================*/
void CProfileRecord::Destroy (void) {
  DestroyPointerArray(pName);
  ProfileCount = 0;
  ProfileTime = 0;
  MaxTime = 0;
  MinTime = 0;
 }
/*===========================================================================
 *		End of Class Method CProfileRecord::Destroy()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CProfileRecord::AddProfileTime()"
/*===========================================================================
 *
 * Class CProfileRecord Method - void AddProfileTime (const profile_t Profile)
 *
 * Adds a profile time to the current record.
 *
 *=========================================================================*/
void CProfileRecord::AddProfileTime (const profile_t Profile) { 
  profile_time_t DiffTime = Profile.EndTime - Profile.StartTime;

  ProfileTime += DiffTime;
  ProfileCount++; 
  
	/* Compute the max/min time values */
  if (ProfileCount == 1) {
    MaxTime = DiffTime;
    MinTime = DiffTime;
   }
  else if (DiffTime > MaxTime) 
    MaxTime = DiffTime;
  else if (DiffTime < MinTime)
    MinTime = DiffTime;

 }
/*===========================================================================
 *		End of Class Method CProfileRecord::AddProfileTime()
 *=========================================================================*/
  

#undef  __FUNC__
#define __FUNC__ "CProfileRecord::OutputProfile()"
/*===========================================================================
 *
 * Class CProfileRecord Method - boolean OutputProfile (FILE* pFileHandle)
 *
 * Outputs the current profile information of the record to the given
 * file stream.  If the input file handle is NULL, the current profile
 * log file is used.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CProfileRecord::OutputProfile (FILE* pFileHandle) {
  float Count;
  float Seconds;
  float Rate;
  float Frequency;
  int   Result;

	/* Ensure a valid file handle */
  if (pFileHandle == NULL) {
    pFileHandle = ProfileLog.GetFileHandle();
    CHECK_POINTER(pFileHandle, FALSE);
   }

	/* Output counter values */
  switch (ProfileTime.CountType) {

		/* Use the peroformance counter */
    case PROFILE_PERFORMANCE_COUNTER: {

      #ifdef _WIN32
        LARGE_INTEGER Freq;
	
        Count = (float) ProfileTime.TimerCount.QuadPart;
	Result = QueryPerformanceFrequency(&Freq);

        if (!Result || Freq.QuadPart == 0) 
	  Frequency = (float) 1.0;
        else
	  Frequency = (float) Freq.QuadPart;

      #else
        ProfileLog.Printf ("\t%s: Performance counter not available on this platform!", GetNiceName());
	return (FALSE);
      #endif

      break; }

		/* Use the regular clock timer */
    case PROFILE_CLOCK_COUNTER: 
      Count = (float) ProfileTime.ClockCount;
      Frequency = (float) CLOCKS_PER_SEC;
      break; 

		/* Unknown timer type */
    default:
      ProfileLog.Printf ("\t%s: Invalid timer type specified!", GetNiceName());
      break; 
   }

	/* Compute the rate and total profile time */  
  Seconds = Count / Frequency;
  Rate = ((float) ProfileCount) / Seconds;

	/* Output the result to the file stream */
  Result = fprintf (pFileHandle, "\t%s: Total %g sec (%ld calls), %g calls/sec (%g sec/call) (Min/Max Times = %g / %g sec)\n", GetNiceName(), Seconds, ProfileCount, Rate, 1.0/Rate, (float)MinTime/Frequency, (float)MaxTime/Frequency);

  if (Result < 0) {
    SET_EXT_ERROR(ERR_WRITE);
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CProfileRecord::OutputProfile()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CProfileRecord::operator =()"
/*===========================================================================
 *
 * Class CProfileRecord Method - CProfileRecord& operator =(Record)
 *
 * Overloaded copy operator.
 *
 *=========================================================================*/
CProfileRecord& CProfileRecord::operator =(const CProfileRecord& Record) {

  SetName(Record.GetName());
  ProfileTime = Record.GetProfileTime();
  ProfileCount = Record.GetProfileCount();
  MaxTime = Record.GetMaxTime();
  MinTime = Record.GetMinTime();

  return (*this);
 }
/*===========================================================================
 *		End of Class Method CProfileRecord::operator =()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "GetProfileTime()"
/*===========================================================================
 *
 * Function - void GetProfileTime (ProfileTime);
 *
 * Get the current profile counter value.
 *
 *=========================================================================*/
void GetProfileTime (profile_time_t& ProfileTime) {

	/* Attempt to use the performance counter */
  #ifdef _WIN32
    boolean Result;
    
		/* Attempt to get the performance timer count */
    Result = QueryPerformanceCounter(&ProfileTime.TimerCount);

		/* Set the counter type and return on success */
    if (Result) {
      ProfileTime.CountType = PROFILE_PERFORMANCE_COUNTER;
      return;
     }
  #endif

  ProfileTime.ClockCount = clock();
  ProfileTime.CountType  = PROFILE_CLOCK_COUNTER;
 }
/*===========================================================================
 *		End of Function GetProfileTime()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "OutputProfile()"
/*===========================================================================
 *
 * Function - void OutputProfile (Profile, pString);
 *
 * Outputs a profile statistics to the profile log file.
 *
 *=========================================================================*/
void OutputProfile (profile_t& Profile, const char* pString) {
  
	/* Ensure valid count types */
  if (Profile.EndTime.CountType != Profile.StartTime.CountType) {
    ProfileLog.Printf ("PROFILE %s: Times are not in the same timer units!", pString);
    return;
   }

	/* Output counter values */
  switch (Profile.EndTime.CountType) {

		/* Use the peroformance counter */
    case PROFILE_PERFORMANCE_COUNTER: {

      #ifdef _WIN32
        LARGE_INTEGER Diff;
	LARGE_INTEGER Freq;
	boolean       Result;

	Diff.QuadPart = Profile.EndTime.TimerCount.QuadPart - Profile.StartTime.TimerCount.QuadPart; 
	Result = QueryPerformanceFrequency(&Freq);

        if (Result) 
	  ProfileLog.Printf ("PROFILE %s: %.4f sec (%I64d counts)", pString, (float)Diff.QuadPart / (float)Freq.QuadPart, Diff.QuadPart);
        else	
	  ProfileLog.Printf ("PROFILE %s: %I64d counts", pString, (long) Diff.QuadPart);

      #else
        ProfileLog.Printf ("PROFILE %s: Performance counter not available on this platform!", pString);
      #endif

      break; }

		/* Use the regular clock timer */
    case PROFILE_CLOCK_COUNTER: {
      clock_t Diff = Profile.EndTime.ClockCount - Profile.StartTime.ClockCount;

      ProfileLog.Printf ("PROFILE %s: %.4f sec", pString, (float)Diff / (float)CLOCKS_PER_SEC);
      break; }

		/* Unknown timer type */
    default:
      ProfileLog.Printf ("PROFILE %s: Invalid timer type specified!", pString);
      break; 
   }

 }
/*===========================================================================
 *		End of Function OutputProfile()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "OutputProfileList()"
/*===========================================================================
 *
 * Function - boolean OutputProfileList (FILE* pFileHandle)
 *
 * Outputs the results of the ProfileList to the given file stream.  Returns
 * FALSE on any error.  If the input file stream is NULL, the current 
 * ProfileLog file is used.
 *
 *=========================================================================*/
boolean OutputProfileList (FILE* pFileHandle) {
  CProfileRecord* pProfileRecord;
  boolean	  Result;

	/* Ensure a valid file handle */
  if (pFileHandle == NULL) {
    pFileHandle = ProfileLog.GetFileHandle();
    CHECK_POINTER(pFileHandle, FALSE);
   }

	/* Start at the head of the list */
  pProfileRecord = ProfileList.GetHeadData();

  while (pProfileRecord != NULL) {
    Result = pProfileRecord->OutputProfile(pFileHandle);
    if (!Result) return (FALSE);
    pProfileRecord = ProfileList.GetNext();
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Function OutputProfileList()
 *=========================================================================*/