/*******************************************************************************
 *	Boap.h	Boap RPC protocol
 *			T.Barnaby,	BEAM Ltd,	8/5/03
 *	Copyright (c) 2012 All Right Reserved, Beam Ltd, http://www.beam.ltd.uk
 *******************************************************************************
 */
#ifndef	Boap_HH
#define	Boap_HH

#include <stdint.h>
#include <BTypes.h>
#include <BPoll.h>
#include <BSocket.h>
#include <BThread.h>
#include <BError.h>
#include <BEvent1.h>
#include <BMutex.h>
#include <BTimeStamp.h>
#include <BBuffer.h>

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

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

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

	BUInt32		getCmd();
	
	int		peekHead(BoapPacketHead& head);
	int		pushHead(BoapPacketHead& head);
	int		popHead(BoapPacketHead& head);
	void		updateHead();
};

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

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

	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
	virtual BError	handleReconnect(BError err);			///< Handle a reconnect performing autorisaztion if required

	BString		oname;
	BUInt32		oapiVersion;
	BoapPriority	opriority;
	BoapService	oservice;
	int		oconnected;
	BUInt32		omaxLength;
	BoapPacket	otx;
	BoapPacket	orx;
	BMutex		olock;
	int		otimeout;
	int		oreconnect;					///< Handle an automatic reconnect on timeout
};

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);
	virtual		~BoapServerConnection();

	virtual BError		init();				///< Initialise connection
	virtual BError		process();
	virtual BSocket&	getSocket();
	virtual void		setMaxLength(BUInt32 maxLength);
	virtual BError		getHead(BoapPacketHead& head);

	virtual BError		validate();			///< Validate the connection
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();
	virtual				~BoapServer();
	virtual BError			init(BString boapNsHost = "", int port = 0, int threaded = 0, int isBoapns = 0);
	virtual BError			run(int inThread = 0);
	virtual BError			process(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);
	virtual BError			processEvent(BoapPacket& rx);
	
	// Support routines
	virtual BError			addObject(BoapServiceObject* object);
	virtual BError			sendEvent(BoapPacket& tx);
	virtual BError			processEvent(int fd);
	virtual void			clientGone(BoapServerConnection* client);
	BSocket&			getSocket();
	BSocket&			getEventSocket();
	BString				getHostName();
	int				getConnectionsNumber();

	// Override functions
	virtual BoapServerConnection*	newConnection(int fd, BSocketAddressINET address);

private:
	void*				function();

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

public:
	BUInt64				onumOperations;
};

/*******************************************************************************
 *	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);
	BUInt32		ocmd;
	BoapFunc	ofunc;
};

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

	BError		setName(BString name);
	
	BError		sendEvent(BString signalName, BInt32 arg);
	virtual BError	processEvent(BString objectName, BString name, BInt32 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
