/**********************************************************************

  Audacity: A Digital Audio Editor

  Profiler.cpp
  
  Created by Michael Chinen (mchinen) on 8/12/08
  Audacity(R) is copyright (c) 1999-2008 Audacity Team.
  License: GPL v2.  See License.txt.

******************************************************************//**

\class Profiler
\brief A simple profiler to measure the average time lengths that a 
particular task/function takes.  Currently not thread-safe and not thread-smart,
but it will probably work fine if you use it on a high level.

\class TaskProfile
\brief a simple class to keep track of one task that may be called multiple times.

*//*******************************************************************/

#include "Profiler.h"

///write to a profile at the end of the test.
Profiler::~Profiler()
{
   if(mTasks.size())
   {
      //print everything out.  append to a log.
      FILE* log = fopen("AudacityProfilerLog.txt", "a");
      time_t now;
      
      time(&now);
      fprintf(log,"Audacity Profiler Run, Ended at ");
      fprintf(log,"%s",ctime(&now));
      fprintf(log,"****************************************\n");
      //print out the tasks
      for(int i=0;i<(int)mTasks.size();i++)
      {
         if(mTasks[i]->mNumHits>0)
         {
            fprintf(log,"Task: %s\n(begins at line %d in %s)\n\n",mTasks[i]->mDescription,mTasks[i]->mLine,mTasks[i]->mFileName);
            fprintf(log,"Number of times run: %d\n",mTasks[i]->mNumHits);
            fprintf(log,"Total run time (seconds): %f\n", (double)mTasks[i]->mCumTime/CLOCKS_PER_SEC);
            fprintf(log,"Average run time (seconds): %f\n",mTasks[i]->ComputeAverageRunTime());
            
            if(i < ((int)mTasks.size()) -1)
               fprintf(log,"----------------------------\n");
         }
      }
      fprintf(log,"\n****************************************\n\n\n");
      
      fclose(log);
      
      //delete everything.
      for(int i=0;i<(int)mTasks.size();i++)
         delete mTasks[i];
   }
}

///start the task timer.
void Profiler::Begin(char* fileName, int lineNum, char* taskDescription)
{
   mTasksMutex.Lock();
   GetOrCreateTaskProfile(fileName,lineNum)->Begin(fileName,lineNum,taskDescription);
   mTasksMutex.Unlock();
}
  
///end the task timer.
void Profiler::End(char* fileName, int lineNum, char* taskDescription)
{
   mTasksMutex.Lock();
   TaskProfile* tp;
   tp=GetTaskProfileByDescription(taskDescription);
   if(tp)
      tp->End(fileName,lineNum,taskDescription);
   mTasksMutex.Unlock();
}
  
///Gets the singleton instance
Profiler* Profiler::Instance()
{
   static Profiler* pro=NULL;
   //this isn't 100% threadsafe but I think Okay for this purpose.
   
   if(!pro)
      pro = new Profiler();
   
   return pro;

}

///find a taskProfile for the given task, otherwise create
TaskProfile* Profiler::GetOrCreateTaskProfile(char* fileName, int lineNum)
{
   for(int i=0;i<(int)mTasks.size();i++)
   {
      if(strcmp(fileName,mTasks[i]->mFileName)==0 && lineNum == mTasks[i]->mLine)
         return mTasks[i];
   }
   
   TaskProfile* tp = new TaskProfile();
   mTasks.push_back(tp);
   return tp;
}

TaskProfile* Profiler::GetTaskProfileByDescription(char* description)
{
   for(int i=0;i<(int)mTasks.size();i++)
   {
      if(strcmp(description,mTasks[i]->mDescription)==0)
         return mTasks[i];
   }
   
   return NULL;

}


///Task Profile
TaskProfile::TaskProfile()
{
   mFileName = NULL;
   mCumTime=0;
   mNumHits=0;
}

TaskProfile::~TaskProfile()
{
   if(mFileName)
   {
      delete [] mFileName;
      delete [] mDescription;
   }
}

///start the task timer.
void TaskProfile::Begin(char* fileName, int lineNum, char* taskDescription)
{
   if(!mFileName)
   {
      mFileName = new char[strlen(fileName)+1];
      strcpy(mFileName,fileName);
      mDescription = new char[strlen(taskDescription)+1];
      strcpy(mDescription,taskDescription);
      mLine = lineNum;
   }

   mLastTime = clock();
   
}

///end the task timer.
void TaskProfile::End(char* fileName, int lineNum, char* taskDescription)
{
   mCumTime += clock() - mLastTime;
   mNumHits++;
}
      
double TaskProfile::ComputeAverageRunTime()
{
   if(mNumHits)
      return (double) ((double)mCumTime/CLOCKS_PER_SEC)/mNumHits;
   else
      return 0.0;
}
    