#ifndef _PROFILER_H
#define _PROFILER_H

#include <bfc/wasabi_std.h>
#include <bfc/string/bfcstring.h>
#include <bfc/ptrlist.h>


#ifdef NO_PROFILING
#define PR_ENTER(msg) 
#define PR_LEAVE()
#else

#define _PR_ENTER(msg, line) { __Profiler __prx##line(msg) 
#define _PR_ENTER2(msg, msg2, line) { __Profiler __prx##line(msg, msg2) 
#define PR_ENTER(msg) _PR_ENTER(msg, line)
#define PR_ENTER2(msg, msg2) _PR_ENTER2(msg, msg2, line) 
#define PR_LEAVE() }

class __ProfilerEntry {
  public: 
    __ProfilerEntry(const char *txt) { text = txt; totaltime = 0; totaln = 0; subcount = 0; lastcps = -1;}
    virtual ~__ProfilerEntry() {}

    void add(float ms) { totaltime += ms; totaln++; 
                         if (subcount == 0) {
                           firstcall = Wasabi::Std::getTimeStampMS();
                         }
                         if (Wasabi::Std::getTimeStampMS() - firstcall < 1) {
                           subcount++; 
                         } else {
                           lastcps = subcount;
                           subcount = 0;
                         }
                       }
    float getAverage() { if (totaln == 0) return 0; return totaltime / (float)totaln; }
    float getTotal() { return totaltime; }
    const char *getText() { return text; }
    int getLastCPS() { return lastcps; }

  private:
    float totaltime;
    int totaln;
    stdtimevalms firstcall;
    int lastcps;
    int subcount;
    String text;
};

class __ProfilerEntrySort {
public:
  static int compareAttrib(const wchar_t *attrib, void *item) {
    return STRICMP((const char *)attrib, ((__ProfilerEntry*)item)->getText());
  }
  static int compareItem(void *i1, void *i2) {
    return STRICMP(((__ProfilerEntry*)i1)->getText(), ((__ProfilerEntry*)i2)->getText());
  }
};

extern COMEXP PtrListInsertSorted<__ProfilerEntry, __ProfilerEntrySort> __profiler_entries;
extern COMEXP int __profiler_indent;

class __ProfilerManager {
  public:
  static void log(const char *txt, float ms, float *total, float *average, int *lastcps) {
    int pos=-1;
    __ProfilerEntry *e = __profiler_entries.findItem((const wchar_t *)txt, &pos);
    if (pos < 0 || e == NULL) {
      e = new __ProfilerEntry(txt);
      __profiler_entries.addItem(e);
    }
    if (e != NULL) {
      e->add(ms);
      if (total != NULL) *total = e->getTotal();
      if (average != NULL) *average = e->getAverage();
      if (lastcps != NULL) *lastcps = e->getLastCPS();
    }
  }
};

#undef USE_TICK_COUNT

class __Profiler {
public:
  __Profiler(const char *text, const char *text2="") : str(text), str2(text2) {
    if (!str2.isempty()) str2 += " ";
#ifdef USE_TICK_COUNT
    ts1 = GetTickCount();
#else
    ts1 = Wasabi::Std::getTimeStampMS();
#endif
    __profiler_indent++;
  }
  ~__Profiler() {
    __profiler_indent--;
#ifdef USE_TICK_COUNT
    stdtimevalms ts2 = GetTickCount();
#else
    stdtimevalms ts2 = Wasabi::Std::getTimeStampMS();
#endif
    float ms = (float)((ts2 - ts1)
#ifndef USE_TICK_COUNT
*1000.0
#endif
);
    float total=0;
    float average=0;
    int lastcps=0;
    __ProfilerManager::log(str, ms, &total, &average, &lastcps);
    char buf[4096];
    if (lastcps >= 0) 
      sprintf(buf, "%*sProfiler: %s: %s%6.4f ms (total: %6.4f ms, average: %6.4f ms, calls per second : %d)\n", __profiler_indent*4, " ", str.getValue(), str2.getValue(), ms, total, average, lastcps);
    else
      sprintf(buf, "%*sProfiler: %s: %s%6.4f ms (total: %6.4f ms, average: %6.4f ms)\n", __profiler_indent*4, " ", str.getValue(), str2.getValue(), ms, total, average);
#ifdef _WIN32
    OutputDebugStringA(buf);
#else
#warning port me
#endif
  }
private:
  String str, str2;
  stdtimevalms ts1;
};

#endif//!NO_PROFILING

#endif