#ifdef _DEBUG_COMPONENT
#include "ReferenceCounted.h"
#include <boost/iterator/transform_iterator.hpp>
#include <sstream>
#include <set>
#include <algorithm>
#include <cassert>

struct make_pair : public std::unary_function<const void*, std::pair<const void*, const type_info*>>
{
  std::pair<const void*, const type_info*> operator()(const void* p) const
  {
    return std::make_pair(p, &typeid(p));
  }
};

struct Comparison
{
  template <class First>
  bool operator()(const std::pair<const First*, const type_info*>& p1, const std::pair<const First*, const type_info*>& p2) const
  {
    return p1.first < p2.first;
  }
};

size_t ReferenceCounted::_signalMemoryLeaks()
{
  std::cout << std::endl << "Checking for memory leaks (tracked objects only)..." << std::endl;
  std::set<const void*> leakedObjects;
  std::set<std::pair<const void*, const type_info*>, Comparison> acquirers;
  const Tracked* object = Tracked::_getTail();
  while (object != NULL)
  {
    std::cout << "\"";
    try
    {
      if (object->m_debugInfoSet)
        std::cout << object->m_debugName;
      else
        std::cout << *object;
    }
    catch (...) {}
    std::cout << " (" << typeid(*object).name() << " 0x" << std::hex << static_cast<const void*>(object) << ")";
    std::cout << "\"";

    // Pending references
    const ReferenceCounted* referenceCounted = dynamic_cast<const ReferenceCounted*>(object);
    if (referenceCounted == NULL)
    {
      leakedObjects.insert(static_cast<const void*>(object));
    }
    else
    {
      leakedObjects.insert(static_cast<const void*>(referenceCounted));

      std::cout << " 0x" << std::hex << static_cast<const void*>(referenceCounted) << " has " << std::dec << referenceCounted->m_nbRef << " pending reference(s).";
      for (std::map<const void*, boost::tuple<const type_info*, size_t, bool>>::const_iterator it = referenceCounted->m_acquirers.begin();
           it != referenceCounted->m_acquirers.end();
           ++it)
      {
        acquirers.insert(std::make_pair(it->first, it->second.get<0>()));

        std::cout << std::endl << "    \"" << it->second.get<0>()->name() << " 0x" << std::hex << it->first << "\" " << it->second.get<1>() << " ref(s)";
        if (!it->second.get<2>())
          std::cout << " (NOT REFERENCE COUNTED)";
      }
    }
    std::cout << std::endl;

    object = Tracked::_getPrevious(object);
  }

  if (!leakedObjects.empty())
  {
    // If there are cycles, we're fucked

    std::set<std::pair<const void*, const type_info*>, Comparison> rootAcquirers;
    std::set_difference(acquirers.begin(), 
                        acquirers.end(), 
                        boost::transform_iterator<make_pair, std::set<const void*>::const_iterator>(leakedObjects.begin()),
                        boost::transform_iterator<make_pair, std::set<const void*>::const_iterator>(leakedObjects.end()),
                        std::inserter(rootAcquirers, rootAcquirers.begin()), 
                        Comparison());
    if (rootAcquirers.empty())
    {
      std::cout << std::endl << "Could not find a root acquirer among reference-counted objects..."
                                "this may indicate there's a cycle in the referenced objects." << std::endl;
    }
    else
    {
      std::cout << std::endl << "Root acquirers (of reference-counted objects)..." << std::endl;
      for (std::set<std::pair<const void*, const type_info*>, Comparison>::const_iterator it = rootAcquirers.begin();
           it != rootAcquirers.end();
           ++it)
      {
        assert(leakedObjects.find(it->first) == leakedObjects.end());
        std::cout << "'" << it->second->name() << " " << std::hex << it->first << "'" << std::endl;
      }
    }
  }

  std::cout << std::endl;

  return leakedObjects.size();
}

#endif