#pragma once
#include "../../Utils/Tracked.h"
#include <sstream>
#include <set>

class EventHandlerBase;
class SchedulerTask;

/**
 * @brief Base class for components, which are basically everything.
 */
class Component : public virtual Tracked
{
public:
  /**
   * @brief Constructs a new Component instance. When _DEBUG_COMPONENT is on, initialize the debugging information. 
   */
  Component() 
  {}

#ifdef _DEBUG_COMPONENT
  /**
   * @brief Component's destructor. When _DEBUG_COMPONENT is on, print an error message for each event handler owned by this
   *        component that is still connected to events (since we do not want the events to notify a "dead" event handler).
   *        Additionally, print an error message for each still active task owned by this component.
   */ 
  virtual ~Component();
#endif
  /**
   * @return The somewhat-coherent name of this component, extracted from the component's class name.
   */
  std::string getName() const;

  /**
   * @brief Adds a new zombie component whose deletion is deferred to later (typically the next frame).
   *        This is done to avoid having deleted components still referenced in the call stack.
   * @param component The component to zombify.
   */
  template <class ComponentType>
  static void addZombieComponent(const ComponentType* component)
  {
    // This static_assert is supposed to prevent people from zombifying processes manually; 
    // use process->zombifyOnTerminate() or ProcessContainer/ProcessContainerList instead.
    //static_assert<!boost::is_convertible<const ComponentType*,const Process*>::value || boost::is_same<ComponentType,Process>::value> use_process_zombify_on_terminate_or_process_container_to_zombify_processes;
    //unused(use_process_zombify_on_terminate_or_process_container_to_zombify_processes);
    assert(std::find(s_zombieComponents.begin(), s_zombieComponents.end(), component) == s_zombieComponents.end());

    if (component != NULL)
      s_zombieComponents.push_back(component);
  }

  static void deleteZombieComponents();

private:
  /**
   * @brief The list of zombie components, which are sporadically deleted.
   */
  static std::list<const Component*> s_zombieComponents;   // Optimization: chain the UnitData to each other?

#ifdef _DEBUG_COMPONENT
public:

  /**
   * @brief Outputs an error message for each pending subscription associated to this component.
   *        The caller assumes the component should have cancelled all its subscriptions at this point.
   */
  void _verifyPendingSubscriptions(const char* error) const;
  /**
   * @brief Outputs an error message for each pending task associated to this component.
   *        The caller assumes the component should have deactivated all its tasks at this point.
   */
  void _verifyPendingTasks(const char* error) const;

  void _onNewEventHandler(const EventHandlerBase* eventHandler) const
  {
    m_eventHandlers.insert(eventHandler);
  }

  void _onDestroyEventHandler(const EventHandlerBase* eventHandler) const
  {
    m_eventHandlers.erase(eventHandler);
  }

  void onNewTask(const SchedulerTask* task) const
  {
    m_tasks.insert(task);
  }

  void onDestroyTask(const SchedulerTask* task) const
  {
    m_tasks.erase(task);
  }

  /**
   * @brief The set of event handlers owned by this component.
   */
  mutable std::set<const EventHandlerBase*> m_eventHandlers;
  /**
   * @brief The set of tasks owned by this component.
   */
  mutable std::set<const SchedulerTask*> m_tasks;

#endif
};
