#if defined(GD_IDE_ONLY) || defined(DEBUG) || defined(DEV)
/*

****************************************************************************************************
**
** profile.cpp
**
** Real-Time Hierarchical Profiling for Game Programming Gems 3
**
** by Greg Hjelstrom & Byon Garrabrant
**
***************************************************************************************************/

// Credits: The Clock class was inspired by the Timer classes in
// Ogre (www.ogre3d.org).

#include "GDCpp/Runtime/profile.h"

static btClock gProfileClock;

inline void Profile_Get_Ticks(unsigned long int* ticks) {
  *ticks = gProfileClock.getTimeMicroseconds();
}

inline float Profile_Get_Tick_Rate(void) {
  //	return 1000000.f;
  return 1000.f;
}
/***************************************************************************************************
**
** CProfileNode
**
***************************************************************************************************/

/***********************************************************************************************
 * INPUT: * name - pointer to a static string which is the name of this profile
 *node                    * parent - parent pointer *
 *                                                                                             *
 * WARNINGS: * The name is assumed to be a static pointer, only the pointer is
 *stored and compared for     * efficiency reasons. *
 *=============================================================================================*/
CProfileNode::CProfileNode(const char* name, CProfileNode* parent)
    : Name(name),
      TotalCalls(0),
      TotalTime(0),
      StartTime(0),
      RecursionCounter(0),
      Parent(parent),
      Child(NULL),
      Sibling(NULL) {
  Reset();
}

void CProfileNode::CleanupMemory() {
  delete (Child);
  Child = NULL;
  delete (Sibling);
  Sibling = NULL;
}

CProfileNode::~CProfileNode(void) {
  delete (Child);
  delete (Sibling);
}

/***********************************************************************************************
 * INPUT: * name - static string pointer to the name of the node we are
 *searching for                   *
 *                                                                                             *
 * WARNINGS: * All profile names are assumed to be static strings so this
 *function uses pointer compares   * to find the named node. *
 *=============================================================================================*/
CProfileNode* CProfileNode::Get_Sub_Node(const char* name) {
  // Try to find this sub node
  CProfileNode* child = Child;
  while (child) {
    if (child->Name == name) {
      return child;
    }
    child = child->Sibling;
  }

  // We didn't find it, so add it

  CProfileNode* node = new CProfileNode(name, this);
  node->Sibling = Child;
  Child = node;
  return node;
}

void CProfileNode::Reset(void) {
  TotalCalls = 0;
  TotalTime = 0.0f;

  if (Child) {
    Child->Reset();
  }
  if (Sibling) {
    Sibling->Reset();
  }
}

void CProfileNode::Call(void) {
  TotalCalls++;
  if (RecursionCounter++ == 0) {
    Profile_Get_Ticks(&StartTime);
  }
}

bool CProfileNode::Return(void) {
  if (--RecursionCounter == 0 && TotalCalls != 0) {
    unsigned long int time;
    Profile_Get_Ticks(&time);
    time -= StartTime;
    TotalTime += (float)time / Profile_Get_Tick_Rate();
  }
  return (RecursionCounter == 0);
}

/***************************************************************************************************
**
** CProfileIterator
**
***************************************************************************************************/
CProfileIterator::CProfileIterator(CProfileNode* start) {
  CurrentParent = start;
  CurrentChild = CurrentParent->Get_Child();
}

void CProfileIterator::First(void) {
  CurrentChild = CurrentParent->Get_Child();
}

void CProfileIterator::Next(void) {
  CurrentChild = CurrentChild->Get_Sibling();
}

bool CProfileIterator::Is_Done(void) { return CurrentChild == NULL; }

void CProfileIterator::Enter_Child(int index) {
  CurrentChild = CurrentParent->Get_Child();
  while ((CurrentChild != NULL) && (index != 0)) {
    index--;
    CurrentChild = CurrentChild->Get_Sibling();
  }

  if (CurrentChild != NULL) {
    CurrentParent = CurrentChild;
    CurrentChild = CurrentParent->Get_Child();
  }
}

void CProfileIterator::Enter_Parent(void) {
  if (CurrentParent->Get_Parent() != NULL) {
    CurrentParent = CurrentParent->Get_Parent();
  }
  CurrentChild = CurrentParent->Get_Child();
}

/***************************************************************************************************
**
** CProfileManager
**
***************************************************************************************************/

CProfileNode CProfileManager::Root("Root", NULL);
CProfileNode* CProfileManager::CurrentNode = &CProfileManager::Root;
int CProfileManager::FrameCounter = 0;
unsigned long int CProfileManager::ResetTime = 0;

/***********************************************************************************************
 * CProfileManager::Start_Profile -- Begin a named profile *
 *                                                                                             *
 * Steps one level deeper into the tree, if a child already exists with the
 *specified name     * then it accumulates the profiling; otherwise a new child
 *node is added to the profile tree. *
 *                                                                                             *
 * INPUT: * name - name of this profiling record *
 *                                                                                             *
 * WARNINGS: * The string used is assumed to be a static string; pointer
 *compares are used throughout      * the profiling code for efficiency. *
 *=============================================================================================*/
void CProfileManager::Start_Profile(const char* name) {
  if (name != CurrentNode->Get_Name()) {
    CurrentNode = CurrentNode->Get_Sub_Node(name);
  }

  CurrentNode->Call();
}

/***********************************************************************************************
 * CProfileManager::Stop_Profile -- Stop timing and record the results. *
 *=============================================================================================*/
void CProfileManager::Stop_Profile(void) {
  // Return will indicate whether we should back up to our parent (we may
  // be profiling a recursive function)
  if (CurrentNode->Return()) {
    CurrentNode = CurrentNode->Get_Parent();
  }
}

/***********************************************************************************************
 * CProfileManager::Reset -- Reset the contents of the profiling system *
 *                                                                                             *
 *    This resets everything except for the tree structure.  All of the timing
 *data is reset.  *
 *=============================================================================================*/
void CProfileManager::Reset(void) {
  gProfileClock.reset();
  Root.Reset();
  Root.Call();
  FrameCounter = 0;
  Profile_Get_Ticks(&ResetTime);
}

/***********************************************************************************************
 * CProfileManager::Increment_Frame_Counter -- Increment the frame counter *
 *=============================================================================================*/
void CProfileManager::Increment_Frame_Counter(void) { FrameCounter++; }

/***********************************************************************************************
 * CProfileManager::Get_Time_Since_Reset -- returns the elapsed time since last
 *reset         *
 *=============================================================================================*/
float CProfileManager::Get_Time_Since_Reset(void) {
  unsigned long int time;
  Profile_Get_Ticks(&time);
  time -= ResetTime;
  return (float)time / Profile_Get_Tick_Rate();
}

#include <stdio.h>

void CProfileManager::dumpRecursive(CProfileIterator* profileIterator,
                                    int spacing) {
  profileIterator->First();
  if (profileIterator->Is_Done()) return;

  float accumulated_time = 0,
        parent_time = profileIterator->Is_Root()
                          ? CProfileManager::Get_Time_Since_Reset()
                          : profileIterator->Get_Current_Parent_Total_Time();
  int i;
  int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset();
  for (i = 0; i < spacing; i++) printf(".");
  printf("----------------------------------\n");
  for (i = 0; i < spacing; i++) printf(".");
  printf("Profiling: %s (total running time: %.3f ms) ---\n",
         profileIterator->Get_Current_Parent_Name(),
         parent_time);
  float totalTime = 0.f;

  int numChildren = 0;

  for (i = 0; !profileIterator->Is_Done(); i++, profileIterator->Next()) {
    numChildren++;
    float current_total_time = profileIterator->Get_Current_Total_Time();
    accumulated_time += current_total_time;
    float fraction = parent_time > SIMD_EPSILON
                         ? (current_total_time / parent_time) * 100
                         : 0.f;
    {
      int i;
      for (i = 0; i < spacing; i++) printf(".");
    }
    printf("%d -- %s (%.2f %%) :: %.3f ms / frame (%d calls)\n",
           i,
           profileIterator->Get_Current_Name(),
           fraction,
           (current_total_time / (double)frames_since_reset),
           profileIterator->Get_Current_Total_Calls());
    totalTime += current_total_time;
    // recurse into children
  }

  if (parent_time < accumulated_time) {
    printf("what's wrong\n");
  }
  for (i = 0; i < spacing; i++) printf(".");
  printf("%s (%.3f %%) :: %.3f ms\n",
         "Unaccounted:",
         parent_time > SIMD_EPSILON
             ? ((parent_time - accumulated_time) / parent_time) * 100
             : 0.f,
         parent_time - accumulated_time);

  for (i = 0; i < numChildren; i++) {
    profileIterator->Enter_Child(i);
    dumpRecursive(profileIterator, spacing + 3);
    profileIterator->Enter_Parent();
  }
}
void CProfileManager::dumpAll() {
  CProfileIterator* profileIterator = 0;
  profileIterator = CProfileManager::Get_Iterator();

  dumpRecursive(profileIterator, 0);

  CProfileManager::Release_Iterator(profileIterator);
}

#endif
