/******************************************************************************
Copyright (C) 2005 Matteo Lucarelli 

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
******************************************************************************/

//  WIN NEED: libpthreadGC.a , libws2_32.a
//  LIN NEED: lpthread

#if !defined(_ULM_H_INCLUDED_)
#define _ULM_H_INCLUDED_

#ifdef WIN32
#include <Winsock2.h>
#else
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#define ULM_MAX_MSG	32768  // max message dim
#define ULM_HEAD_DIM	13     // header dim


// ulmSocket utility class - win/lin compatibility layer ///////////////////////

class ulmSocket
{
public:
	ulmSocket(unsigned short port);
	virtual ~ulmSocket();

	// host can be name or addr
	// use addr to avoid time-consuming resolution 
	int Send(const void* buff,      // buffer to send
		 long size,             // size of buffer
		 const char* host,      // destination host
		 unsigned short port);  // destination port
		 
	// wait (without timeout) incoming data
	// sender address and port are returned in fromHost:fromPort
	// return value is the size of packet (could be higher then size!)
	int Recv(void *buff,               // buffer where put incoming msg
		 long size,                // size (must be allocated)
		 char* fromHost,           // sender address - char[16] or NULL
		 unsigned short* fromPort, // sender port - NULL if don't care
		 long msTimeout=500);      // Timeout (-1 infinite)

private:

#ifdef WIN32
	SOCKET m_hSocket;
	bool m_WsaInit;
#else
	int m_hSocket;
#endif 
	struct sockaddr_in m_BindAddr;
};

// ulmMessage - to send messages ///////////////////////////////////////////////

class ulmMessage
{
public:
	// header chars are configurable
	// message with different header are discarded from the receiver
	ulmMessage(char ch1=0,  // first configurable header char
	           char ch2=0,  // second configurable header char
		   char ch3=0); // third configurable header char
		   
	virtual ~ulmMessage();

	char msg_buf[ULM_MAX_MSG+ULM_HEAD_DIM];  // the message
	unsigned short msg_dim;  // message dim (without header)

	// add a text field to current message
	// text value must be null terminate string of printable chars
	int FieldAdd(	const char* label,    // string label 
			const char* value);   // string value
			
	// extract a text fiels from message
	int FieldGet(	const char* label,    // label to search (string)
			char* value,          // returned value (string)
			long valueDimBytes);  // buffer dim - must be allocated

	// add a binary field to message - no more text field are admitted
	int BinaryFieldAdd(	const char* label,    // string label
				const void* data,     // binary value
				unsigned short size); // value dim in bytes
				
	// extract a binary field from message
	// if size>sizeof(data) then data war too small
	int BinaryFieldGet(	const char* label,      // label to search
				void* data,             // buffer to return data
				unsigned short maxsize, // max byte to return
				unsigned short* size);  // found field size
	
	// send current message to host:port using local port lport
	// Return 0: ok
	//	 -1: generic error
	//	 -2: unresolved name
	int Send(	unsigned short lport,    // local port used to send
			const char* host,        // destination host
			unsigned short port);    // destination port

	
	// empty current message
	void Clear();
	
private:
	char computeXOR(char* buf,long dim);
	char m_ch1,m_ch2,m_ch3;
	bool m_binFlag;  // true if message contains binary fields
};

// ulmReceiver - ///////////////////////////////////////////////////////////////
// to receive message put a global function defined friend in handler class
//
//      friend ulmCallback ulmCb;
//
//  the function body for windows will be something like:
//
//      void __stdcall ulmCb(ulmMessage msg, void* pClient){
//          ((YourHandlerClass*)pClient)->YourHandlerFunction(msg);
//      }
//
//  and for linux:
//
//      void ulmCb(ulmMessage msg, void* pClient){
//          ((YourHandlerClass*)pClient)->YourHandlerFunction(msg);
//      }
//
////////////////////////////////////////////////////////////////////////////////

typedef void ulmCallback(ulmMessage msg,const void* pClient); 

class ulmReceiver  
{
public:
	// note: the receiver start immediatly
	// header chars are configurable
	// use 0 means 'don't care', else message with
	// different header are discarded
	ulmReceiver(	void* pClient,          // pointer to the handler object
			ulmCallback* Cb,             // pointer to the callback 
			unsigned short ListenOnPort, // port to listen to
			char ch1=0,                  // first header char
			char ch2=0,                  // second header char         
			char ch3=0);                 // third header char
			
	virtual ~ulmReceiver();

private:

	struct RecvThreadParms{
		ulmCallback* ClientCb;
		void* pClient;
		ulmSocket* Socket;
		char ch1,ch2,ch3;
	};

	struct RecvThreadParms m_RTparms;

	pthread_t m_pRecvThread;
	static void* RecvThread(void* pParms);
};

#endif  // !defined(_ULM_H_INCLUDED_)