#pragma once
#include <iostream>
#include <cassert>

#ifndef _DEBUG_COMPONENT
/**
 * @brief "Tracked" objects are kept in memory until they are destroyed, in order to identify 
 *        potential memory leaks. The objects are actually linked with each other directly, instead
 *        of using an external list.
 */
class Tracked 
{
public:
  virtual ~Tracked()
  {
  }

  friend std::ostream& operator<<(std::ostream& out, const Tracked& tracked)
  {
    tracked._output(out);
    return out;
  }
protected:
  virtual void _output(std::ostream& out) const = 0;
};
#else
#include "Utils.h"

/**
 * @brief "Tracked" objects are kept in memory until they are destroyed, in order to identify 
 *        potential memory leaks. The objects are actually linked with each other directly, instead
 *        of using an external list.
 */
class Tracked
{
public:
  /**
   * @brief Creates a new Tracked instance and adds the instance to the chain of instances.
   */
  Tracked()
    : m_debugInfoSet(false)
    , m_debugName("Debug name not set yet. Problem?")
#ifdef _DEBUG
    , m_previous(NULL)
    , m_next(NULL)
#endif
  {
    _addToList();
  }

  /**
   * @brief Creates a copy of an existing Tracked instance and adds the instance to the chain of instances.
   */
  Tracked(const Tracked&)
#ifdef _DEBUG
    : m_previous(NULL)
    , m_next(NULL)
#endif
  {
    _addToList();
  }

  /**
   * @brief Destroy the Tracked instance and removes it from the chain of instances.
   */
  virtual ~Tracked()
  {
    _removeFromList();
  }

  /**
   * @brief Sets the debugging information related to this component, if not already present.
   *        The debugging information cannot be set during construction since it depends on virtual calls.
   * @return True if the information has been set, false if it was already present.
   */
  bool _setDebugInfo() const;
  /**
   * @brief Static method to set the debugging information of newly-instantiated components.
   */
  static void _setComponentsDebugInfo();

  /**
   * @brief Prints memory leaks.
   */
  static size_t _signalMemoryLeaks();

  static const Tracked* _getTail()
  {
    return s_tail;
  }

  static const Tracked* _getPrevious(const Tracked* tracked)
  {
    return tracked->m_previous;
  }

  static const Tracked* _getNext(const Tracked* tracked)
  {
    return tracked->m_next;
  }

  /**
   * @brief Outputs the Tracked instance of the given output stream.
   * @param out The output stream.
   * @param tracked The Tracked instance.
   * @return The same output stream.
   */
  friend std::ostream& operator<<(std::ostream& out, const Tracked& tracked)
  {
    tracked._output(out);
    return out;
  }

protected:
  /**
   * @brief Outputs the Tracked instance of the given output stream.
   * @param out The _output stream.
   */
  virtual void _output(std::ostream& out) const = 0;

  /**
   * @brief Adds the Tracked instance to the chain of instances.
   */
  void _addToList() const
  {
#ifdef _DEBUG
    // Make sure it hasn't been added already (for example by calling the constructor twice due 
    // to an inheritance error...)
    assert(this->m_previous == NULL && this->m_next == NULL && s_tail != this);
#endif

    this->m_previous = s_tail;
    this->m_next = NULL;
    if (s_tail != NULL)
      s_tail->m_next = this;
    s_tail = this;
  }

  /**
   * @brief Removes the Tracked instance from the chain of instances.
   */
  void _removeFromList() const
  {
#ifdef _DEBUG
    // Make sure it hasn't been added already (for example by calling the constructor twice due 
    // to an inheritance error...)
    assert(this->m_previous != NULL || this->m_next != NULL || s_tail == this);
#endif

    if (this->m_previous != NULL)
      this->m_previous->m_next = this->m_next;
    if (this->m_next != NULL)
      this->m_next->m_previous = this->m_previous;
    if (s_tail == this)
      s_tail = this->m_previous;
  }

public:
  /**
   * @brief Whether the debugging information has been set.
   */
  mutable bool m_debugInfoSet;
  /**
   * @brief The debugging information, which consists solely in the name of this component. The component's name
   *        cannot be acquired during construction since the virtual method Component::_output() is called.
   */
  mutable std::string m_debugName;

private:
  static const Tracked* s_tail;

  mutable const Tracked* m_previous;

  mutable const Tracked* m_next;
};


#endif