/* 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_NET_REACTOR_LINUX_HPP
#define NMM_NET_REACTOR_LINUX_HPP NMM_NET_REACTOR_LINUX_HPP

#include "nmm/nmm_prefix.hpp"

#include "SocketHandle.hpp"

#include "nmm/utils/thread/WorkerThread.hpp"
#include "nmm/utils/thread/TTask.hpp"
#include "nmm/utils/Singleton.hpp"
#include "nmm/utils/NamedObject.hpp"
#include "NetHandler.hpp"

#include <sys/poll.h>
#include <vector>
#include <map>

#include "visibility.hpp"

namespace NMM {

  /** \class NetReactor
   * The NetReactor is used to wait for incoming network events and to notify registered NetHandler
   * for a specific network action like a closed connection or incomming data. The events are defined
   * in an enum to allow a unified use on different systems. The NetReactor uses the poll-method to
   * wait for changes on registered sockets and uses therfore a own thread.
   *
   * This class is designed as a singleton.
   */
  class DLLEXPORT NetReactor : public NamedObject {
	DECLARE_SINGLETON(NetReactor, CreateUsingNew, DefaultLifetime, MultiThreaded);
    /** The CreateUsingNew is friend to call the constructor */
    friend class CreateUsingNew<NetReactor>;
  public:
    /** Same values as poll events. includes also the values defined in asm/poll.h
     */
    enum PollEvent {NR_POLLIN  = POLLIN,   NR_POLLPRI = POLLPRI, NR_POLLOUT  = POLLOUT,
		    NR_POLLERR  = POLLERR, NR_POLLHUP = POLLHUP, NR_POLLNVAL = POLLNVAL,
		    NR_POLLRDNORM = 0x0040, NR_POLLRDBAND = 0x0080, NR_POLLWRNORM = 0x0100,
		    NR_POLLWRBAND = 0x0200, NR_POLLMSG    = 0x0400};
    /** @name Registration methods
     */
    //@{
    /** This method adds a new socket and a corresponding NetHandler wich is informed about incoming events.
     * Every NetHandler is informed about errors on the given socket. This method is threadsafe. 
     * \param socket The new socket.
     * \param handler The NetHandler which is informed about incoming events.
     * \param events The interesting events for the sockets. Several events can combined with or-operator(operator|()).
     */
    void registerSocket(SocketHandle socket, NetHandler* handler, short events = NR_POLLIN|NR_POLLERR);

    /** This method adds a new socket and a corresponding NetHandler wich is informed about incoming events.
     * Every NetHandler is informed about errors on the given socket. This method is not thread safe and can be 
     * called only from the current leader thread of the NetReactor.
     * \param socket The new socket.
     * \param handler The NetHandler which is informed about incoming events.
     * \param events The interesting events for the sockets. Several events can combined with or-operator(operator|()).
     */
    void internalRegisterSocket(SocketHandle socket, NetHandler* handler, short events = NR_POLLIN|NR_POLLERR);

    /** This method changes the events a NetHandler is listening too.
     * \param socket The socket to change events
     * \param events The new events for the socket. Several events can combined with or-operator(operator|()).
     */
    void changeEvents(SocketHandle socket, short events);

    /** This method removes a registered socket and its corresponding NetHandler. The NetHandler is not destroyed.
     * \param socket The socket that is removed.
     */
    void unregisterSocket(SocketHandle socket);
 
    /** This method removes a registered socket and its corresponding NetHandler. The NetHandler is not destroyed.
     * This method is not thread safe and can be 
     * called only from the current leader thread of the NetReactor.
     * \param socket The socket that is removed.
     */
    void internalUnregisterSocket(SocketHandle socket);

    /** This method sets the timeout used by the poll()-method in milliseconds. The default value is 100 ms.
     * \param timeout The new timeout for the poll()-method
     */
    void setTimeout(int timeout);
    //@}

    /** Requests a new leader thread. The leader thread is looking for new events on registered sockets. 
     */
    void requestLeaderThread();

  protected:
    /** The run method is used to call poll on registred sockets
     */
    virtual void run();

  private:
    /** @name Constructor & Destructor
     */
    //@{
    /** Default Constructor.
     */
    NetReactor() ;

    /** Disabled Copy Constructor
     */
    NetReactor(const NetReactor& );

    /** Destructor
     */
    ~NetReactor();
    //@}

    /** This methos checks the registered sockets. If this mehtod returns true, the thread can not be reused.
     * by the NetReactor, because another thread was used as leader.
     *
     * \return true if the thread can not be reused by the NetReactor, because another thread was used as leader.
     */
    bool checkSockets();

    /** Returns an error as string for an error code.
     * \param error The errno-value
     * \return an error as string for an error code. 
     */
    const string& getError(int error);

    /** Test, if a specific socket is registerd
     * @param socket that should be checked if it is registerd or not
     * @return true if socket is registerd
     */
    bool isSocketRegisterd(SocketHandle socket);
  private:

    /** Locks the internal mutex
     */
    void internalLock();

    /** Unlocks the internal mutex
     */
    void internalUnlock();

    /** The InternalNetHandler is used to interrupt the blocking poll method of the NetReactor
     */
    class InternalNetHandler : public NetHandler {
    public:
      /** Constructor
       * @param reactor Reference to the NetReactor
       */
      InternalNetHandler(NetReactor& reactor);

     /** This method is called by the NetReactor and must be implement by all sub classes
      * \param socket The socket on which an event occurs.
      * \param event The events which have been registered.
      * \param revent The events which have been occured on the socket
      * \return VOID if nothing should be done with the socket, RECHECK if the socket should be checked for new events, 
      *         or REMOVE if the Socket should be no longer handled
      */
      virtual NetResult receiveEvent(SocketHandle socket, short event, short revents);

     /** This method returns true, if the NetHandler requires an own thread.
      * \return true it the NetHandler requires an own thread, otherwise false
      */
      virtual bool requireThread() const { return false; }
    private:
      int         __receive_signal;
      char        __internal_char;
      NetReactor& __reactor;
    };

    friend class InternalNetHandler;

  private:
    vector<pollfd>            __socket_array;         ///< stores all registered sockets in a pollfd-struct
    map<SocketHandle, NetHandler*> __handler;         ///< stores all registered sockets with the corresponding NetHandler
    ThreadMutex               __handler_mutex;        ///< Mutex for the internal handler
    ThreadMutex               __handler_cond_mutex;   ///< Mutex for the internal handler
    int                       __timeout;              ///< Timeout used for the poll() method
    vector<int>               __pipe_socket;          ///< sockets for the pipe
    char                      __internal_char;        ///< Dummy byte which is written in the pipe to interrupt poll
    InternalNetHandler        __internal_handler;     ///< Internal handler to interrupt the poll method
    TTask<NetReactor>         __reactor_task;
    WorkerThread*             __leader_thread;

    static bool               __is_running;           ///<  running flag for the thread
  
    static const string EBADF_error;                  ///< static error string for errno EBADF
    static const string ENOMEM_error;                 ///< static error string for errno ENOMEM
    static const string EFAULT_error;                 ///< static error string for errno EFAULT
    static const string EINTR_error;                  ///< static error string for errno EINTR
    static const string unknown_error;                ///< static error string for an unknown error
  };

  /* Typdef to access the NetReactor */
  typedef NetReactor::Singleton SNetReactor;


}

#include "nmm/nmm_suffix.hpp"

#endif
