///////////////////////////////////////////////////////////////////////////////
// MQ4CPP - Message queuing for C++
// Copyright (C) 2004-2007  Riccardo Pompeo (Italy)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//

#define SILENT
#include "Router.h"
#include "Logger.h"
#include <string>
#include <strstream>
using namespace std;

//#define DISPLAY_TRAFFIC
#ifdef DISPLAY_TRAFFIC
#define DISPLAYC2S { cout << ">"; }
#define DISPLAYS2C { cout << "<"; }
#define DISPLAYB2S { cout << "*"; }
#else
#define DISPLAYC2S
#define DISPLAYS2C
#define DISPLAYB2S
#endif

RemoteRouter::RemoteRouter(const char* theName, const char* theHost,int thePort, const char* theTarget) 
	   :MessageQueue(theName)
{
	TRACE("RemoteRouter::RemoteRouter - start")
	TRACE("Queue name=" << getName())
	itsSeqNum=0;
	itsProxy=0;
	itsServer=0;
	itsHost=theHost;
	itsPort=thePort;
	itsTarget=theTarget;
	itsConnected=false;
	
	for(int i=0; i < MAXSESSIONS; i++)
	{
		itsSessions[i].proxy=0;
		itsSessions[i].client=0;
		itsSessions[i].seqnum=0;
	}
	
	SCHEDULE(this,5000);
	MessageProxyFactory::lookupAt(itsHost.c_str(),itsPort,itsTarget.c_str(),this);			
	TRACE("RemoteRouter::RemoteRouter - end")
}
	
RemoteRouter::~RemoteRouter() 
{
	TRACE("RemoteRouter::~RemoteRouter - start")

	TRACE("RemoteRouter::~RemoteRouter - end")
}

void RemoteRouter::onLookup(LookupReplyMessage* theMessage)
{
	TRACE("RemoteRouter::onLookup - start")
	if(itsConnected==false && !theMessage->isFailed())
	{
		itsServer=theMessage->getHandle();
		itsProxy=theMessage->getSender();
		itsConnected=true;
		LOG("Remote thread lookup ok.")	
	}
	TRACE("RemoteRouter::onLookup - end")
}

void RemoteRouter::onWakeup(Wakeup* theMessage)
{
	TRACE("RemoteRouter::onWakeup - start")
	if(itsConnected==false || (itsConnected==true && !isStillAvailable(itsProxy)))
	{
		itsConnected=false;	
		TRACE("Try to lookup service")
		MessageProxyFactory::lookupAt(itsHost.c_str(),itsPort,itsTarget.c_str(),this);			
	}
	TRACE("RemoteRouter::onWakeup - end")
}

void RemoteRouter::onMessage(Message* theMessage)
{
	TRACE("RemoteRouter::onMessage - start")
	TRACE("Thread name=" << getName())

	try
	{
		if(theMessage->is("Wakeup"))
		{
			TRACE("Call onWakeup")
			onWakeup((Wakeup*)theMessage);
		}
		else if(theMessage->is("LookupReplyMessage"))
		{
			TRACE("Call onLookup")
			onLookup((LookupReplyMessage*)theMessage);
		}
		else if(theMessage->is("NetworkMessage"))
		{
			NetworkMessage* aRequest=(NetworkMessage*)theMessage;

			if(itsProxy==aRequest->getSender() && itsServer==aRequest->getRemoteSender() && !aRequest->isBroadcasting())
			{
				TRACE("Handling message from server")
				DISPLAYS2C

				int anIndex=aRequest->getSequenceNumber() % MAXSESSIONS;
				if(itsSessions[anIndex].proxy!=0 && isStillAvailable(itsSessions[anIndex].proxy))
				{
					_TIMEVAL current=Timer::timeExt();
					long elapsed=Timer::subtractMillisecs(&itsSessions[anIndex].time,&current);

					if(elapsed < SESSION_EXPIRATION_TIME)
					{
						NetworkMessage* aReply=(NetworkMessage*)aRequest->clone();
						aReply->setSender(getID());
						aReply->setRemoteSender(0);
						aReply->setTarget(itsSessions[anIndex].client);
						aReply->setSequenceNumber(itsSessions[anIndex].seqnum);
						post(itsSessions[anIndex].proxy,aReply); 
					}

					itsSessions[anIndex].proxy=0;
					itsSessions[anIndex].client=0;
					itsSessions[anIndex].seqnum=0;
				}
			}
			else if(!aRequest->isBroadcasting())
			{
				TRACE("Handling message from client")
				if(itsConnected==true)
				{
					DISPLAYC2S
					
					unsigned short anIndex=itsSeqNum % MAXSESSIONS;					
					itsSessions[anIndex].proxy=aRequest->getSender();
					itsSessions[anIndex].client=aRequest->getRemoteSender();
					itsSessions[anIndex].seqnum=aRequest->getSequenceNumber();
					itsSessions[anIndex].time=Timer::timeExt();

					NetworkMessage* aNewRequest=(NetworkMessage*)aRequest->clone();
					aNewRequest->setSender(getID());
					aNewRequest->setRemoteSender(0);
					aNewRequest->setTarget(itsServer);
					aNewRequest->setSequenceNumber(itsSeqNum);
					post(itsProxy,aNewRequest);

					itsSeqNum++;
				}			
			}
		}
	}
	catch(Exception& ex)
	{
		DISPLAY("RemoteRouter::onMessage(" << getName() << ") : " << ex.getMessage().c_str())
	}
	catch(...)
	{		
		DISPLAY("RemoteRouter::onMessage(" << getName() << ") : Unhandled exception")
	}

	TRACE("RemoteRouter::onMessage - end")
}

LocalRouter::LocalRouter(const char* theName, const char* theTarget) 
	   :MessageQueue(theName)
{
	TRACE("LocalRouter::LocalRouter - start")
	TRACE("Queue name=" << getName())
	itsSeqNum=0;
	if(!lookup(theTarget,itsServer))
		throw ThreadException("Lookup of local service failed");
	
	for(int i=0; i < MAXSESSIONS; i++)
	{
		itsSessions[i].proxy=0;
		itsSessions[i].client=0;
		itsSessions[i].seqnum=0;
	}

	TRACE("LocalRouter::LocalRouter - end")
}
	
LocalRouter::~LocalRouter() 
{
	TRACE("LocalRouter::~LocalRouter - start")

	TRACE("LocalRouter::~LocalRouter - end")
}

void LocalRouter::onMessage(Message* theMessage)
{
	TRACE("LocalRouter::onMessage - start")
	TRACE("Thread name=" << getName())

	try
	{
		if(theMessage->is("NetworkMessage"))
		{
			NetworkMessage* aRequest=(NetworkMessage*)theMessage;

			if(itsServer==aRequest->getSender() && aRequest->getRemoteSender()==0 && !aRequest->isBroadcasting())
			{
				TRACE("Handling message from server")	
				int anIndex=aRequest->getSequenceNumber() % MAXSESSIONS;
				if(itsSessions[anIndex].proxy!=0 && isStillAvailable(itsSessions[anIndex].proxy))
				{
					DISPLAYS2C

					_TIMEVAL current=Timer::timeExt();
					long elapsed=Timer::subtractMillisecs(&itsSessions[anIndex].time,&current);

					if(elapsed < SESSION_EXPIRATION_TIME)
					{
						NetworkMessage* aReply=(NetworkMessage*)aRequest->clone();
						aReply->setSender(getID());
						aReply->setRemoteSender(0);
						aReply->setTarget(itsSessions[anIndex].client);
						aReply->setSequenceNumber(itsSessions[anIndex].seqnum);
						post(itsSessions[anIndex].proxy,aReply); 
					}

					itsSessions[anIndex].proxy=0;
					itsSessions[anIndex].client=0;
					itsSessions[anIndex].seqnum=0;
				}
			}
			else if (!aRequest->isBroadcasting())
			{
				TRACE("Handling message from client")
				DISPLAYC2S

				unsigned short anIndex=itsSeqNum % MAXSESSIONS;					
				itsSessions[anIndex].proxy=aRequest->getSender();
				itsSessions[anIndex].client=aRequest->getRemoteSender();
				itsSessions[anIndex].seqnum=aRequest->getSequenceNumber();
				itsSessions[anIndex].time=Timer::timeExt();

				NetworkMessage* aNewRequest=(NetworkMessage*)aRequest->clone();
				aNewRequest->setSender(getID());
				aNewRequest->setRemoteSender(getID());
				aNewRequest->setTarget(itsServer);
				aNewRequest->setSequenceNumber(itsSeqNum);
				post(itsServer,aNewRequest);

				itsSeqNum++;
			}
		}
	}
	catch(Exception& ex)
	{
		DISPLAY("LocalRouter::onMessage(" << getName() << ") : " << ex.getMessage().c_str())
	}
	catch(...)
	{		
		DISPLAY("LocalRouter::onMessage(" << getName() << ") : Unhandled exception")
	}

	TRACE("LocalRouter::onMessage - end")
}

