#ifdef DEBUG_MEMORY_LEAK

#include "Common/include/Message.h"
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstddef>
#include <map>

using namespace std;
using namespace morpheo;

#undef new

// Global flags set by macros in MemCheck.h
bool traceFlag  = false;
bool activeFlag = false;

namespace {

  // Memory map entry type
  struct Info 
  {
//  void* ptr;
    const char* file;
    long        line;
    size_t      size;
  };

  bool Info_lt(std::pair<void *,Info> a, std::pair<void *,Info> b)
  {
    return a.second.size < b.second.size;
  }
  
  // Memory map data
  typedef map<void *,Info>           memMap_t;
  typedef map<void *,Info>::iterator memMap_it;

  memMap_t memMap;

  // Searches the map for an address
  memMap_it findPtr(void* p) 
  {
    return memMap.find(p);
  }
  
  void delPtr(void* p) 
  {
    memMap_it it = findPtr(p);

    assert(it != memMap.end());
    
    // Remove pointer from map
    memMap.erase(it);
  }

  // Dummy type for static destructor
  struct Sentinel 
  {
    ~Sentinel() 
    {
      size_t size = memMap.size();
      if(size > 0) 
	{
	  size_t size_sum = 0;
	  msgWarning("Leaked memory at:\n");
	  for (memMap_it it=memMap.begin();
	       it!=memMap.end();
	       ++it)
	    {
	      size_t s = it->second.size;
	      size_sum += s;
	      
	      msgWarning("\t%p :  %u bytes (file: %s, line %ld)\n",
		     it->first,
		     s,
		     it->second.file,
		     it->second.line);
	    }
	  msgWarning("Total : %u bytes on %d localisations\n",size_sum, size);

	  uint32_t nb_top = (size>10)?10:size;
	  msgWarning("Top %d of leaked memory\n",nb_top);
	  for (uint32_t i=0; i<nb_top; ++i)
	    {
	      memMap_it max = max_element(memMap.begin(), memMap.end(),Info_lt);

	      msgWarning("  * [%d] %u bytes (file: %s, line %ld)\n",
			 i,
			 max->second.size,
			 max->second.file,
			 max->second.line);

	      memMap.erase(max);
	    }
	}
      else
	msgInformation("No user memory leaks!\n");
    }
  };
  
  // Static dummy object
  Sentinel s;
  
} // End anonymous namespace

// Overload scalar new
void* operator new(size_t size, const char* file, long line) 
{
  void* p = malloc(size);

  if(activeFlag) 
    {
      Info info;
      info.file = file;
      info.line = line;
      info.size = size;

      memMap[p] = info;
    }
  
  if(traceFlag) 
    {
      msgInformation("Allocated %u bytes at address %p (file: %s, line: %ld)\n", size, p, file, line);
    }

  return p;
}

// Overload array new
void* operator new[](size_t size, const char* file, long line) 
{
  return operator new(size, file, line);
}

// Override scalar delete
void operator delete(void* p) 
{
  bool can_free = true;
  
  if (activeFlag)
    {
      memMap_it it = findPtr(p);
      
      if (it != memMap.end())
        {
          // this pointer is previously allocated
          delPtr(p);
          
        }
      else
        {
          // this pointer is not previously allocated

          can_free = false;
          msgError("Attempt to delete unknown pointer: %p\n", p);
        }
    }

  
  if (can_free)
    {
      if(traceFlag)
        msgInformation("Deleted memory at address %p\n", p);
      
      free(p);
    }
}

// Override array delete
void operator delete[](void* p) 
{
  operator delete(p);
} 

#endif // DEBUG_MEMORY_LEAK
