#pragma once
#include "Component.h"
#include "Logger.h"
#include "SafeList.h"
#include "SafeListAdaptor.h"

#include <boost/tuple/tuple.hpp>

template <class ProcessType, class Handler> class ProcessContainer; // for subscribeDelayed

template <class Arguments> class EventHandlerT;

/**
 * @brief Base class for events which other components may subscribe to or unsubscribe from.
 *        Events are usually not instantiated directly with the 'new' operator; they are most often 
 *        members of other classes.
 *        The virtual destructor is necessary only to make this class virtual.
 */
class EventBase : public virtual Component
{
public:
  virtual ~EventBase() {}
};

/**
 * @brief Base class for events, templated on the type of arguments sent with the event.
 *        Extensive debugging is conducted when the _DEBUG_COMPONENT flag is on.
 * @tparam Arguments Type of arguments, must be boost::tuple<...>
 */
template <class Arguments = boost::tuple<>>
class Event : public EventBase
{
public:
  /**
   * @brief The type of event handler that must be used by subscribers.
   */
  typedef EventHandlerT<Arguments> EventHandlerType;

protected:
  /**
   * @brief Information about a single subscriber.
   */
  struct SubscriberInfo
  {
    /**
     * @brief Constructs a new SubscriberInfo instance.
     * @param eventHandler The subscriber's event handler.
     * @param data The subscriber's data specified when registering the event handler.
     */
    SubscriberInfo(EventHandlerType* eventHandler, void* data)
      : m_eventHandler(eventHandler)
      , m_data(data)
#ifdef _DEBUG_COMPONENT
      , m_isDestroyed(false)
#endif
    {
    }
    /**
     * @brief The subscriber's event handler.
     */
    EventHandlerType* const m_eventHandler;
    /**
     * @brief The subscriber's data specified when registering the event handler. 
     */
    void* const m_data;
#ifdef _DEBUG_COMPONENT
    /**
     * @brief Whether this particular subscriber has been destroyed (deleted).
     *        Needed by the debugging features to know whether to ignore this one.
     */
    bool m_isDestroyed;
#endif

  private :
    //const member prevent the use of the assignment operator
    SubscriberInfo& operator= (const SubscriberInfo&);
  };

public:
  /**
   * @brief The type of ID given to the subscribers.
   */
  class SubscriberID
  {
  public:
    /**
     * @brief Constructs a new invalid SubscriberID instance.
     */
    SubscriberID()
      : m_isValid(false)
    {}
    /**
     * @brief Constructs a new valid SubscriberID instance.
     * @param id The subscriber's position in the safe list of subscribers.
     */
    SubscriberID(typename SafeList<SubscriberInfo>::iterator id)
      : m_id(id)
      , m_isValid(true)
    {}
    /**
     * @return Whether this subscription ID has been initialized (and hence whether it is present 
     *        in the subscribers list).
     */
    bool isValid() const
    {
      return m_isValid;
    }
  protected:
    friend class Event;
    /**
     * @brief Whether this subscription ID has been initialized (and hence whether it is present 
     *        in the subscribers list).
     */
    bool m_isValid;
    /**
     * @brief The position of the subscriber in the list of subscribers.
     */
    typename SafeList<SubscriberInfo>::iterator m_id;
  };

  /**
   * @brief Creates a new event.
   * @param owner The owner of this event, only used for debugging purposes.
   */
  Event(const Component& owner)
    : m_delayedSubscribersInsertionPoint(m_subscribers.end())
#ifdef _DEBUG_COMPONENT
    , m_owner(owner)
#endif
  {
    unused(owner);
  }

  /**
   * @brief The Event's destructor. Notifies the list of subscribers that this event has been deleted.
   */
  virtual ~Event();

  /**
   * @brief Subscribes an event handler to this event.
   * @param eventHandler The handler to notify when this event is raised.
   * @param data Additional data associated with the subscriber which will be sent back upon raising the event.
   * @return The ID associated with the new subscription.
   */
  SubscriberID subscribe(EventHandlerType* eventHandler, void* data)
  {
    typename SafeList<SubscriberInfo>::iterator pos = m_subscribers.add(m_delayedSubscribersInsertionPoint, SubscriberInfo(eventHandler, data));

  #ifdef _DEBUG_COMPONENT
    eventHandler->_onSubscribeTo(this, pos);
  #endif

    assert(m_delayedSubscribersInsertionPoint == m_subscribers.end() || !(*m_delayedSubscribersInsertionPoint).isDeleted());

    return SubscriberID(pos);
  }

  /**
   * @brief Unsubscribes an event handler from this event.
   * @param id The ID associated with the subscription (the one given upon subscription).
   */
  void unsubscribe(SubscriberID& id);

  /**
   * @brief Raises the event, which notifies the list of subscribers.
   * @param arguments The arguments associated with the event.
   */
  void internalRaise(Arguments& arguments);

#ifdef _DEBUG_COMPONENT
  template <class Arguments> friend class EventHandlerT;

  void _onEventHandlerDestroyed(typename SafeList<SubscriberInfo>::iterator pos)
  {
    static_cast<SubscriberInfo&>(*pos).m_isDestroyed = true;
  }

  void _verifyPendingSubscriptions(const char* error) const
  {
    struct LogErrorMessage
    {
      LogErrorMessage(const Event& ev, const char* error) : m_ev(&ev), m_error(error) { }
      typename SafeListBase::ConstIterationStep operator()(const SubscriberInfo& subscriber)
      {
        SPAR_LOG(LogTypes.GENERAL_ERROR, m_error, subscriber.m_eventHandler->m_debugName.c_str(), m_ev->m_debugName.c_str());

        return typename SafeListBase::ConstIterationStep();
      }
    private:
      const Event * m_ev;
      const char * m_error;
    };

    m_subscribers.for_each(LogErrorMessage(*this, error));
  }
#endif

protected:
  template <class ProcessType, class Handler> friend class ProcessContainer;
  /**
   * @brief Subscribes a delayed event handler to this event. Delayed event handlers are called after regular event handlers 
   *        (but please don't abuse them).
   * @param eventHandler The handler to notify when this event is raised.
   * @param data Additional data associated with the subscriber which will be sent back upon raising the event.
   * @return The ID associated with the new subscription.
   */
  SubscriberID subscribeDelayed(EventHandlerType* eventHandler, void* data);

  virtual void _output(std::ostream& out) const
  {
    out << getName();
#ifdef _DEBUG_COMPONENT
    out << " (owner: \"" << m_owner << "\")";
#endif
  }

  /**
   * @brief The list of subscribers to be notified when this event is raised.
   */
  SafeList<SubscriberInfo> m_subscribers;
  /**
   * @brief An iterator pointing to a position in the list of subscribers after which all the subscribers
   *        are considered "delayed" subscribers. This is only necessary to implement the list of "delayed" subscribers.
   */
  typename SafeList<SubscriberInfo>::iterator m_delayedSubscribersInsertionPoint;

#ifdef _DEBUG_COMPONENT
  const Component& m_owner;
#endif

//private:
//  Event(const Event& other);
};

/**
 * @brief Shorthand for events with no argument.
 */
class Event0 : public Event<boost::tuple<>>
{
public:
  Event0(const Component& owner)
    : Event(owner)
  {}
  void raise()
  {
    boost::tuples::tuple<> tuple = boost::make_tuple();
    internalRaise(tuple);
  }
};

/**
 * @brief Shorthand for events with one argument.
 * @tparam T1 Type of first argument.
 */
template <class T1>
class Event1 : public Event<boost::tuple<T1>>
{
public:
  Event1(const Component& owner)
    : Event(owner)
  {}
  void raise(T1 t1)
  {
    boost::tuples::tuple<T1> tuple = boost::make_tuple(t1);
    internalRaise(tuple);
  }
};

/**
 * @brief Shorthand for events with two arguments.
 * @tparam T1 Type of first argument.
 * @tparam T2 Type of second argument.
 */
template <class T1, class T2>
class Event2 : public Event<boost::tuple<T1,T2>>
{
public:
  Event2(const Component& owner)
    : Event(owner)
  {}
  void raise(T1 t1, T2 t2)
  {
    boost::tuples::tuple<T1, T2> tuple = boost::make_tuple(t1,t2);
    internalRaise(tuple);
  }
};

/**
 * @brief Shorthand for events with three arguments.
 * @tparam T1 Type of first argument.
 * @tparam T2 Type of second argument.
 * @tparam T3 Type of third argument.
 */
template <class T1, class T2, class T3>
class Event3 : public Event<boost::tuple<T1,T2,T3>>
{
public:
  Event3(const Component& owner)
    : Event(owner)
  {}
  void raise(T1 t1, T2 t2, T3 t3)
  {
    boost::tuples::tuple<T1, T2, T3> tuple = boost::make_tuple(t1, t2, t3);
    internalRaise(tuple);
  }
};

/**
 * @brief Shorthand for events with four arguments.
 * @tparam T1 Type of first argument.
 * @tparam T2 Type of second argument.
 * @tparam T3 Type of third argument.
 * @tparam T4 Type of fourth argument.
 */
template <class T1, class T2, class T3, class T4>
class Event4 : public Event<boost::tuple<T1,T2,T3,T4>>
{
public:
  Event4(const Component& owner)
    : Event(owner)
  {}
  void raise(T1 t1, T2 t2, T3 t3, T4 t4)
  {
    boost::tuples::tuple<T1, T2, T3, T4> tuple = boost::make_tuple(t1, t2, t3, t4);
    internalRaise(tuple);
  }
};

/**
 * @brief Shorthand for events with five arguments.
 * @tparam T1 Type of first argument.
 * @tparam T2 Type of second argument.
 * @tparam T3 Type of third argument.
 * @tparam T4 Type of fourth argument.
 * @tparam T5 Type of fifth argument.
 */
template <class T1, class T2, class T3, class T4, class T5>
class Event5 : public Event<boost::tuple<T1,T2,T3,T4,T5>>
{
public:
  Event5(const Component& owner)
    : Event(owner)
  {}
  void raise(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
  { 
    boost::tuples::tuple<T1, T2, T3, T4, T5> tuple = boost::make_tuple(t1, t2, t3, t4, t5);
    internalRaise(tuple);
  }
};

#define DECLARE_EVENT(Name) \
  class Name : public Event0 \
  { \
  public: \
    Name(const Component& owner) \
      : Event0(owner) {} \
    virtual ~Name() {} \
  };

#define DECLARE_EVENT1(Name, T1) \
  class Name : public Event1<T1> \
  { \
  public: \
    Name(const Component& owner) \
      : Event1(owner) {} \
    virtual ~Name() {} \
  };

#define DECLARE_EVENT2(Name, T1, T2) \
  class Name : public Event2<T1,T2> \
  { \
  public: \
    Name(const Component& owner) \
      : Event2(owner) {} \
    virtual ~Name() {} \
  };

#define DECLARE_EVENT3(Name, T1, T2, T3) \
  class Name : public Event3<T1,T2,T3> \
  { \
  public: \
    Name(const Component& owner) \
      : Event3(owner) {} \
    virtual ~Name() {} \
  };

#define DECLARE_EVENT4(Name, T1, T2, T3, T4) \
  class Name : public Event4<T1,T2,T3,T4> \
  { \
  public: \
    Name(const Component& owner) \
      : Event4(owner) {} \
    virtual ~Name() {} \
  };

#define DECLARE_EVENT5(Name, T1, T2, T3, T4, T5) \
  class Name : public Event5<T1,T2,T3,T4,T5> \
  { \
  public: \
    Name(const Component& owner) \
      : Event5(owner) {} \
    virtual ~Name() {} \
  };

/**
 * @brief Declares an event. The event is mutable since adding and removing subscribers is not
 *        considered a modification to the actual data (events having no data of their own).
 */
#define EVENT(Name) \
  mutable Name


///////////////////////////////////////////////////////////////////////////////

#include "EventHandler.h"

template <class Arguments>
::Event<Arguments>::~Event()
{
#ifdef _DEBUG_COMPONENT
  SafeListAdaptor<SubscriberInfo> adaptor(m_subscribers);
  for (typename SafeListAdaptor<SubscriberInfo>::iterator it = adaptor.unsafe_begin();
       it != adaptor.unsafe_end();
       ++it)
  {
    SubscriberInfo& subscriber = *it;
    if (!subscriber.m_isDestroyed) 
    {
      subscriber.m_eventHandler->_onEventDestroyed(this, SafeList<SubscriberInfo>::iterator(it.base()));
    }
  }
#endif
}

template <class Arguments>
void ::Event<Arguments>::unsubscribe(typename ::Event<Arguments>::SubscriberID& id)
{
  if (id.m_isValid)
  {
#ifdef _DEBUG_COMPONENT
    static_cast<SubscriberInfo&>(*id.m_id).m_eventHandler->_onUnsubscribeFrom(this, id.m_id);
#endif
    if (m_delayedSubscribersInsertionPoint == id.m_id)
    {
      m_subscribers.increment(m_delayedSubscribersInsertionPoint);
    }
    m_subscribers.erase(id.m_id);
    id.m_isValid = false;

    assert(m_delayedSubscribersInsertionPoint == m_subscribers.end() || !(*m_delayedSubscribersInsertionPoint).isDeleted());
  }
}

template <class Arguments>
void ::Event<Arguments>::internalRaise(Arguments& arguments)
{
  struct RaiseOnEvent
  {
    RaiseOnEvent(Arguments& args) : m_args(&args) { }
    typename SafeList<SubscriberInfo>::IterationStep operator()(SubscriberInfo& subscriber)
    {
      subscriber.m_eventHandler->onEvent(subscriber.m_data, *m_args);
      return typename SafeList<SubscriberInfo>::IterationStep();
    }
  private:
    Arguments * m_args;
  };

  m_subscribers.for_each(RaiseOnEvent(arguments));
}

template <class Arguments>
typename Event<Arguments>::SubscriberID Event<Arguments>::subscribeDelayed(
  typename Event<Arguments>::EventHandlerType* eventHandler, 
  void* data)
{
  typename SafeList<SubscriberInfo>::iterator pos = m_subscribers.add(SubscriberInfo(eventHandler, data));
  
#ifdef _DEBUG_COMPONENT
  eventHandler->_onSubscribeTo(this, pos);
#endif    
  if (m_delayedSubscribersInsertionPoint == m_subscribers.end())
  {
    m_delayedSubscribersInsertionPoint = pos;
  }
  
  assert(m_delayedSubscribersInsertionPoint == m_subscribers.end() || !(*m_delayedSubscribersInsertionPoint).isDeleted());
  
  return SubscriberID(pos);
}
