// Copyright (c) 2000-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:
//

#ifdef _DEBUG
#undef _MSG_NO_LOGGING
#endif

#include "SMSSSEND.H"

#include <smuthdr.h>			
#include <msventry.h>
#include <txtrich.h>
#include <smutset.h>

#include "SMSSPAN.H"
#include "SMSRecipientSend.h"

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS  
#include <tmsvsmsentry.h>
#endif


CSmsSend* CSmsSend::NewL(TSmsProgress& aProgress,CMsvServerEntry& aServerEntry, RFs& aFs, CSmsHeader& aHeader, CRichText& aRichText, TMsvEntry& aEntry)
	{
	return new(ELeave) CSmsSend(aProgress, aServerEntry, aFs, aHeader, aRichText, aEntry);
	}

CSmsSend::~CSmsSend()
	{
	Cancel();
	}

void CSmsSend::Start(TRequestStatus& aStatus,TMsvId aMsvId, CSmsRecipientSend* aRecipientSend)
	{
	__ASSERT_DEBUG(iState==ESmsSendWaiting,Panic(KSmssPanicUnexpectedState));

	iRecipientSend = aRecipientSend;
	Queue(aStatus);
	iEntry=TMsvEntry();
	TRAPD(err,OpenEntryL(aMsvId));
	RequestComplete(&iStatus, err, ETrue);
	}

void CSmsSend::DoSmssCancel()
	{
	switch (iState)
		{
		case ESmsSendWaiting:
			break;
		case ESmsSendOpeningEntry:
			break;
		case ESmsSendUpdatingRecipient:				
		case ESmsSendSendingEntry:
			{
			iRecipientSend->Cancel();
			break;
			}
		default:
			Panic(KSmssPanicUnexpectedState);
		}
	}

CSmsSend::CSmsSend(TSmsProgress& aProgress, CMsvServerEntry& aServerEntry, RFs& aFs, CSmsHeader& aHeader, CRichText& aRichText, TMsvEntry& aEntry)
:	CSmssActive(aFs, aServerEntry, KSmsSessionPriority),
	iProgress(aProgress),
	iEntry(aEntry),
	iSmsHeader(aHeader),
	iRichText(aRichText),
	iState(ESmsSendWaiting)
	{
	CActiveScheduler::Add(this);
	}

void CSmsSend::DoRunL()
	{
	switch (iState)
		{
		case ESmsSendOpeningEntry:
			{
			SendNextRecipientL();
			break;
			}
		case ESmsSendSendingEntry:
			{
			++iProgress.iRcpDone;
			UpdateAfterRecipientSentL(iProgress.iError);
			break;
			}
		case ESmsSendUpdatingRecipient:
			{
			SendNextRecipientL();
			break;
			}
		case ESmsSendWaiting:
		default:
			Panic(KSmssPanicUnexpectedState);
		};
	}

void CSmsSend::OpenEntryL(const TMsvId aMsvId)
	{
	iState = ESmsSendOpeningEntry;
	User::LeaveIfError(iServerEntry.SetEntry(aMsvId));
	iEntry = iServerEntry.Entry();
	CMsvStore* msvstore = iServerEntry.ReadStoreL();
	CleanupStack::PushL(msvstore);
	iSmsHeader.RestoreL(*msvstore);
	iRichText.Reset();
	msvstore->RestoreBodyTextL(iRichText);

	CleanupStack::PopAndDestroy(msvstore);

	iProgress.iRcpDone	= 0;
	iProgress.iRcpCount	= iSmsHeader.Recipients().Count();
	
	// Check that there is at least one recipient for this message
	if( iProgress.iRcpCount == 0 )
		User::Leave(KErrNotFound);

	iSentRecipients = 0;
	iLastSentIndex	= 0;
	
	//Check if Last Segment Staus has been requested . If so , make iDeliveryReports as ETrue 
	//so that acknowledgement status is EPendingAck , since the last segment acknowledgement is pending .
	CSmsSettings* smsSet = CSmsSettings::NewL();
	CleanupStack::PushL(smsSet);

	iSmsHeader.GetSmsSettingsL(*smsSet);
	if(smsSet->LastSegmentDeliveryReport())
		{
		iDeliveryReports = ETrue;
		}
	else
		{
		iDeliveryReports = iSmsHeader.Submit().StatusReportRequest();
		}
	CleanupStack::PopAndDestroy(smsSet);

	iEntry.SetConnected(ETrue);
	iEntry.SetFailed(EFalse);
	iEntry.SetSendingState(KMsvSendStateSending);
	iEntry.iError = KErrNone;

	ChangeEntryL(iEntry);
	}

void CSmsSend::SendNextRecipientL()
	{
	iState = ESmsSendSendingEntry;

	if (iProgress.iRcpDone < iProgress.iRcpCount)
		{
		CSmsNumber& rcpt = *(iSmsHeader.Recipients()[iProgress.iRcpDone]);
		iSmsHeader.Message().SetToFromAddressL(rcpt.Address());

		//Do not send to the recipient if they failed or was successfully sent previously.
		//Whether to send to a recipient is dependent on CSmsScheduledEntry::CheckRecipient().
		if (CanSendMessageToRecipient(iEntry, rcpt))
			{
			//Move off the current entry so it isn't locked while sending
			iServerEntry.SetEntry(KMsvNullIndexEntryId); //ignore any error

			//Send the message to the recipient
			iRecipientSend->Start(iStatus, iEntry, iSmsHeader, rcpt);
			SetActive();
			}
		else
			{
#ifndef _MSG_NO_LOGGING
			TPtrC addr(rcpt.Address());
			SMSSLOG(FLogFormat(_L("Cannot send %d to %S (state %d, status %d)"), iEntry.Id(), &addr, iEntry.SendingState(), rcpt.Status()));
#endif
			iProgress.iRcpDone++;
			SendNextRecipientL();
			}
		}
	else
		{
		// Sending Complete
		UpdateEntryAfterAllSentL();
		}
	}

void CSmsSend::UpdateAfterRecipientSentL(const TInt aError)
	{
	iState = ESmsSendUpdatingRecipient;

	User::LeaveIfError(iServerEntry.SetEntry(iEntry.Id()));
	iEntry = iServerEntry.Entry();

	CSmsNumber* rcpt = iSmsHeader.Recipients()[iProgress.iRcpDone - 1];
	
	//In a list of recipients if message send to any one of the recipient has failed 
	//means the message should be in outbox (user can edit the message and send) and also 
	//UI will be intimated with proper status. iProgress.iError will contain only the last
	// recipient's status after sending all recipients. So save the all recipient's failed 
	//status (if any) and update the same to iProgress.iError after sending a message to all recipients.
	if(aError != KErrNone && (iProgress.iRcpCount > 1))
		{
		iRecipientFailedStatus = aError;
		}

	rcpt->Time().UniversalTime();
	rcpt->SetError(aError);
	CMsvRecipient::TRecipientStatus rcptStatus = CMsvRecipient::ESentSuccessfully;

	if( aError != KErrNone )
		{
		if( aError == KErrCancel )
			{
			rcptStatus = CMsvRecipient::ENotYetSent;
			}
		else
			{
			rcptStatus = CMsvRecipient::EFailedToSend;
			}

		iEntry.iError = aError;
		iEntry.SetFailed(ETrue);
		}
	else if( iDeliveryReports )
		{
		// Set the deliver info in the recipiant to pending ack.
		rcpt->SetAckStatus(ESmsAckTypeDelivery, CSmsNumber::EPendingAck);
		
		// Update flag to indicate that a recipient has successfully sent to 
		// and record the index of the last recipient to be sent.
		++iSentRecipients;
		iLastSentIndex = iProgress.iRcpDone - 1;
		}

	rcpt->SetStatus(rcptStatus);
	StoreHeaderL(iSmsHeader);

	RequestComplete(&iStatus, KErrNone, ETrue);
	}

void CSmsSend::UpdateEntryAfterAllSentL()
	{
	// Assumes iServerEntry is already set to iEntry.Id()

	iEntry.SetConnected(EFalse);
	
	//if a message to last recipient is sent successfully and failed to send to some of the other recipients in 
	//a list of recipients then update the iProgress.iError with iRecipientFailedStatus
	if( iProgress.iError == KErrNone && iRecipientFailedStatus != KErrNone )
		{
		iProgress.iError = iRecipientFailedStatus;
		}
	iRecipientFailedStatus = KErrNone;

	if( iEntry.SendingState() != KMsvSendStateSuspended )
		{
		TInt newSendingState = KMsvSendStateSent;
		TBool failed = iEntry.Failed() || iEntry.iError;

		if( failed )
			{
			if( iEntry.iError == KErrCancel )
				{
				newSendingState = KMsvSendStateSuspended;
				}
			else
				{
				newSendingState = KMsvSendStateFailed;
				}
			}
		if( iDeliveryReports && iSentRecipients > 0 )
			{
			// At least one recipient has been sent and delivery reports have
			// been requested - update the entry summary to reflect this.
			TMsvSmsEntry& entry = static_cast<TMsvSmsEntry&>(iEntry);

			// Set the message ID field only if this is not a bio-message. Need 
			// to do this before updating the summary as the current summary 
			// will determine if the message ID can be set.
			if( entry.iBioType == 0 )
				{
				TBool validMessageId = EFalse;
				TLogId logId = 0;
				
				// NOTE - need to ensure correct index information in the cases 
				// when a message with multiple recipients only sends to one 
				// recipient the first time and then on the re-schedule sends to
				// one or more remaining recipients.
				if( (iSentRecipients == 1) && (entry.AckSummary(ESmsAckTypeDelivery) != TMsvSmsEntry::EPendingAcks) )
					{
					if(iProgress.iRcpCount <= 0)
						{
						newSendingState = KMsvSendStateFailed;
						failed =ETrue;
						}
					else
						{
						// There is now only a single recpient that is pending delivery
						// reports - store the log ID in the index.
						logId = iSmsHeader.Recipients()[iLastSentIndex]->LogId();
						validMessageId = ETrue;	
						}
					}
				entry.SetMessageId(logId, validMessageId);
				}
			// It is now safe to update the summary.
			entry.SetAckSummary(ESmsAckTypeDelivery, TMsvSmsEntry::EPendingAcks);
			}
		iEntry.SetFailed(failed);
		
		if( newSendingState != KMsvSendStateFailed )
			iEntry.SetSendingState(newSendingState);
		}

	ChangeEntryL(iEntry);
	}

void CSmsSend::DoComplete(TInt& aStatus)
	{
	//If there is an error, then update the entry accordingly.
	if (aStatus != KErrNone)
		{
   		__ASSERT_DEBUG(iEntry.Connected() || iState == ESmsSendOpeningEntry, Panic(KSmssPanicUnexpectedErrorCode));

		TInt err = iServerEntry.SetEntry(iEntry.Id());

		if (!err)
			{
			iEntry = iServerEntry.Entry();
			iEntry.iError = aStatus;
			TRAP(err, UpdateEntryAfterAllSentL()); //ignore error
			}

		iProgress.iError = aStatus;
		aStatus = KErrNone;
		}
	
	iState = ESmsSendWaiting;
	}
