/*
    *  Copyright (C) 2004 Alo Sarv <madcat_@users.sourceforge.net>
    *
    *  This program is free software; you can redistribute it and/or modify
    *  it under the terms of the GNU General Public License as published by
    *  the Free Software Foundation; either version 2 of the License, or
    *  (at your option) any later version.
    *
    *  This program is distributed in the hope that it will be useful,
    *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    *  GNU General Public License for more details.
    *
    *  You should have received a copy of the GNU General Public License
    *  along with this program; if not, write to the Free Software
    *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    */

   /** @file event.h Interface for Event subsystem */

   #ifndef __EVENT_H__
   #define __EVENT_H__

   /**
    * @page ehsv2 Event Handling Subsystem, version 2
    *
    * \section intro Introduction
    * Event Handling Subsystem version 2 (EHS) provides a generic API for passing
    * arbitary objects from a <i>source</i> to one or more <i>handlers</i> without
    * coupling neither the source and handlers, nor coupling either of those with
    * the EHS.
    *
    * \section ratio Rationale
    * The original Event Handling Subsystem (version 1) had a number of issues that
    * made the subsystem inconvenient to use, errorpone and hard to debug. Namely,
    * the syntax for postingevents and/or setting handlers was so inconvenient that
    * we had to resort to preprocessor macros to compensate for that. In addition,
    * the original engine was based on CBFunctor library, which dates back to 1994,
    * and thus breaks on some modern compilers. Thus, the system was rewritten.
    *
    * \section req Requirements
    * The requirements for EHSv2 are:
    * \list
    * - Decouple the event source from event tables. This is required to allow
    *   posting events from sources that were originally not designed for events,
    *   because they come from, for example, third-party libraries.
    * - Easy-to-learn and easy-to-use usage syntax.
    * - Base on a modern function objects library that is portable.
    *
    * \section usage Usage
    * There are two common operations needed to be done when it comes to event
    * tables usage - setting up event handlers, and posting events. In order to
    * provide maximum simplicity for those operations, there are a set of utility
    * methods (defined in <b>EHS</b> namespace) which simplify the syntax. The 
    * basic usage of EHS goes as follows:
    *
    * \code
    * // Initialize objects. Note: the first event (posted in MySource
    * // constructor) is not handled.
    * MyHandler *h = new MyHandler;
    * MySource *s = new MySource;
    * // Set up handler, and post an event
    * EHS::addHandler(s, h, &MyHandler::handlerfunc);
    * EHS::postEvent(s, MyEvent("Hello world!"));
    * \endcode 
    *
    * The utility methods are defined in <b>EHS</b> namespace. If your compiler
    * does not support argument-dependant lookup, the calls to those methods must
    * be qualified with explicit namespace name, e.g.
    *
    * \code
    * EHS::addHandler(s, h, &MyHandler::handlerfunc);
    * EHS::postEvent(s, MyEvent("Hello world!"));
    * \endcode
    */

   // hydranode includes
   #include <hn/eventbase.h>
   #include <hn/gettickcount.h>

   // event tables backend - Boost.Signal, Boost.Function and Boost.Bind libraries
   #include <boost/signal.hpp>
   #include <boost/signals/trackable.hpp>
   #include <boost/signals/connection.hpp>
   #include <boost/function.hpp>
   #include <boost/bind.hpp>

   // multi-thread safety - Boost.Thread (mutexes)
   #include <boost/thread.hpp>

   // std includes
   #include <map>

   /**
    * EventTable template class encapsulates pending events storage, event handlers
    * storage and event handlers calling when instructed to do so from main event
    * loop. For each event source and event type, there is a specific event table.
    * All event tables are Singletons.
    */
   template<
           typename Source,
           typename Event,
           typename Handler = boost::function<void (Source, Event)>
   >
   class EventTable : public EventTableBase {
   public:
           /**
            * Accessor for the single instance of this class. Note that the static
            * object is also protected via mutexes to be completely safe in MT
            * environment.
            */
           static EventTable& instance() {
                   static EventTable evtT;
                   static boost::recursive_mutex evtTMutex;;
                   boost::recursive_mutex::scoped_lock l(evtTMutex);
                   return evtT;
           }

           /**
            * Connect an event handler to a source. The handler shall be called
            * whenever the source object emits events.
            *
            * \note It is recommended to use classes derived from boost::signals::
            *       trackable for event handlers, and connect them via the 
            *       overloaded addHandler method, since that way automatic 
            *       disconnection is guaranteed when either the signal source or 
            *       the signal handler objects get destroyed.
            *
            * @param src     Source the handler is interested in
            * @param ha      Event handler function object
            * @return        The established connection between the event source
            *                and event handler. This can later be used to disconnect
            *                the handler from the source.
            */
           boost::signals::connection addHandler(Source src, Handler ha) {
                   boost::recursive_mutex::scoped_lock l(m_sigMapMutex);
                   Iter i = m_sigMap.find(src);
                   if (i == m_sigMap.end()) {
                           i = m_sigMap.insert(
                                   m_sigMap.end(),
                                   std::make_pair(src, new SigType())
                           );
                   }
                   return (*i).second->connect(ha);
           }

           /**
            * Overloaded version of the above method, performs function object 
            * binding internally. This is the recommended version of this function,
            * since while being also simpler to use, it also guarantees function 
            * disconnection in case the object is derived from boost::signals::
            * trackable.
            *
            * \note This limitation is inherent from Boost.Signals library - 
            *       namely, trackable objects only work when bound with 
            *       boost::bind, raw Boost.Function or Boost.Lambda functors do not
            *       implement the necesery interface for this to work.
            *
            * @param src   Event source to be interested in
            * @param obj   Event handler object
            * @param func  Event handler function
            * @return      The established connection between the event source and
            *              event handler, which can later be used to disconnect the
            *              handler from the source.
            */
           template<typename T>
           boost::signals::connection addHandler(
                   Source src, T *obj, void (T::*func)(Source, Event)
           ) {
                   return addHandler(src, boost::bind(func, obj, _1, _2));
           }

           /**
            * Disconnect an event handler which has previously been connected with
            * addHandler() method.
            *
            * @param src     Event source
            * @param c       Connection to be disconnected
            */
           void delHandler(Source src, const boost::signals::connection &c) {
                   boost::recursive_mutex::scoped_lock l(m_sigMapMutex);
                   Iter i = m_sigMap.find(src);
                   if (i != m_sigMap.end()) {
                           (*i).second->disconnect(c);
                   }
                   if ((*i).second->empty()) {
                           delete (*i).second;
                           m_sigMap.erase(i);
                   }
           }

           /**
            * Erase all handlers refering to a specific source. This is useful to
            * be called from either source object's destructor, or by whoever is
            * destroying the source, to clean up the event table things.
            *
            * @param src    Source object
            */
           void delHandler(Source src) {
                   boost::recursive_mutex::scoped_lock l(m_sigMapMutex);
                   Iter i = m_sigMap.find(src);
                   if (i != m_sigMap.end()) {
                           delete (*i).second;
                           m_sigMap.erase(i);
                   }
           }

           /**
            * Post an event to the event queue, which shall be passed to all 
            * handlers during next event loop.
            *
            * @param src     Event source
            * @param evt     The event itself
            */
           void postEvent(Source src, Event evt) {
                   boost::recursive_mutex::scoped_lock l(m_pendingMutex);
                   m_pending.push_back(InternalEvent(src, evt));
           }

           /**
            * Post a delayed event, which shall be emitted when the delay is over.
            * Note that the actual delay until to the event emitting may vary, 
            * depending on application load, up to 100ms.
            *
            * @param src     Event source
            * @param evt     The event itself
            * @param delay   Delay, in milliseconds, until event will be passed to 
            *                handlers
            */
           void postDelayedEvent(Source src, Event evt, uint32_t delay) {
                   boost::recursive_mutex::scoped_lock l(m_delayedMutex);
                   m_delayed.insert(
                           std::make_pair(
                                   Utils::getTick() + delay,
                                   InternalEvent(src, evt)
                           )
                   );
           }

           /**
            * Call all handlers for all pending events. This method is called from
            * main event loop.
            */
           virtual void handleEvents() {
                   checkForDelayed();

                   boost::recursive_mutex::scoped_lock l1(m_pendingMutex);
                   boost::recursive_mutex::scoped_lock l2(m_sigMapMutex);

                   for (PIter i = m_pending.begin(); i != m_pending.end(); ++i) {
                           Iter j = m_sigMap.find((*i).getSource());
                           if (j != m_sigMap.end()) {
                                   (*(*j).second)(
                                           (*i).getSource(), ((*i).getEvent())
                                   );
                           }
                   }
                   m_pending.clear();
           }
   private:
           /**
            * Singleton pattern requirements - private constructor/destructor, and
            * non-copyable requirement.
            */
           //@{
           EventTable() {}
           EventTable(const EventTable&);
           EventTable& operator=(const EventTable &);
           ~EventTable() {}
           //@}

           class InternalEvent;

           //! Type of signal used in this event table
           typedef typename boost::signal<void (Source, Event)> SigType;

           //! Helper typedef, for iterating on sigMap
           typedef typename std::map<Source, SigType*>::iterator Iter;

           //! Helper typedef, for iterating on pending events vector
           typedef typename std::vector<InternalEvent>::iterator PIter;

           /**
            * Stores all signal objects for all objects of type Source. This map
            * is filled by addHandler() method, which adds the signal here if it's
            * not here already. Also, addHandler() connects functions to the 
            * signals in this map. When a signal in this map no longer has any 
            * handlers, it should be removed (done in delHandler() method).
            *
            * \note We'r using heap-allocated signal objects here, since Signal
            *       is not copyable.
            */
           std::map<Source, SigType*> m_sigMap;

           /**
            * Pending events queue, filled by postEvent() method and cleared in
            * handleEvents().
            */
           std::vector<InternalEvent> m_pending;

           /**
            * Delayed events, which will be posted when the timeout is over.
            * The key to this map is the actual tick when the event should be
            * emitted.
            */
           std::multimap<uint32_t, InternalEvent> m_delayed;

           /**
            * Recursive mutexes which protect the above three containers in multi-
            * threaded environment. They are recursive since we are most likely
            * to be called from an event handler from same thread, in which case we
            * want to be able to access the maps.
            */
           //@{
           boost::recursive_mutex m_sigMapMutex;
           boost::recursive_mutex m_pendingMutex;
           boost::recursive_mutex m_delayedMutex;
           //@}

           /**
            * Wrapper class for temporary storing pending events and similar.
            */
           class InternalEvent {
           public:
                   //! Constructor
                   InternalEvent(Source src, Event evt)
                   : m_src(src), m_evt(evt){}

                   //! @name Accessors
                   //@{
                   Source getSource() const { return m_src; }
                   Event  getEvent()  const { return m_evt; }
                   //@}
           private:
                   Source m_src;     //!< Event source object
                   Event     m_evt;     //!< The event itself
           };

           /**
            * Transfer all delayed events for which the delay is over to pending
            * list.
            */
           void checkForDelayed() {
                   boost::recursive_mutex::scoped_lock l1(m_delayedMutex);
                   boost::recursive_mutex::scoped_lock l2(m_pendingMutex);
                   uint64_t tick = Utils::getTick();

                   while (m_delayed.size() && (*m_delayed.begin()).first < tick) {
                           m_pending.push_back((*m_delayed.begin()).second);
                           m_delayed.erase(m_delayed.begin());
                   }
           }
   };

   //! Event Handler Subsystem utility functions
   namespace EHS {

   /**
    * @name Event Handling Subsystem Utility Functions
    * Globally defined methods for easier access for automatic template
    * parameter deduction. Thus, the following two source lines are equivalent:
    *
    * \code
    * EventTable<MyClass, MyEvent>::instance().postEvent(this, evt);
    * postEvent(this, evt);
    * \endcode
    */
   //@{
   template<typename Source, typename Event>
   void postEvent(Source src, Event evt) {
           EventTable<Source, Event>::instance().postEvent(src, evt);
   }
   template<typename Source, typename Event>
   boost::signals::connection addHandler(
           Source src, boost::function<void (Source, Event)> ha
   ) {
           return EventTable<Source, Event>::instance().addHandler(src, ha);
   }
   template<typename Source, typename Event, typename T>
   boost::signals::connection addHandler(
           Source src, T *obj, void (T::*ha)(Source, Event)
   ) {
           return EventTable<Source, Event>::instance().addHandler(src, obj, ha);
   }
   template<typename Source, typename Event>
   void delHandler(Source src, boost::signals::connection conn) {
           EventTable<Source, Event>::instance().delHandler(src, conn);
   }
   template<typename Source, typename Event>
   void delHandler(Source src) {
           EventTable<Source, Event>::instance().delHandler(src);
   }
   //@}

   }

   #endif