#ifndef ___IOCP_H_INCLUDED___
#define ___IOCP_H_INCLUDED___

#include <windows.h>
#include "socket_client.h"

///////////////////////////////////////////////////////////////////////////////////////////////
// A template class (wrapper) to handle basic operations 
// related to IO Completion Ports (IOCP).

template<class T>
class IOCPSimple {
private:
	HANDLE	m_hIocp;
	DWORD	m_dwTimeout;

protected:
	// Useful method to queue a socket (handler in fact) in the IOCP's
	// queue, without requesting any 'physical' I/O operations. 
	BOOL PostQueuedCompletionStatus( ClientSocket<T> *sock, MYOVERLAPPED *pMov ) {
		return ::PostQueuedCompletionStatus( this->m_hIocp, 1, 
			(DWORD) sock, (OVERLAPPED*) pMov );
	};

public:
	~IOCPSimple() {
		// Destroy the IOCP handler 
		::CloseHandle( this->m_hIocp );
	};

	IOCPSimple( DWORD dwTimeout = 0 ) {
		// This will be used as a timeout value to wait
		// for completion events from IOCP (in milliseconds).
		// See 'GetQueuedCompletionStatus(...)' for more 
		// details. If is set to 0 == means infinite.
		m_dwTimeout = dwTimeout;

		// Create the IOCP handler 
		this->m_hIocp = ::CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
		if ( this->m_hIocp == NULL ) {
			throw "CreateIoCompletionPort() failed to create IO Completion port.";
		}
	};

	// Method associates Socket handler with the handler
	// of the IOCP. It is important to do this association
	// first of all, once Socket is accepted and is valid.
	// In fact, this method registers socket with the IOCP, 
	// so IOCP will monitor every I/O operation happening 
	// with the socket.
	void AssociateSocket( ClientSocket<T> *sock ) {
		if ( sock != NULL ) {
			// As a key of this association, the pointer to the 
			// "sock" is set. The pointer to the "sock" will be 
			// returned with "GetQueuedCompletionStatus" call
			// whenever a I/O operation completes, so we always know
			// which is the relevant socket.
			::CreateIoCompletionPort( (HANDLE) sock->GetSocket(), 
				this->m_hIocp, (DWORD) sock, 0 );
		}
	};

	// Queue in the IOCP's queue with the status 'pending'.
	// This is a fictive status (not real). Method is useful in
	// cases when packets/data buffers to send via socket are yet
	// not ready (e.g. due some internal, time consuming, calculations) 
	// and rather than keeping socket somewhere, in separate structures, 
	// place it in the queue with status pending. It will be returned
	// shortly with the "GetQueuedCompletionStatus" call anyway.
	BOOL SetPendingMode( ClientSocket<T> *sock ) {
		BOOL ret = false;

		if ( sock != NULL ) {
			// Get a free instance of the structure
			// from the pool.
			MYOVERLAPPED *mov = Overlapped::Get();

			if ( mov != NULL ) {
				mov->OperationType = _PENDING;
				mov->nSession = sock->GetSession();
				ret = PostQueuedCompletionStatus( sock, mov );
				if ( !ret ) Overlapped::Release( mov );
			}
		}

		return ret;
	};

	// Queue in the IOCP's queue with the status 'close'.
	// This is a fictive status (not real). Useful only 
	// when close socket cases should be treated/handled 
	// within the routine, handling I/O completion statuses, 
	// and just to not spread the code across different 
	// modules/classes.
	BOOL SetCloseMode( ClientSocket<T> *sock, MYOVERLAPPED *pMov = NULL ) {
		BOOL ret = false;

		if ( sock != NULL ) {
			// Get a free instance of the structure
			// from the pool.
			MYOVERLAPPED *mov = Overlapped::Get();

			if ( mov != NULL ) {
				mov->OperationType = _CLOSE;
				mov->nSession = sock->GetSession();

				if (pMov != NULL) {
					mov->DataBuf.buf = pMov->DataBuf.buf;
					mov->DataBuf.len = pMov->DataBuf.len;
				}

				ret = PostQueuedCompletionStatus( sock, mov );
				if ( !ret ) Overlapped::Release( mov );
			}
		}

		return ret;
	};

	// Queue in the IOCP's queue with the status 'accept'.
	// This is a fictive status (not real). When a new
	// client connection is accepted by the server socket,
	// method is used to acknowledge the routine, handling 
	// I/O completion statuses, of this. Same reason as per 
	// 'close' status, just to have a unique piece of code 
	// handling all the possible statuses.
	BOOL SetAcceptMode( ClientSocket<T> *sock ) {
		BOOL ret = false;

		if ( sock != NULL ) {
			// Get a free instance of the structure
			// from the pool.
			MYOVERLAPPED *mov = Overlapped::Get();

			if ( mov != NULL ) {
				mov->OperationType = _ACCEPT;
				mov->nSession = sock->GetSession();
				ret = PostQueuedCompletionStatus( sock, mov );
				if ( !ret ) Overlapped::Release( mov );
			}
		}

		return ret;
	};

	// This method is just a wrapper. For more details, 
	// see MSDN for "GetQueuedCompletionStatus". The only
	// difference is, method is adjusted to handle 
	// 'ClientSocket<T>' (as registered with 'AssociateSocket(...)')
	// rather than pointer to the 'DWORD'.
	BOOL GetQueuedCompletionStatus( LPDWORD pdwBytesTransferred,
		ClientSocket<T> **sock, MYOVERLAPPED **lpOverlapped ) {
		return ::GetQueuedCompletionStatus( this->m_hIocp, pdwBytesTransferred, 
			(LPDWORD) sock, (LPOVERLAPPED*) lpOverlapped, m_dwTimeout );
	};
};

#endif