// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// csendmessagequeue.cpp
// 
//
/**
@file
@internalTechnology
*/

#include "f32file.h" 

#include "cmessagehandler.h"
#include "csendmessagequeue.h"
__FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
/**
Two phase constructor
@param aDnsMessage Dns message to be sent 
@param aUnicast True if it is a unicast response
@param aAddr address to which the packet to be send
@return CSendMessageData object
*/
CSendMessageData* CSendMessageData::NewL(CDnsMessage* aDnsMessage, TBool aUnicast,TSockAddr aAddr,MMessageHandler& aCallback)
	{
	CSendMessageData* self = new(ELeave)CSendMessageData(aDnsMessage,aUnicast,aAddr,aCallback);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;	
	}

/**
Constant function which returns the reference to the Dns message object it wraps
@return constant reference to dnsmessage object
*/
const CDnsMessage& CSendMessageData::GetDnsMessage()const
	{
	__FLOG(_L8("CSendMessageData::GetDnsMessage - Exit"));
	return const_cast<CDnsMessage&> (*iMessage);	
	}

/**
Constant function which returns the address of the data to be sent
@return Constant reference to the address
*/
const TSockAddr& CSendMessageData::GetSockAddress()const
	{
	__FLOG(_L8("CSendMessageData::GetSockAddress - Exit"));
	return iSockAddr;	
	}

/**
Constant function which returns true if the message is unicast
@return True if message is unicast
*/
const TBool& CSendMessageData::IsUnicast()const
	{
	__FLOG(_L8("CSendMessageData::IsUnicast - Exit"));
	return iUnicast;	
	}
/**
Destructor
*/
CSendMessageData::~CSendMessageData()
	{
	__FLOG(_L8("CSendMessageData::~CSendMessageData - Entry"));
	delete iMessage;
	__FLOG(_L8("CSendMessageData::~CSendMessageData - Exit"));
	__FLOG_CLOSE;
	}

/**
Two phase constructor
@param aDnsMessage DnsMessage to be send
*/
void CSendMessageData::ConstructL()
	{
	__FLOG_OPEN(KMDNSSubsystem, KComponent);	
	}
	
/**
Constructor
@param aUnicast True if it is a unicast response
@param aAddr Address to which the data to be sent
*/
CSendMessageData::CSendMessageData(CDnsMessage* aDnsMessage,TBool aUnicast,TSockAddr aAddr,MMessageHandler& aCallback):iMessage(aDnsMessage),iUnicast(aUnicast),iSockAddr(aAddr),iCallback(aCallback)
	{
	
	}

MMessageHandler& CSendMessageData::Callback()
	{
	__FLOG(_L8("CSendMessageData::Callback - Exit"));
	return iCallback;	
	}
/**
Two phase Constructor
@param aSocket reference to the RSocket
*/
CSendMessageQueue* CSendMessageQueue::NewL(RSocket& aSocket)
	{
	CSendMessageQueue* self = new (ELeave)CSendMessageQueue();
	CleanupStack::PushL(self);
	self->ConstructL(aSocket);
	CleanupStack::Pop(self);
	return self;		
	}
	
/**
Constructor
*/
CSendMessageQueue::CSendMessageQueue():CTimer(EPriorityStandard)
	{
	CActiveScheduler::Add(this);	
	}
	
/**
Function derived from MSocketHandler Observer.
This method will be called on succesfull sending of the packet.
*/
void CSendMessageQueue::OnCompletionL(TDesC8& /*aData*/, const TSockAddr& /*aAddr*/, TInt /*aLength*/)
	{
	__FLOG(_L8("CSendMessageQueue::OnCompletionL - Entry"));
	if(iSendMessageData)
		{
		iIsSocketActive = EFalse;
		iSendMessageData->Callback().OnPacketSendL(KErrNone);	
		}
	delete iSendMessageData;
	iSendMessageData = NULL;
	if(iMessageQueue.Count() > 0 && !IsActive() )
		{
		StartTransmit();
		}
	__FLOG(_L8("CSendMessageQueue::OnCompletionL - Exit"));
	}

/*
Function derived from MSocketHandler Observer.
This method will be called on error in  sending the packet.
*/	
void CSendMessageQueue::OnError(TInt aError)
	{
	__FLOG(_L8("CSendMessageQueue::OnError - Entry"));
	if(iSendMessageData)
		{
		iSendMessageData->Callback().OnPacketSendL(aError);	
		}	
	__FLOG(_L8("CSendMessageQueue::OnError - Exit"));
	}
	
/**
Destructor
*/	
CSendMessageQueue::~CSendMessageQueue()
	{
	__FLOG(_L8("CSendMessageQueue::~CSendMessageQueue - Entry"));
	delete iSendSocket;
	delete iSendMessageData;
	iMessageQueue.ResetAndDestroy();
	iMessageQueue.Close();	
	iOutput.Close();
	__FLOG(_L8("CSendMessageQueue::~CSendMessageQueue - Exit"));
	__FLOG_CLOSE;
	}

/**
Two phase constructor
@param refernce to RSocket
*/	
void CSendMessageQueue::ConstructL(RSocket& aSocket)
	{
	__FLOG(_L8("CSendMessageQueue::ConstructL - Entry"));
	iSendSocket = CSocketHandler::NewL(aSocket, *this, ESocketSend);
	CTimer::ConstructL();
	iIsSocketActive = EFalse;
	__FLOG(_L8("CSendMessageQueue::ConstructL - Exit"));
	}
	
/**
Derived from CActive
*/	
void CSendMessageQueue::RunL()
	{
	__FLOG(_L8("CSendMessageQueue::RunL - Entry"));
	if (MessageReady())
		{
		iSendMessageData = NextDnsMessageL();
		CleanupStack::PushL(iSendMessageData);
		const CDnsMessage& dnsMessage = iSendMessageData->GetDnsMessage();
		// close the previously allocated memory.  
		iOutput.Close();
		//composes the message from the Dnsmessage object .
		CDnsMessageComposerParser* comPos = CDnsMessageComposerParser::NewL();
		CleanupStack::PushL(comPos);
		comPos->CreateMessageL(iOutput, iSendMessageData->GetDnsMessage());
		TSocketHandlerParams param(ESocketSendTo,&iOutput,&(iSendMessageData->GetSockAddress()));
		iSendSocket->Activate(param);
		iIsSocketActive = ETrue;
		CleanupStack::PopAndDestroy();//comPos
		CleanupStack::Pop();
		}
	__FLOG(_L8("CSendMessageQueue::RunL - Exit"));
	}
/*
 * Nothing to do.
 */
void CSendMessageQueue::DoCancel()
	{
	__FLOG(_L8("CSendMessageQueue::DoCancel - Exit"));
	}

void CSendMessageQueue::QueueDnsMessageL(const CSendMessageData& aMessageData)
	{
	__FLOG(_L8("CSendMessageQueue::QueueDnsMessageL - Entry"));
	if (aMessageData.IsUnicast())
		{
		// Unicast replies are to be given higher priority .
		// Insert the  unicast response in the queue.
		TInt count(iMessageQueue.Count());
		TInt index(0);
		for (; index < count; ++index)
			{
			if (!iMessageQueue[index]->IsUnicast())
				{
				break;
				}
			}
		iMessageQueue.InsertL(&aMessageData, index);
		
		iNextTransmit.HomeTime();
		Cancel();
		}
	else
		{
		// Queue the packet
		iMessageQueue.AppendL(&aMessageData);
		}
	
	// kick the transmit limiter, so we're sure the message will be sent
	if(!iIsSocketActive)
	    {
	    NextTransmitTime(iNextTransmit);
	    }
	__FLOG(_L8("CSendMessageQueue::QueueDnsMessageL - Exit"));	
	}
	
void CSendMessageQueue::NextTransmitTime(TTime aTime)
	{
	__FLOG(_L8("CSendMessageQueue::NextTransmitTime - Entry"));
	// if we aren't already active, set the next TX time
	if (!IsActive())
		{
		TTime now;
		now.HomeTime();
		
		if (aTime > now)
			{
			At(aTime);
			}
		else
			{
			StartTransmit();
			}
		}	
	__FLOG(_L8("CSendMessageQueue::NextTransmitTime - Exit"));
	}

void CSendMessageQueue::StartTransmit()
	{
	__FLOG(_L8("CSendMessageQueue::StartTransmit - Entry"));
	SetActive();
	TRequestStatus *status = &iStatus;
	User::RequestComplete(status,KErrNone);
	__FLOG(_L8("CSendMessageQueue::StartTransmit - Exit"));
	}

CSendMessageData* CSendMessageQueue::NextDnsMessageL()
	{
	__FLOG(_L8("CSendMessageQueue::NextDnsMessageL - Entry"));
	if (0 == iMessageQueue.Count())
		{
		User::Leave(KErrNotReady);
		}
		
	CSendMessageData* ret = iMessageQueue[0];
	iMessageQueue.Remove(0);
	// calculate the next possible transmit time.
	iNextTransmit.HomeTime();
	TInt delay = 20 + (Math::Rand(iRandomSeed) % 100);
	iNextTransmit += TTimeIntervalMicroSeconds(delay * 1000);  
	__FLOG(_L8("CSendMessageQueue::NextDnsMessageL - Exit"));
	return ret;
	}

TBool CSendMessageQueue::MessageReady()
	{
	__FLOG(_L8("CSendMessageQueue::MessageReady - Entry"));
	TBool ready = EFalse;
	if (0 != iMessageQueue.Count())
		{
		if (iMessageQueue[0]->IsUnicast())
			{
			// unicast messages go out straight away.
			ready = ETrue;
			}
		else
		 	{
		 	TTime now;
		 	now.HomeTime();
		 	if (!IsActive())
		 		{
		 		if (now >= iNextTransmit)
		 			{
		 			ready = ETrue;
		 			}
		 		else
		 			{
					NextTransmitTime(iNextTransmit);		 		
		 			}
		 		}
			}
		}
	__FLOG(_L8("CSendMessageQueue::MessageReady - Exit"));
	return ready;
	}
					
