// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef tools_mem
#define tools_mem

#ifdef TOOLS_MEM
// to count instances.

// WARNING : it uses writable static data, then it is NOT thread safe.
//           This class must be used for debugging only.

#include <ostream>
#include <list>
#include <string>
#include <cstring> //strcmp

namespace tools {

class mem {
  static const std::string& s_class() {
    static const std::string s_v("tools::mem");
    return s_v;
  }
protected:
  mem(){increment(s_class().c_str());}
  virtual ~mem(){decrement(s_class().c_str());}
  mem(const mem&){}
  mem& operator=(const mem&){return *this;}
public:
//static void increment(){counter()++;}
//static void decrement(){counter()--;}

  static void increment(const char* a_class){
    counter()++;
    if(check_by_class()) {
      mem_list::iterator it;
      for(it=list().begin();it!=list().end();++it) {
        if(!::strcmp((*it).first.c_str(),a_class)) {
          (*it).second++;
          return;
        }
      }
      list().push_back(std::pair<std::string,int>(std::string(a_class),1));
    }
  }

  static void decrement(const char* a_class,unsigned int a_num = 1){
    counter() -= a_num;
    if(check_by_class()) {
      mem_list::iterator it;
      for(it=list().begin();it!=list().end();++it) {
        if(!::strcmp((*it).first.c_str(),a_class)) {
          (*it).second -= a_num;
          return;
        }
      }
      list().push_back(std::pair<std::string,int>(std::string(a_class),-int(a_num)));
    }
  }

  static void set_check_by_class(bool a_value) {
    check_by_class() = a_value;
  }
/*
  static void reset();
*/

  static void balance(std::ostream& a_out){
    if(counter()) {
      a_out << "tools::mem::balance :"
            << " bad global object balance : " << counter() 
            << std::endl;
      if(check_by_class()) {
        a_out << "tools::mem::balance :"
              << " check by class was enabled." 
              << std::endl;
      } else {
        a_out << "tools::mem::balance :"
              << " check by class was disabled." 
              << std::endl;
      }
    }
    mem_list::iterator it;
    for(it=list().begin();it!=list().end();++it) {
      if((*it).second) {
        a_out << "tools::mem::balance :"
              << " for class " << (*it).first 
              << ", bad object balance : " << (*it).second
              << std::endl;
      }
    }
    list().clear();
  }
  
protected:
public: //for MT disconnection (see test/mt_root_ntuple.cpp).
  static int& counter() {
    static int s_count = 0;
    return s_count;
  }
  
  static bool& check_by_class() {
    static bool s_check_by_class = false;
    return s_check_by_class;
  }
protected:

  typedef std::list< std::pair<std::string,int> > mem_list;

  static mem_list& list() {
    static mem_list* s_list = new mem_list(); //have it on the heap, so that an atexit logic works.
    return *s_list;
  }
};

inline const std::string& s_new() {
  static const std::string s_v("new");
  return s_v;
}

inline const std::string& s_malloc() {
  static const std::string s_v("malloc");
  return s_v;
}

inline const std::string& s_tex() {
  static const std::string s_v("tex");
  return s_v;
}

inline const std::string& s_gsto() {
  static const std::string s_v("gsto");
  return s_v;
}

}

#endif

#endif




