/*******************************************************************************
 *	Boap.h	Boap RPC protocol
 *			T.Barnaby,	BEAM Ltd,	8/5/03
 *******************************************************************************
 */
#ifndef	Boap_HH
#define	Boap_HH

#include <stdint.h>
#include <BPoll.h>
#include <BSocket.h>
#include <BThread.h>
#include <BError.h>
#include <BEvent.h>
#include <BMutex.h>
#include <BTypes.h>

// Main BOAP Types
const UInt32		BoapMagic = 0x424F4100;
enum BoapType		{ BoapTypeRpc, BoapTypeRpcReply, BoapTypeSignal };
typedef UInt32		BoapService;
enum BoapPriority	{ BoapPriorityLow, BoapPriorityNormal, BoapPriorityHigh };

// Boap packet header
struct BoapPacketHead {
	UInt32		type;
	UInt32		length;
	UInt32		service;
	UInt32		cmd;
};

// Boap packet
class BoapPacket {
public:
			BoapPacket();
			~BoapPacket();

	int		resize(int size);	
	BError		setData(void* data, int nbytes);
	int		nbytes();
	char*		data();
	int		peekHead(BoapPacketHead& head);
	UInt32		getCmd();
	
	int		pushHead(BoapPacketHead& head);
	int		push(Int8 v);
	int		push(UInt8 v);
	int		push(Int16 v);
	int		push(UInt16 v);
	int		push(Int32 v);
	int		push(UInt32 v);
	int		push(Int64 v);
	int		push(UInt64 v);
	int		push(const BString& v);
	int		push(Double v);
	int		push(const BError& v);
	int		push(UInt32 nBytes, const void* data, char* swapType = "1");
	
	int		popHead(BoapPacketHead& head);
	int		pop(Int8& v);
	int		pop(UInt8& v);
	int		pop(Int16& v);
	int		pop(UInt16& v);
	int		pop(Int32& v);
	int		pop(UInt32& v);
	int		pop(Int64& v);
	int		pop(UInt64& v);
	int		pop(BString& v);
	int		pop(Double& v);
	int		pop(BError& v);
	int		pop(UInt32 nBytes, void* data, char* swapType = "1");
private:
	void		copyWithSwap(void* dst, const void* src, UInt32 nBytes, char* swapType); 
	void		updateLen();
	int		osize;
	int		onbytes;
	char*		odata;
	int		opos;
	
};

/*******************************************************************************
 *	Main Client communications classes
 *******************************************************************************
 */

/*******************************************************************************
 *	Base for all Client Objects
 *******************************************************************************
 */
class BoapClientObject : public BSocket {
public:
			BoapClientObject(BString name = "");

	BError		connectService(BString name);			///< Connects to the named service
	BError		disconnectService();				///< Disconnects from the named service
	BString		getServiceName();				///< Get the name of the service
	
	BError		ping(BUInt32& apiVersion);			///< Pings the connection and finds the remotes version number
	BError		setConnectionPriority(BoapPriority priority);	///< Sets the connection priority
	void		setMaxLength(BUInt32 maxLength);		///< Sets the maximum packet length
	void		setTimeout(int timeout);			///< Sets the timeout in micro seconds. -1 is wait indefinitely
protected:
	BError		pingLocked(BUInt32& apiVersion);
	BError		checkApiVersion();
	BError		performCall(BoapPacket& tx, BoapPacket& rx);	///< Performs a RPC call to the named service
	BError		performSend(BoapPacket& tx);			///< Performs a send to the named service
	BError		performRecv(BoapPacket& rx);			///< Performs a receive

	BString		oname;
	BUInt32		oapiVersion;
	BoapPriority	opriority;
	BoapService	oservice;
	int		oconnected;
	BUInt32		omaxLength;
	BoapPacket	otx;
	BoapPacket	orx;
	BMutex		olock;
	int		otimeout;
	int		oreconnect;
};

class BoapSignalObject : public BSocket {
public:
			BoapSignalObject();

protected:
	BError		performSend(BoapPacket& tx);			// Performs a send to the named service
	BoapPacket	otx;
	BoapPacket	orx;
};


/*******************************************************************************
 *	Main Server communications class
 *******************************************************************************
 */
class BoapServiceObject;

class BoapServiceEntry {
public:
				BoapServiceEntry(BoapService service = 0, BoapServiceObject* object = 0){
					oservice = service;
					oobject = object;
				}
	BoapService		oservice;
	BoapServiceObject*	oobject;
};

class BoapServer;

class BoapServerConnection : public BThread {
public:
			BoapServerConnection(BoapServer& boapServer, int fd);

	BError		process();
	BSocket&	getSocket();
	void		setMaxLength(BUInt32 maxLength);
private:
	void*			function();

	BoapServer&		oboapServer;
	BSocket			osocket;
	BoapPacket		orx;
	BoapPacket		otx;
	BUInt32			omaxLength;
};

namespace Boapns {
class Boapns;
}

class BoapServer : public BThread {
public:
	enum		{ NOTHREADS=0, THREADED=1 };

			BoapServer();
			~BoapServer();
	BError		init(BString boapNsHost = "", int threaded = 0, int isBoapns = 0);
	BError		run(int inThread = 0);

	BError		processEvent(BoapPacket& rx);
	
	// Support routines
	BError		addObject(BoapServiceObject* object);
	BError		process(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);
	BError		sendEvent(BoapPacket& tx);
	BSocket&	getSocket();
	BSocket&	getEventSocket();
	BError		processEvent(int fd);
	BString		getHostName();
	void		clientGone(BoapServerConnection* client);
	
	int		getConnectionsNumber();
private:
	void*				function();

	int				othreaded;
	int				oisBoapns;
	Boapns::Boapns*			oboapns;
	BList<BoapServerConnection*>	oclients;
	BEventInt			oclientGoneEvent;
	BList<BoapServiceEntry>		oservices;
	BPoll				opoll;
	BSocket				onet;
	BSocket				onetEvent;
	BSocketAddressINET		onetEventAddress;
	BString				ohostName;
};

/*******************************************************************************
 *	Base for all Server Objects
 *******************************************************************************
 */
class BoapServiceObject;

typedef BError (BoapServiceObject::*BoapFunc)(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);

class BoapFuncEntry {
public:
			BoapFuncEntry(int cmd, BoapFunc func);
	UInt32		ocmd;
	BoapFunc	ofunc;
};

class BoapServiceObject {
public:
			BoapServiceObject(BoapServer& server, BString name = "");
	virtual		~BoapServiceObject();

	BError		setName(BString name);
	
	BError		sendEvent(BString signalName, Int32 arg);
	virtual BError	processEvent(BString objectName, BString name, Int32 arg);

	BString		name();
	BError		doPing(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);
	BError		doConnectionPriority(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);
	BError		process(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);
	virtual BError	processEvent(BoapPacket& rx);
protected:
	BError			sendEvent(BoapPacket& tx);

	BoapServer&		oserver;
	BString			oname;
	BUInt32			oapiVersion;
	BList<BoapFuncEntry>	ofuncList;
};
#endif
