/**
 *  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
 */

#ifndef __SSOCKET_H__
#define __SSOCKET_H__

#include <hn/sockets.h>            // xplatform socket api
#include <hn/gettickcount.h>       // getTick()
#include <boost/smart_ptr.hpp>     // smart pointers
#include <boost/function.hpp>      // function objects
#include <boost/bind.hpp>          // function object binder

/**
 * SchedBase implements the third level of hydranode networking scheduler.
 * This class performs the actual bandwidth and connections dividing between
 * requests in it's main scheduler loop. It is derived from EventTableBase,
 * and as such will be run during each event loop, performing bandwidth requests
 * handling.
 */
class SchedBase : public EventTableBase {
public:
	//! Singleton class - accessor to the only instance (created on demand)
	static SchedBase& instance() {
		static SchedBase s;
		return s;
	}

	//! @name Accessors for various limits and other internal variables
	//@{
	void     setUpLimit(uint32_t amount)   { m_upLimit = amount;   }
	void     setDownLimit(uint32_t amount) { m_downLimit = amount; }
	uint32_t getUpLimit()                  { return m_upLimit;     }
	uint32_t getDownLimit()                { return m_downLimit;   }
	uint64_t getTotalUpstream()            { return m_totalUp;     }
	uint64_t getTotalDownstream()          { return m_totalDown;   }
	//@}

	// calculate the relative PS for the given module
	// this is based on how high % of the total upstream and how much
	// of the total downstream has the module used. The result is that
	// modules who use relativly more downstream than upstream get higher
	// PS, thus also more bandwidth
	template<typename Module>
	float getScore() {
		float up_perc = Module::getUploaded()*100.0/m_totalUp;
		float dn_perc = Module::getDownloaded()*100.0/m_totalDown;
		return Module::getPriority() + up_perc - dn_perc;
	}

private:
	//! @name Singleton
	//@{
	SchedBase();
	~SchedBase() {}
	SchedBase(const SchedBase&);
	SchedBase& operator=(const SchedBase&);
	//@}

	//! Request base, only contains score of the request
	class ReqBase {
	public:
		ReqBase(float score) : m_score(score) {}
		virtual ~ReqBase() {}
		virtual void notify() = 0;
		float getScore() const { return m_score; }
	private:
		float m_score;
	};

	//! Request of type upload
	class UploadReqBase : public ReqBase {
	public:
		UploadReqBase(float score) : ReqBase(score) {}
		virtual uint32_t doSend(uint32_t amount) = 0;
		virtual uint32_t getPending() const = 0;
	};
	//! Request of type download
	class DownloadReqBase : public ReqBase {
	public:
		DownloadReqBase(float score) : ReqBase(score) {}
		virtual uint32_t doRecv(uint32_t amount) = 0;
	};
	//! Request of type connection
	class ConnReqBase : public ReqBase {
	public:
		ConnReqBase(float score) : ReqBase(score) {}
		virtual bool doConn() = 0;
	};

	//! @name Request sets
	//@{
	std::set<UploadReqBase*  > m_uploadReqs;
	std::set<DownloadReqBase*> m_downloadReqs;
	std::set<ConnReqBase*    > m_connReqs;
	//@}

	//! @name Iterator typedefs for the above sets to make life easier
	//@{
	typedef std::set<UploadReqBase*  >::iterator UIter;
	typedef std::set<DownloadReqBase*>::iterator DIter;
	typedef std::set<ConnReqBase*    >::iterator CIter;
	//@}

	//! @name Main networking loop and helper functions
	//@{
	void handleEvents();
	void handleDownloads();
	void handleUploads();
	void handleConnections();
	//@}

	//! @name Get the amount of free bandwidth/connections at this moment
	//@{
	uint32_t getFreeDown();
	uint32_t getFreeUp();
	bool     getConnection();
	//@}

	//! Keeps current tick value - to reduce getTick() calls somewhat
	uint32_t m_curTick;

	//! @name Various limits and counts
	//@{
	uint32_t m_upLimit;      //!< upstream limit
	uint32_t m_downLimit;    //!< downstream limit
	uint32_t m_connLimit;    //!< open connections limit
	uint32_t m_connCnt;      //!< open connections count
	uint64_t m_totalUp;      //!< overall total uploaded
	uint64_t m_totalDown;    //!< overall total downloaded
	//@}

	/**
	 * Last sent data amounts, along with timestamps
	 * This keeps last N milliseconds of transfers, effectivly allowing
	 * calculating current upstream transfer rate.
	 */
	std::deque<std::pair<uint32_t, uint32_t> > m_lastSent;

	/**
	 * Last received data amounts, along with timestamps
	 * This keeps last N milliseconds of download transfers, effectivly
	 * allowing calculating current downstream data rate.
	 */
	std::deque<std::pair<uint32_t, uint32_t> > m_lastRecv;

	//! Iterator typedef for the above deques for easier usage
	typedef std::deque<std::pair<uint32_t, uint32_t> >::iterator QIter;
public:
	//! @name Internal stuff - add new requests
	//@{
	void addUploadReq(UploadReqBase *r) {
		m_uploadReqs.insert(r);
	}
	void addDloadReq(DownloadReqBase *r) {
		m_downloadReqs.insert(r);
	}
	void addConnReq(ConnReqBase *r) {
		m_connReqs.insert(r);
	}
	//@}

	//! @name Modify open connection count
	//@{
	void addConn() { ++m_connCnt; }
	void delConn() { --m_connCnt; }
	//@}
};

/**
 * Policy class used by Scheduler for events handling.
 *
 * @param Source       Source class type generating the events
 */
template<typename Source>
class ClientEventHandler {
public:
	/**
	 * Socket event handler. Note that the actual events handling is
	 * directed to smaller helper functions for simplicity and clarity.
	 *
	 * @param Scheduler  The scheduler governing this socket
	 *
	 * @param src        Source of the event
	 * @param evt        The event itself
	 *
	 * \note This function is called from main event loop, and should never
	 *       be called directly.
	 */
	template<typename Scheduler>
	static void onEvent(Source *src, SocketEvent evt) {
		switch (evt) {
			case SOCK_READ:
				handleRead<Scheduler>(src);
				break;
			case SOCK_WRITE:
				handleWrite<Scheduler>(src);
				break;
			case SOCK_LOST:
				handleLost<Scheduler>(src);
				break;
			case SOCK_ERR:
				break;
			case SOCK_CONNECTED:
				handleConnected<Scheduler>(src);
				break;
			default:
				break;
		}
	}
	//! Handles "connection established" type of events
	template<typename Scheduler>
	static void handleConnected(Source *src) {
		typedef typename Scheduler::SSocketWrapper SWrapper;
		typedef typename Scheduler::UploadReq UploadReq;
		typedef typename Scheduler::BIter BIter;
		BIter i = Scheduler::s_bufferedOut.find(src);
		SWrapper ss(*Scheduler::s_sockets.find(src));
		if (i != Scheduler::s_bufferedOut.end()) {
			UploadReq *r = new UploadReq(ss, (*i).second);
			SchedBase::instance().addUploadReq(r);
		}
		ss.notify(SOCK_CONNECTED);
	}
	//! Handles "connection lost" type of events
	template<typename Scheduler>
	static void handleLost(Source *src) {
		typedef typename Scheduler::SSocketWrapper SWrapper;
		SchedBase::instance().delConn();
		SWrapper ss(*Scheduler::s_sockets.find(src));
		ss.notify(SOCK_LOST);
	}
	//! Handles "socket became writable" type of events
	template<typename Scheduler>
	static void handleWrite(Source *src) {
		typedef typename Scheduler::SSocketWrapper SWrapper;
		typedef typename Scheduler::UploadReq UploadReq;
		typedef typename Scheduler::BIter BIter;
		BIter i = Scheduler::s_bufferedOut.find(src);
		SWrapper ss(*Scheduler::s_sockets.find(src));
		if (i != Scheduler::s_bufferedOut.end()) {
			UploadReq *r = new UploadReq(ss, (*i).second);
			SchedBase::instance().addUploadReq(r);
		} else {
			ss.notify(SOCK_WRITE);
		}
	}
	//! Handles "socket became readable" type of events
	template<typename Scheduler>
	static void handleRead(Source *src) {
		typedef typename Scheduler::SSocketWrapper SWrapper;
		typedef typename Scheduler::DownloadReq DownloadReq;
		SWrapper ss(*Scheduler::s_sockets.find(src));
		DownloadReq *r = new DownloadReq(ss);
		SchedBase::instance().addDloadReq(r);
	}
};

/**
 * Policy class for events emitted from server type sockets. This class is
 * used by Scheduler for events handling.
 *
 * @param Source       Event source object type
 */
template<typename Source>
class ServerEventHandler {
public:
	/**
	 * Event handler function for events emitted from servers
	 *
	 * @param Scheduler      Scheduler governing this socket
	 * @param src            Source of the event
	 * @param evt            The actual event itself
	 *
	 * \note This function is called from main event loop and should never
	 *       be called directly by user code.
	 */
	template<typename Scheduler>
	static void onEvent(Source *src, SocketEvent evt) {
		typedef typename Scheduler::SSocketWrapper SWrapper;
		typedef typename Scheduler::AcceptReq AccReq;
		switch (evt) {
			case SOCK_ACCEPT: {
				SWrapper ss(*Scheduler::s_sockets.find(src));
				AccReq *ar = new AccReq(ss);
				SchedBase::instance().addConnReq(ar);
				break;
			}
			case SOCK_LOST:
				// Server losing connection ?
				break;
			case SOCK_ERR:
				// Server became erronous ?
				break;
			default:
				break;
		}
	}
};

/**
 * Traits class - selects the event handler policy class based on the event
 * source type. For example, SocketClient and UDPSocket use same event handler
 * policy, while SocketServer needs a specialized version of event handler
 * due to the radically different nature of it.
 *
 * The primary template of this class is not implemented. Instead, one of the
 * specialized versions below are chosen. If no possible specialization could
 * be chosen, this generates an undefined reference during linking, which is
 * expected.
 */
template<typename Source> class GetEventHandler;
template<> class GetEventHandler<SocketClient> {
public:
	typedef ClientEventHandler<SocketClient> Handler;
};
template<> class GetEventHandler<UDPSocket> {
public:
	typedef ClientEventHandler<UDPSocket> Handler;
};
template<> class GetEventHandler<SocketServer> {
public:
	typedef ServerEventHandler<SocketServer> Handler;
};

/**
 * Scheduler class, implementing second level of HydraNode Networking Scheduling
 * API, abstracts away modules part of the sockets by generating a priority
 * score (PS) for each of the pending requests. All requests are received from
 * the frontend, wrapped into generic containers, and buffered internally for
 * later processing. No direct action shall be taken in the functions directly
 * or indirectly called from frontend.
 *
 * @param Impl             Implemenetation class type
 * @param ImplPtr          Pointer to implementation class
 * @param EventType        Type of events emitted from Impl
 * @param HandlerFunc      Function prototype for events emitted from Impl
 * @param EventHandler     Event handler object
 */
template<
	typename Impl,
	typename ImplPtr      = Impl*,
	typename EventType    = typename Impl::EventType,
	typename HandlerFunc  = boost::function<void(ImplPtr, EventType)>,
	typename EventHandler = typename GetEventHandler<Impl>::Handler
>
class Scheduler {
private:
	//! Function object that can be used to retrieve a module's score
	typedef typename boost::function<float ()> ScoreFunc;
	//! Accept type (saves some typing)
	typedef typename Impl::AcceptType AcceptType;
public:
	/**
	 * Schedule outgoing data
	 *
	 * @param ptr   Implementation pointer where to send this data
	 * @param buf   Data buffer to be sent out
	 *
	 * If there is existing outgoing data scheduled for this socket, the
	 * newly passed data must be appeneded to existing data buffer.
	 * Otherwise, a new data buffer is allocated for this socket, and the
	 * data copied into there. In the latter case, a new upload request
	 * is generated and submitted to SchedBase for processing.
	 *
	 * \note The pointed socket is not required to be in connected state
	 *       at the time of this call, since no actual I/O is performed
	 *       here. In that case, the data will be sent as soon as the
	 *       connection has been established.
	 *
	 * \pre ptr is previously added to scheduler using addSocket member
	 */
	template<typename Module>
	static void write(ImplPtr ptr, const std::string &buf) {
		BIter i = s_bufferedOut.find(ptr);
		if (i == s_bufferedOut.end()) {
			std::string *s = new std::string(buf);
			s_bufferedOut.insert(std::make_pair(ptr, s));
			if (ptr->isConnected()) {
				SIter iter = s_sockets.find(ptr);
				CHECK_THROW(iter != s_sockets.end());
				UploadReq *req = new UploadReq(*iter, s);
				SchedBase::instance().addUploadReq(req);
			}
		} else {
			(*i).second->append(buf);
		}
	}

	/**
	 * Read data from socket
	 *
	 * @param ptr       Socket to read data from
	 * @param buf       Buffer to append the retrieved data to
	 *
	 * This function is only allowed to read data from previously allocated
	 * internal buffer. No additional network I/O may be performed. The
	 * internal buffer is located using the passed pointer, and the found
	 * data (if any) appeneded to the designated buffer. The internal buffer
	 * must then be deallocated.
	 *
	 * \note It is not required that the designated socket is in connected
	 *       state at the time of this call, since no actual networking I/O
	 *       is performed.
	 */
	template<typename Module>
	static void read(ImplPtr ptr, std::string *buf) {
		BIter i = s_bufferedIn.find(ptr);
		if (i != s_bufferedIn.end()) {
			buf->append(*(*i).second);
			delete (*i).second;
			s_bufferedIn.erase(i);
		}
	}

	/**
	 * Accept a pending connection
	 *
	 * @param ptr    Socket to accept the connection from
	 * @return       A new socket, which is in connected state
	 *
	 * The function searches the internal buffer for the socket designated
	 * to by ptr parameter, and returns the dynamically allocated connection
	 * to caller. If there are no pending accepted connections, exception
	 * is thrown.
	 */
	template<typename Module>
	static AcceptType* accept(ImplPtr ptr) {
		AIter i = s_accepted.find(ptr);
		if (i == s_accepted.end()) {
			throw std::runtime_error("No pending connections.");
		}
		AcceptType *a = (*i).second;
		s_accepted.erase(i);
		return a;
	}

	// Disconnect the socket and reduce open connections count by one
	template<typename Module>
	static void disconnect(ImplPtr ptr) {
		ptr->disconnect();
		SchedBase::instance().delConn();
	}

	// Add a socket into scheduler
	template<typename Module>
	static void addSocket(ImplPtr s, HandlerFunc h) {
		ScoreFunc sf(
			boost::bind(
				&SchedBase::getScore<Module>,
				&SchedBase::instance()
			)
		);
		s_sockets.insert(SSocketWrapper(s, h, sf));
		s->getEventTable().addHandler(
			s, &EventHandler::template onEvent<Scheduler>
		);
		fprintf(stderr, "Adding socket to scheduler.\n");
	}
	// Remove a socket from scheduler
	static void delSocket(ImplPtr s) {
		s_sockets.erase(SSocketWrapper(s));
		s->getEventTable().delHandler(
			s, &EventHandler::template onEvent<Scheduler>
		);
	}

// We need the internal classes public so the policy classes (namely,
// EventHandler) can access them. If only we could make the EventHandler a
// friend of ours, we could move this to private sector.
public:
	/**
	 * Wrapper object for scheduled socket, contains all the useful
	 * information we need, e.g. score, frontend event handler, and
	 * underlying socket object. Used by requests to keep track of which
	 * socket the request belongs to. This object can and should be held
	 * on stack.
	 */
	class SSocketWrapper {
	public:
		/**
		 * Construct new socket wrapper
		 *
		 * @param s     Implementation-defined pointer to underlying
		 *              socket
		 * @param h     Frontend event handler for notifications
		 * @param f     Score function object to retrieve this socket's
		 *              priority score
		 */
		SSocketWrapper(ImplPtr s, HandlerFunc h = 0, ScoreFunc f = 0)
		: m_socket(s), m_handler(h), m_scoreFunc(f) {}

		//! @name Accessors
		//@{
		ImplPtr     getSocket()  const { return m_socket;    }
		ScoreFunc   getScore()   const { return m_scoreFunc; }
		HandlerFunc getHandler() const { return m_handler;   }
		//@}

		/**
		 * Pass event to frontend
		 *
		 * @param evt      Event to be sent
		 *
		 * \note If no handler is set, this function does nothing
		 */
		void notify(EventType evt) {
			if (m_handler) {
				m_handler(m_socket, evt);
			}
		}
		friend bool operator<(
			const SSocketWrapper &x, const SSocketWrapper &y
		) {
			return x.m_socket < y.m_socket;
		}
	private:
		SSocketWrapper();         //!< Forbidden
		ImplPtr     m_socket;     //!< Underlying socket
		HandlerFunc m_handler;    //!< Frontend event handler
		ScoreFunc   m_scoreFunc;  //!< Function to retrieve the score
	};

	/**
	 * Upload request is a request that indicates we wish to send out
	 * data to a socket. The request is to be passed to SchedBase class
	 * which in turn calls us back through virtual function doSend() when
	 * we are allowed to perform the actual sending.
	 *
	 * \note The data buffer contained within this class is the same buffer
	 *       also stored in s_bufferedOut map. As such, it is this class's
	 *       responsibility to clean out the data buffer also from that
	 *       map when all of the buffer has been sent out.
	 */
	class UploadReq : public SchedBase::UploadReqBase {
	public:
		/**
		 * Construct new upload request
		 *
		 * @param s       Socket where this request belongs to
		 * @param buf     Pointer to data buffer to be sent
		 *
		 * \note The data buffer will be deleted once all of it is
		 *       sent out. s_bufferedOut map will be cleared also in
		 *       this case.
		 */
		UploadReq(SSocketWrapper s, std::string *buf)
		: UploadReqBase(s.getScore()()), m_obj(s), m_buf(buf) {}

		/**
		 * Send out data
		 *
		 * @param num      Number of bytes to send out
		 * @return         Number of bytes actually sent out
		 */
		virtual uint32_t doSend(uint32_t num) {
			if (m_buf->size() < num) {
				num = m_buf->size();
			}
			int ret = m_obj.getSocket()->write(m_buf->c_str(), num);
			if (ret < m_buf->size()) {
				m_buf->erase(0, ret);
			} else {
				delete m_buf;
				m_buf = 0;
				s_bufferedOut.erase(m_obj.getSocket());
			}
			return ret;
		}

		//! Send notification to client code, requesting more data
		virtual void notify() {
			m_obj.notify(SOCK_WRITE);
		}

		//! Retrieve number of pending bytes in this request
		virtual uint32_t getPending() const {
			return m_buf ? m_buf->size() : 0;
		}
	private:
		UploadReq();           //!< Forbidden
		SSocketWrapper m_obj;  //!< Keeps reference data for socket
		std::string *m_buf;    //!< Internal data buffer
	};

	//! Download reqest
	class DownloadReq : public SchedBase::DownloadReqBase {
	public:
		DownloadReq(SSocketWrapper s) : DownloadReqBase(s.getScore()()),
		 m_obj(s) {}
		// read out as much data from socket as allowed by parameter
		// return the amount of actually read
		virtual uint32_t doRecv(uint32_t amount) {
			char buf[amount];
			int ret = m_obj.getSocket()->read(buf, amount);
			// Got no data - mh ?
			if (ret == 0) {
				return 0;
			}
			// check if we already have buffered data for this
			// socket, and if so, append to existing buffer.
			// otherwise, create new buffer to store the data
			// in (and also notify client code)
			BIter i = s_bufferedIn.find(m_obj.getSocket());
			if (i == s_bufferedIn.end()) {
				// create new buffer
				std::string *s = new std::string(buf, ret);
				s_bufferedIn[m_obj.getSocket()] = s;
				m_obj.notify(SOCK_READ);
			} else {
				// append to existing buffer
				(*i).second->append(buf, ret);
			}
			return ret;
		}
		virtual void notify() {
			m_obj.notify(SOCK_READ);
		}
	private:
		DownloadReq();             //!< Forbidden
		SSocketWrapper m_obj;
	};

	//! accept request
	class AcceptReq : public SchedBase::ConnReqBase {
	public:
		AcceptReq(SSocketWrapper s) : ConnReqBase(s.getScore()()),
		m_obj(s) {}
		virtual bool doConn() {
			AcceptType *s = m_obj.getSocket()->accept();
			s_accepted.insert(std::make_pair(m_obj.getSocket(), s));
			return true;
		}
		virtual void notify() {
			m_obj.notify(SOCK_ACCEPT);
		}
	private:
		AcceptReq();             //!< Forbidden
		SSocketWrapper m_obj;
	};

	//! connection request
	class ConnReq : public SchedBase::ConnReqBase {
	public:
		ConnReq(SSocketWrapper s, IPV4Address addr, uint32_t timeout)
		: ConnReqBase(s.getScore()()), m_obj(s), m_addr(addr),
		m_timeout(timeout) {}
		virtual bool doConn() {
			return m_obj.getSocet()->connect(m_addr, m_timeout);
		}
	private:
		ConnReq();                  //!< Forbidden
		SSocketWrapper m_obj;
		IPV4Address    m_addr;
		uint32_t       m_timeout;
	};

	//! set of all scheduled sockets
	static std::set<SSocketWrapper> s_sockets;
	typedef typename std::set<SSocketWrapper>::iterator SIter;

	//! @name IO buffers
	//@{
	//! Buffered incoming data
	static std::map<ImplPtr, std::string*> s_bufferedIn;
	//! Buffered outgoing data
	static std::map<ImplPtr, std::string*> s_bufferedOut;
	//! Buffered incoming connections
	static std::map<ImplPtr, AcceptType*> s_accepted;
	//@}

	typedef typename std::map<ImplPtr, std::string*>::iterator BIter;
	typedef typename std::map<ImplPtr, AcceptType* >::iterator AIter;
};

// initialize static data
template<typename P1, typename P2, typename P3 , typename P4, typename P5>
std::set<typename Scheduler<P1, P2, P3, P4, P5>::SSocketWrapper>
Scheduler<P1, P2, P3, P4, P5>::s_sockets;
template<typename P1, typename P2, typename P3 , typename P4, typename P5>
std::map<P2, std::string*>
Scheduler<P1, P2, P3, P4, P5>::s_bufferedIn;
template<typename P1, typename P2, typename P3 , typename P4, typename P5>
std::map<P2, std::string*>
Scheduler<P1, P2, P3, P4, P5>::s_bufferedOut;
template<typename P1, typename P2, typename P3 , typename P4, typename P5>
std::map<P2, typename P1::AcceptType*>
Scheduler<P1, P2, P3, P4, P5>::s_accepted;

//! Socket types and protocols for easier SSocket class usage
namespace Socket {
	class TCP;        //!< Protocol:  TCP
	class UDP;        //!< Protocol:  UDP
	class Client;     //!< Semantics: Client
	class Server;     //!< Semantics: Server
};


/**
 * Traits class for choosing the underlying implementation based on socket type
 * and protocol. The primary template of this class is not implemented. Instead,
 * one of the specializations are expected to be chosen, which define the actual
 * underlying implementation type. If no possible specialization could be found,
 * an undefined reference is generated during application linking.
 */
template<typename Type, typename Proto>
struct Implement;
template<>
struct Implement<Socket::Client, Socket::TCP> {
	typedef SocketClient Impl;
};
template<>
struct Implement<Socket::Client, Socket::UDP> {
	typedef UDPSocket Impl;
};
template<>
struct Implement<Socket::Server, Socket::TCP> {
	typedef SocketServer Impl;
};

/**
 * SSocket template represents a socket that can be serve as communication
 * medium between two remote parties. The exact implementation is chosen
 * based on the template parameters.
 *
 * @param Module        Required, the module governing this socket.
 * @param Type          Type of the socket. Can be either Client or Server
 * @param Protocol      Protocol to be used in the socket, e.g. TCP or UDP
 * @param Impl          Implementation class. By default, this is chosen based
 *                      on Type and Protocol.
 * @param ImplPtr       Exact type on how to handle the implementation object.
 * @param _Scheduler    Scheduler to be used for socket. This should not be
 *                      changed if used within HydraNode, and is provided only
 *                      for completeness here.
 *
 * \note This class acts as a Facade for the Scheduler engine. All public
 *       member function calls of this class are forwarded to the respective
 *       underlying scheduler classes.
 */
template<
	typename Module,
	typename Type,
	typename Protocol   = Socket::TCP,
	typename Impl       = typename Implement<Type, Protocol>::Impl,
	typename ImplPtr    = Impl*,
	typename _Scheduler = Scheduler<Impl>
>
class SSocket {
public:
	//! Type of events
	typedef typename Impl::EventType EventType;

	//! User-defined event handler functor prototype
	typedef boost::function<void (SSocket*, EventType)> HandlerFunc;

	/**
	 * Construct and initialize, optionally setting event handler
	 *
	 * @param h      Optional event handler
	 */
	SSocket(HandlerFunc h = 0) : m_impl(new Impl), m_handler(h) {
		_Scheduler::template addSocket<Module>(
			m_impl, boost::bind(&SSocket::onEvent, this, _1, _2)
		);
	}

	/**
	 * Convenience constructor - performs event handler functor binding
	 * internally.
	 *
	 * @param obj      Object to receive event notifications
	 * @param func     Function to receive event notifications
	 */
	template<typename T>
	SSocket(T *obj, void (T::*func)(SSocket&, EventType))
	: m_impl(new Impl), m_handler(boost::bind(func, obj, _1, _2)) {
		_Scheduler::template addSocket<Module>(
			m_impl, boost::bind(&SSocket::onEvent, this, _1, _2)
		);
	}

	/**
	 * Constructer used only internally during incoming connections
	 * accepting.
	 *
	 * @param s        New socket
	 */
	SSocket(typename Impl::AcceptType *s) : m_impl(s) {
		_Scheduler::template addSocket<Module>(
			s, boost::bind(&SSocket::onEvent, this, _1, _2)
		);
	}

	/**
	 * @name Input/Output
	 */
	//@{

	/**
	 * Write data into socket
	 *
	 * @param buf   Data to be written
	 */
	void write(const std::string &buf) {
		_Scheduler::template write<Module>(m_impl, buf);
	}

	/**
	 * Read data from socket
	 *
	 * @param buf   Buffer to read data into. The data is appended to the
	 *              specified string.
	 */
	void read(std::string *buf) {
		_Scheduler::template read<Module>(m_impl, buf);
	}

	/**
	 * Perform an outgoing connection
	 *
	 * @param addr      Address to connect to
	 * @param timeout   Optional timeout for connection attempt. Defaults
	 *                  to 5 seconds.
	 */
	void connect(IPV4Address addr, uint32_t timeout = 5000) {
		_Scheduler::template connect<Module>(m_impl, addr, timeout);
	}

	/**
	 * Disconnect a connected socket. If the socket is not connected, this
	 * function does nothing. Note that sockets are automatically
	 * disconnected when they are destroyed.
	 */
	void disconnect() {
		_Scheduler::template disconnect<Module>(m_impl);
	}

	/**
	 * Start a listener, waiting for incoming connections
	 *
	 * @param addr     Local address to listen on. If addr.ip is set to
	 *                 0, connections are accepted from all networks,
	 *                 otherwise connections are only accepted from the
	 *                 designated net. For example, if ip is 127.0.0.1,
	 *                 only loopback connections are accepted.
	 */
	void listen(IPV4Address addr) {
		m_impl->listen(addr);
	}

	/**
	 * Accept an incoming connection.
	 *
	 * @return         New socket, which is in connected state, ready to
	 *                 receive and transmit data. The return type depends
	 *                 on the underlying implementation. The returned socket
	 *                 is created in same module as the listening socket.
	 *
	 * \throws if there was no incoming connection pending at this moment.
	 */
	SSocket<
		Module, Socket::Client, Protocol,
		typename Implement<Type, Protocol>::Impl::AcceptType
	>* accept() {
		return new SSocket<
			Module, Socket::Client, Protocol,
			typename Implement<Type, Protocol>::Impl::AcceptType
		>(
			_Scheduler::template accept<Module>(m_impl)
		);
	}

	/**
	 * Send data to specific address. This applies only to UDP sockets.
	 *
	 * @param to       Address to send data to
	 * @param buf      Buffer containing the data to be sent
	 * @param pt       Optional specification of packet type.
	 *                 PACKET_OVERHEAD type packets get slightly increased
	 *                 priority.
	 */
	void send(
		IPV4Address to, const std::string &buf,
		PacketType pt = PACKET_DATA
	) {
		_Scheduler::template send<Module>(to, buf, pt);
	}

	/**
	 * Receive data from designated address. This applies only to UDP type
	 * sockets.
	 *
	 * @param from      Address to receive data from
	 * @param buf       Buffer to write the retrieved data to. The data is
	 *                  appended to the designated string.
	 */
	void recv(IPV4Address from, std::string *buf) {
		_Scheduler::template recv<Module>(from, buf);
	}
	//@}

	/**
	 * @name Event handling
	 */
	//@{
	//! Set event handler, overwriting old handler
	void        setHandler(HandlerFunc handler) { m_handler = handler; }
	//! Set the event handler, performing functor binding internally
	template<typename T>
	void setHandler(T *obj, void (T::*func)(SSocket&, EventType)) {
		m_handler = boost::bind(func, obj, _1, _2);
	}
	//! Retrieve the handler function object
	HandlerFunc getHandler() const              { return m_handler;    }
	//! Clear the existing event handler
	void        clearHandler()                  { m_handler.clear();   }
	//@}
private:
	ImplPtr     m_impl;           //!< Implementation object
	HandlerFunc m_handler;        //!< External event handler function

	/**
	 * Internal event handler, called from scheduler. Forwards the event
	 * to user-defined event handler (if present).
	 *
	 * @param ptr       Implementation pointer generating this event. Must
	 *                  match m_impl member.
	 * @param evt       The event itself
	 */
	void onEvent(ImplPtr ptr, EventType evt) {
		assert(ptr == m_impl);
		m_handler(this, evt);
	}

};

#endif // !__SSOCKET_H__