/*
*	Base server class that listens on a socket.
*
*	©Copyright 2002-2004, Microsoft Corporation
*
*	Written by Sergey Vasilevskiy.
*/

#define PARTIAL_MESSAGE	-1	//return code from ProcessMessage indicating that another recv is needed
#define SEND_NO_ACK		-2	//return code from ProcessMessage indicating that no ack is needed to send back

//Non-member function that is the entry function for the new thread
template <typename LocalVarsType>
void RunProcessingThread(void* ptr)
{
	TRunProcessThreadInput<LocalVarsType>* pInput = ((TRunProcessThreadInput<LocalVarsType>*)ptr);

	pInput->obj->ProcessingThread(&(pInput->ProcessingInput));

	delete pInput;
}

/*
*	Template on the processing thread local variable type.
*	The local variable will be defined in subclasses.
*/
template <typename LocalVarsType>
class CBaseServer
{
	SOCKET			m_sockfd;	
	char*			m_server;
	unsigned short	m_shPort;
	int				m_iMsgSize;		//size of the message to receive
	HANDLE			m_hExitEvent;	//signaled on destruction
	HANDLE			m_hProcessThreadExited; //signaled on ProcessingThread exit
	long			m_NumThreadsCreated;
	HANDLE			m_hStopListen;	//external event - signal to exit from the listening thread
	//long*			m_pStopListen;	//external flag - signal to exit from the listening thread
	WSAEVENT		m_hWSAEvent;
	WSAEVENT		m_hWaitListenEvents[2];
	bool			m_bVerbose;	

protected:	
	long	    	m_bRunProcessing;	//if true, then ProcessingThread does its work
	long    		m_iDeadlockCount;	// counts the total number of deadlocks	
    TPCE::CMutex	m_OutputLock;
	
private:
	void ThrowError(char *szText);

	friend void RunProcessingThread<LocalVarsType>(void* ptr);	//starts a new thread; cannot be a member function
	void ProcessingThread(void *ptr);

	virtual LocalVarsType* InitializeLocal(void);	//initializes thread local variables - must be defined in subclasses
	// Called from ProcessingThread - actually processes received message.
	// Pure abstract function.
	//
	virtual int ProcessMessage(char* mes, DWORD iTotalBytesRecvd, LocalVarsType* pLocal, int iThreadNum, PSUTReplyMessage pSUTReply) = 0;
	virtual void DeInitializeLocal(LocalVarsType* pLocal);	//deletes the local thread variables

public:
	//CBaseServer(char *szServer, unsigned short shPort, int iMsgSize, long* pStopListen, bool bVerbose);
	CBaseServer(char *szServer, unsigned short shPort, int iMsgSize, HANDLE hStopListen, bool bVerbose);
	~CBaseServer();

	void ListenAndDispatchThread(void *ptr);

	// Output to stdout protected by a lock.
	// Needed so that one processing thread output doesn't appear in the
	// middle of another thread output.
	//
	void OutputStdin(char *szFmt, ...);
	// Same as OutputStdin, but prefix with a timestamp.
	//
	void OutputStdinTimed(char *szFmt, ...);

};

//Input structure for ProcessingThread
typedef struct TProcessThreadInput
{
	SOCKET	hSocket;	//newly established connection socket
	int		iThreadNum;	
} *PProcessThreadInput;

//Input structure for RunProcessingThread
template <typename LocalVarsType> 
struct TRunProcessThreadInput
{
	CBaseServer<LocalVarsType>			*obj;	//should be pointer to class object on which to call RunProcessingThread
	TProcessThreadInput	ProcessingInput;
};

//Recv at least N bytes 
int WSARecvNBytes(
  SOCKET s,
  LPWSABUF lpBuffer,
  LPDWORD lpNumberOfBytesRecvd,
  LPDWORD lpFlags,
  DWORD	dwBytesToRecv	//block until this number of bytes has been received  
);
