/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM 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; only version 2 of the License.      
 * 
 * NMM 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 NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */


#ifndef NMM_EVENTDISPATCHER_HPP
#define NMM_EVENTDISPATCHER_HPP NMM_EVENTDISPATCHER_HPP

#include "nmm/nmm_prefix.hpp"

#include "IState.hpp"
#include "IllegalStateException.hpp"
#include "UnknownEventException.hpp"

#include "nmm/utils/NamedObject.hpp"
#include "nmm/utils/thread/ThreadMutex.hpp"

#include "nmm/Result.hpp"

#include "nmm/nmm_hash_map.hpp"

namespace NMM {
  
  class EDObject;
  class IEventReceiver;
  class IEventSender;
  class Event;
  class CEvent;
  
  /** Handles Events.
   *
   * The class EventDispatcher allows to define which actions should be triggered on a special event.
   * This is done by registering event dispatcher objects (EDObject) for indiviudal events.
   */

class EventDispatcher : public IState, public NamedObject {
  public:
    /*! \name EventDispatcher's constructors
     * These functions creates or destroy an EventDispatcher
     */
    //@{
    /** Constructor.
     *
     * @param name NamedObject name
     * @param state intial state
     */
   EventDispatcher(const char* name = "Dispatcher", int state = -1);

    /** Copy constructor.
     *
     * @param dispatcher EventDispatcher to copy
     */
    EventDispatcher(const EventDispatcher& dispatcher);

    /** Destructor.
     */
    virtual ~EventDispatcher();
    //@}


    /*! \name Event registration methods
     * These methods allow to register, remove and access methods as well as EventListener which are handled by the EventDispatcher.
     * An EventListener receives an incoming event after it was successfully processed
     * by the registered method.
     */
    //@{

    /** Adds an EventDispatcher(ED) Object with specific key.
     * 
     * @param s key of the event
     * @param edo ED object
     */
    void registerEvent(const string& s, EDObject* edo);

    /** Removes Event e and returns the corresponding ED-Object if it exist.
     *
     * @param e key of event
     * @return Pointer to ED object
     */
    EDObject* removeEvent(const string& e);

    /** Adds TED-Object as EventListener. 
     *
     * Adds an TED-Object as EventListener. 
     * Incoming events with corresponding
     * event-keys are forwarded to the TED-Object
     *
     * @param s event key to register ted object for
     * @param edo event listener ted object
     */
    void registerEventListener(const string& s, EDObject* edo);

    /** Adds IEventReceiver as EventListener. 
     *
     * Adds an IEventReceiver as EventListener. All incomming events are forwarded to the
     * eventhandler. This method is obsolete and you should use an IEventReceiver.
     *
     * @param receiver event receiver to add
     */
    void registerEventListener(IEventReceiver* receiver);

    /** Adds IEventReceiver as EventListener. 
     *
     * Adds an IEventReceiver as EventListener. All incomming events are forwarded to the
     * eventhandler. This method is obsolete and you should use an IEventReceiver.
     *
     * @param event_key event key
     * @param event_receiver event receiver to register as event listener
     */
    void registerEventListener(const string& event_key, IEventReceiver* event_receiver);

    /** Removes an EventListener with specified key.
     *
     * @param event_key event key
     * @param  event_receiver event receiver to register as event listener
     */
    void removeEventListener(const string& event_key, IEventReceiver* event_receiver);

    /** Removes an EventListener.
     *
     * @param receiver event receiver to remove
     */
    void removeEventListener(IEventReceiver* receiver);

    /** Removes a certain TEDObject.
     *
     * @param ted_object TEDObject to be removed as event listener
     * @return list of events the TEDObject listened to.
     */
    list<string> removeEventListener(EDObject* ted_object);

    /** Returns all registered event keys.
     *
     * @return list of registered event keys
     */
    list<string> getEventList()              const;

    /** Returns the EDObject with specified event key.
     * 
     * Returns the EDObject with specified event key, NULL if no such object exists.
     *
     * @param event_key event_key to search for
     * @return  EDObject with specified event key
     */
    EDObject* getEDObject(const string& event_key) ;

    /** Returns an Event prototype with substring s in event key.
     *
     * Returns an Event prototype that has a substring s in event key, NULL if no such Event exists.
     *
     * @param s event key substring to search for
     * @return Event prototype with substring s in event key
     */
    Event* matchEventPrototype(const string& s);

    /** Returns the EDObject with specified event key.
     *
     *  Returns the EDObject with specified event key, NULL if no such object exists.
     */
    const EDObject* getEDObject(const string& event_key) const ;

    /** Check if event with specified key exists.
     *
     * @param e event key to search for
     * @return true, if Event with key e is registered
     */
    bool hasEvent(const string & e) const;


    /** Mark state transition as required for specified event key.
     *
     * @param event_key event key to add specify required state transition
     * @param from_state source state
     * @param to_state destination state
     */
    void addRequired(const string& event_key, int from_state, int to_state);

    /** Mark state as limited for specified event key.
     *
     * @param event_key event key to add state limitation to
     * @param state state event key is limited to
     */
    void addLimited(const string& event_key, int state);

    //@}

    /*! \name Processing and configuration methods
     * These methods can be used to process events and to configure and control the states
     */
    //@{
    /** Dispatches an event. 
     *
     * This method dispatches an event. If any failure occurs during the dispatching process
     * an Exception is created and stored inside the event.
     *
     * @param event event to dispatch
     * @param sender sender of event
     * @return FAILURE if an exception occurs or any other failure, otherwise SUCCESS
     */
    Result dispatch(Event& event, const IEventSender* sender = 0)
      throw(const IllegalStateException&);
    
    /** Dispatch internal CEvent.
     *
     * This method processes a composite event inside a node and dispatches all registered
     * methods of the node. 
     *
     * @param cevent CEvent to proess
     * @return FAILURE if one dispatched method returned FAILURE, otherwise SUCCESS
     */
    Result internalProcessCEvent(CEvent* cevent)
      throw(const IllegalStateException&);
    
    /** Dispatches external CEvent.
     *
     * This method processes a composite event when leaving a node upstream or downstream:
     * all external listeners are dispatched.
     *
     * @param cevent CEvent to process
     * @return currently always SUCCESS
     */
    Result externalProcessCEvent(CEvent* cevent);

    /** Resets the execution value of state s for all registered event keys.
     *
     * @param state state to reset
     */
    void reset(int state);

    /** Resets the execution value of state s for specified event key.     
     *
     * @param event_key event key to reset state
     * @param state state to reset
     **/
    void reset(const string& event_key, int state);

    /** Change to given state if possible.
     * 
     * If all EDObjectes with a mandatory bit for state s are executed successfully 
     * the Eventdispatcher changes the state to s. Otherwise an UnsuccessfulRequiredCallException
     * is thrown.
     *
     * @param state state to change to
     */
    void changeState(int state )
      throw (const UnsuccessfulRequiredCallException&);

    /** Returns the event dispatcher state.
     *
     * @return event dispatcher state
     */
    int getState()                           const {return state;}

  /** Check if all needed events were dispatched to change to given state.
     *
     * This methods returns true if the EventDispatcher processes all events with a mandatory bit
     * for the current state.
     *
     * @param new_state state to check mandatory events for
     */
    void checkMandatoryEvents(int new_state) const
      throw (const UnsuccessfulRequiredCallException&);

    /** Returns a list of unhandled events.
     *
     * IMORTANT NOTE! The caller of this method is responsible to delete the Events inside the list
     *
     * @param new_state state to check for unhandled mandatory events
     * @return list of unhandled mandatory events for given state
     */
    list<Event*> getUnhandledMandatoryEvents(int new_state) const;

    //@}

    /** Forwards event to all registered event listeners.
     *
     * @param event event to forward
     * @param sender of event
     */
    void forwardEvent(Event& event, const IEventSender* sender);
    
  /** Dispatch external event and mark as executed.
     *
     * @param event event to dispatch
     * @param sender sender of event
     */
   void externalDispatch(Event& event, const IEventSender* sender);
  
    /** Check if if event listener has been registered.
     *
     * @return true if event listener has been registered, false otherwise
     */
    inline bool hasListener() const { return !__ext_recv_forwarding_list.empty(); }

    /** Check if an IEventReceiver or TEDObject is listening to a specific event with givent event key.
     *
     * @param event_key event key specifying event
     * @return true f an IEventReceiver or TEDObject is listening to specified event, false otherwise. 
     */
    bool hasSpecificListener(const string& event_key) const;

    /** Check if an IEventReceiver or TEDObject is listening to any event.
     *
     * @return true if an IEventReceiver or TEDObject is listening to any event, false otherwise.
     */
    bool hasSpecificListener() const;

    /** Returns list of all events a listener is registered for.
     *
     * @return list of all events a listener is registered for
     */
    list<string> getSpecificListener() const;
  private:
    struct string_hash
    {
      size_t operator()(const string& __s) const { return HASH_MAP_STL::__stl_hash_string(__s.c_str()); }
    };

    typedef HASH_MAP_STL::hash_map<string, EDObject*, string_hash >::iterator event_it;
    typedef HASH_MAP_STL::hash_map<string, EDObject*, string_hash >::const_iterator cevent_it;

    typedef HASH_MAP_STL::hash_map<string, list<EDObject*>, string_hash >::iterator EDOMapIt;
    typedef HASH_MAP_STL::hash_map<string, list<EDObject*>, string_hash >::const_iterator CEDOMapIt;

    typedef HASH_MAP_STL::hash_map<string, list<IEventReceiver*>, string_hash >::iterator ReceiverMapIt;
    typedef HASH_MAP_STL::hash_map<string, list<IEventReceiver*>, string_hash >::const_iterator CReceiverMapIt;

    ThreadMutex                                             mutex; 
    ThreadMutex                                             external_mutex;
    ThreadMutex                                             __internal_process_mutex;

    /** stores the EDObjects that are dispatched primarily in the dispatch()-method */
    HASH_MAP_STL::hash_map<string, EDObject*, string_hash >               __int_event_map;
    /** stores the external EDObjects that are dispatched in the externalDispatch()-method 
     *	(called by dispatch() and externalProcessCEvent() 
     */
    HASH_MAP_STL::hash_map<string, list<EDObject*>, string_hash >         __ext_event_map; 
    /** stores the IEventReceiver objects that are dispatched in the externalDispatch()-method (after __ext_event_map)
     *	(called by dispatch() and externalProcessCEvent() 
     */
    HASH_MAP_STL::hash_map<string, list<IEventReceiver*>, string_hash >   __ext_event_recv_map;
    /** stores all IEventReceiver objects that are dispatched in the forwardEvent()-method 
     *	(called by dispatch() and externalProcessCEvent() 
     */
    list<IEventReceiver*>                                   __ext_recv_forwarding_list;
    int                                                     state; ///< current state 
  };

}

#include "nmm/nmm_suffix.hpp"

#endif
