// 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:
//

/**
 @file
 @internalComponent
*/

#include <comms-infras/ss_log.h>
#include <comms-infras/ss_corepractivities.h> // TODO is this needed?
#include <comms-infras/coretiermanagerstates.h> // TODO is this needed?
#include <comms-infras/coretiermanageractivities.h> // TODO is this needed?
#include "pdpservices.h"
#include "pdptiermanager.h"
#include <pcktcs.h>
#include <comms-infras/es_connectionserv.h>

#ifdef __CFLOG_ACTIVE
#define KPDPTierMgrTag KESockMetaConnectionTag
_LIT8(KMBMSObjectTag, "MBMSObject");
#endif

using namespace Messages;
using namespace MeshMachine; // TODO is this needed?
using namespace ESock;
using namespace NetStateMachine; // TODO is this needed?
using namespace ConnectionServ;


//Constructor for CMBMSServiceRequest
CMBMSServiceRequest::CMBMSServiceRequest(
		CMBMSEngine& aMBMSEngine,
		const TNodeId& aMBMSTMCommsId,
		const Messages::TNodeSignal::TMessageId& aRequestType,
		CRefCountOwnedParameterBundle* aRequestBundleOwner,
		const XMBMSServiceQuerySet::TQueryType aQueryType
		)
	:CActive(EPriorityStandard),
	iMBMSEngine(aMBMSEngine),
	iMBMSTMCommsId(aMBMSTMCommsId),
	iRequestType(aRequestType),
	iObjectBundleOwner(aRequestBundleOwner),
	iQueryType(aQueryType)
	{
	CActiveScheduler::Add(this);
	}

/**
The NewL factory function for CMBMSServiceRequest.
@param aMBMSTMCommsId comms Id.
@param aClientId Node Channel Id.
@param aRequestType Message Id
@param aRequestBundleOwner Parameter Bundle.
@return CMBMSServiceRequest*
*/
CMBMSServiceRequest* CMBMSServiceRequest::NewL(
		CMBMSEngine& aMBMSEngine,
		const TNodeId& aMBMSTMCommsId,
		RNodeInterface* aOriginatorInterface,
		const TRuntimeCtxId& aOriginator,
		const Messages::TNodeSignal::TMessageId& aRequestType,
		CRefCountOwnedParameterBundle* aRequestBundleOwner,
		const XMBMSServiceQuerySet::TQueryType aQueryType
		)
	{
	CMBMSServiceRequest* self = new(ELeave) CMBMSServiceRequest(
													aMBMSEngine,
													aMBMSTMCommsId,
													aRequestType,
													aRequestBundleOwner,
													aQueryType
													);

	CleanupStack::PushL(self);
	self->ConstructL(aOriginatorInterface, aOriginator);
	CleanupStack::Pop(self);

	return self;
	}
/**
ConstructL for CMBMSEngine.
*/
void CMBMSServiceRequest::ConstructL(RNodeInterface* aOriginatorInterface, const TRuntimeCtxId& aOriginator)
	{

	if (iRequestOriginator.Open(*aOriginatorInterface, aOriginator) != KErrNone)
		{
		// Fix the code - looks like this originator already has an outstanding request
		User::Leave(KErrArgument);
		}
	}

/**
Destructor for CMBMSServiceRequest.
*/
CMBMSServiceRequest::~CMBMSServiceRequest()
	{
	Cancel();
	iObjectBundleOwner->Close();
	delete iRetrievePcktMbms;
	}

/**
This function is used to cancel the notification message sent by the client
@param aError const TInt
@return void
*/
void CMBMSServiceRequest::CancelMessage(const TInt aError)
	{
	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::CancelMessage(%d)"), this, aError));
	if (iRequestType == TCFTierStatusProvider::TTierNotificationRegistration::Id())
		{
		iRequestOriginator.PostMessage(
				iMBMSTMCommsId,
				TEBase::TError(TCFTierStatusProvider::TTierNotificationRegistration::Id(), aError).CRef()
				);
		}
	}

/**
This function is used to process the MBMS requests.The RunL() function of CMBMSServiceRequest is called
based on the state machines.
@param aError const TInt
@return void
*/
void CMBMSServiceRequest::StartRequest()
	{
	iScanEngineState = EChecking;

	SetActive();
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
	}

/**
This function is used to return the client id.
@param None
@return TCFNodeChannelId
*/
const TNodeId& CMBMSServiceRequest::GetClientId()
	{
	return iRequestOriginator.Node().RecipientId();
	}

/**
This is CActive overloaded function.Cancel all outstanding requests.
@param None
@return Void
*/
void CMBMSServiceRequest::DoCancel()
	{
     switch (iScanEngineState)
		{
		case ENotifyService:
		case ERemoveAllComplete:
			iMBMSEngine.GetRPacketService().CancelAsyncRequest(EPacketUpdateMbmsMonitorServiceList);
		break;
		case ERetrieveBearerAvailability:
			iMBMSEngine.GetRPacketService().CancelAsyncRequest(EPacketGetMbmsNetworkServiceStatus);
		break;
		case EStartMonitor:
			iMBMSEngine.GetRPacketService().CancelAsyncRequest(EPacketNotifyMbmsServiceAvailabilityChange);
		break;
		case EResultCount:
			if(iQueryType == XMBMSServiceQuerySet::ECountMonitorList)
			   {
			   iMBMSEngine.GetRPacketService().CancelAsyncRequest(EPacketEnumerateMbmsMonitorServiceList);
			   }
			else if(iQueryType == XMBMSServiceQuerySet::ECountActiveServiceList)
			   {
			   iMBMSEngine.GetRPacketService().CancelAsyncRequest(EPacketEnumerateMbmsActiveServiceList);
			   }
		break;
		case EGetMonitorList:
			if(iRetrievePcktMbms != NULL)
			iRetrievePcktMbms->Cancel();
		break;
		}
	}

/**
This is CActive overloaded function.
@param None
@return TInt
*/
TInt CMBMSServiceRequest::RunError(TInt aError)
	{
	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::RunError(%d)"), this, aError));

	// Stop the ScanEngine components
	Cancel();

	if (iRequestType == TCFTierStatusProvider::TTierNotificationRegistration::Id())
		{
		iRequestOriginator.PostMessage(
						iMBMSTMCommsId,
						TEBase::TError(TCFTierStatusProvider::TTierNotificationRegistration::Id(), aError).CRef()
						);
		}
	return KErrNone;
	}

/**
This is CActive overloaded function.
@param None
@return Void
*/
void CMBMSServiceRequest::RunL()
	{
    TRequestStatus* status = &iStatus;
	if (iStatus == KErrNone)
		{
		switch(iScanEngineState)
			{
			case EChecking:
				__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In EChecking State"), this));
				User::LeaveIfError(iMBMSEngine.GetRPacketService().GetStatus(iPsdStatus)); // get initial status

				//check the query type
				if(iQueryType == XMBMSServiceQuerySet::EBearerAvailability)
					iScanEngineState = EBearerAvailable;
				else if (iQueryType == XMBMSServiceQuerySet::EAddService)
					iScanEngineState = EServiceAdd;
				else if (iQueryType == XMBMSServiceQuerySet::ERemoveService)
					iScanEngineState = EServiceRemove;
				else if (iQueryType == XMBMSServiceQuerySet::ERemoveAllService)
					iScanEngineState = EServiceRemoveAll;
				else if (iQueryType == XMBMSServiceQuerySet::ECountMonitorList)
					iScanEngineState = EGetCountMonitorList;
				else if (iQueryType == XMBMSServiceQuerySet::ECountActiveServiceList)
					iScanEngineState = EGetCountServiceList;

				SetActive();
				User::RequestComplete(status, KErrNone);
			break;

			case EBearerAvailable:
				//get MBMS bearer availability.
				 __CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In EBearerAvailable State"), this));
				 iMBMSEngine.GetRPacketService().GetMbmsNetworkServiceStatus(iStatus,ETrue,iNetworkServiceStatus);
			 	 iScanEngineState = ERetrieveBearerAvailability;
				 SetActive();
			break;

		   	case ERetrieveBearerAvailability:
				__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In ERetrieveBearerAvailability State"), this));
			    //create a parameter bundle and send the results to the client.
			    SendResultBundleL();

			    iScanEngineState = EBearerAvailable;
			    SetActive();
			    User::RequestComplete(status, KErrNone);
			break;

			case EServiceAdd:
				{
				CPcktMbmsMonitoredServiceList* mbmsServiceList = UpdateMonitorServiceListL();
			    CleanupStack::PushL(mbmsServiceList);
			    iMBMSEngine.GetRPacketService().UpdateMbmsMonitorServiceListL(iStatus,EAddEntries,mbmsServiceList);
			    CleanupStack::PopAndDestroy(mbmsServiceList);

				SetActive();
				iScanEngineState = ENotifyService;
				}
			break;

			case EServiceRemoveAll:
			    {
			    iMBMSEngine.GetRPacketService().UpdateMbmsMonitorServiceListL (iStatus,ERemoveAllEntries);

				SetActive();
				iScanEngineState = ERemoveAllComplete;
			    }
			break;

			case ERemoveAllComplete:
				SendResultBundleL();
			break;

			case EServiceRemove:
				{
				CPcktMbmsMonitoredServiceList* mbmsSeriveList = UpdateMonitorServiceListL();
			    CleanupStack::PushL(mbmsSeriveList);
			    iMBMSEngine.GetRPacketService().UpdateMbmsMonitorServiceListL (iStatus,ERemoveEntries,mbmsSeriveList);
			    CleanupStack::PopAndDestroy(mbmsSeriveList);

				SetActive();
				iScanEngineState = ENotifyService;
				}
			break;

			case EGetCountMonitorList:
				{
				__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In ECountActiveServiceList State"), this));
				iMBMSEngine.GetRPacketService().EnumerateMbmsMonitorServiceList(iStatus,iCount,iMaxCount);

				SetActive();
				iScanEngineState = EResultCount;
				}
			break;

			case EGetCountServiceList:
				{
				__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In ECountActiveServiceList State"), this));
				iMBMSEngine.GetRPacketService().EnumerateMbmsActiveServiceList(iStatus,iCount,iMaxCount);

				SetActive();
				iScanEngineState = EResultCount;
				}
			break;

			case EResultCount:
				SendResultBundleL();
			break;

			case ENotifyService:
				//notify for MBMS Services.
				__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In ENotifyService State"), this));
				iMBMSEngine.GetRPacketService().NotifyMbmsServiceAvailabilityChange(iStatus);
			    SetActive();
				iScanEngineState = EStartMonitor;
			break;

			case EStartMonitor:
				__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In EStartMonitor State"), this));
				iRetrievePcktMbms = CRetrievePcktMbmsMonitoredServices::NewL(iMBMSEngine.GetRPacketService());
			    iRetrievePcktMbms->Start(iStatus);

			    SetActive();
				iScanEngineState = EGetMonitorList;
			break;

			case EGetMonitorList:
				SendResultBundleL();

			    SetActive();
			    User::RequestComplete(status, KErrNone);
			    iScanEngineState = ENotifyService;
			break;

		  }
		}
	else if(((iStatus.Int() == KErrMbmsImpreciseServiceEntries))||((iStatus.Int() == KErrNotFound)))
		 {
		  __CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::RunL() KErrMbmsImpreciseServiceEntries || KErrNotFound"), this));
		  CancelMessage(iStatus.Int());
		  iMBMSEngine.RemoveFromRequestListL(GetClientId());
		 }
	else
		{
		 __CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::RunL() ERROR: Incorrect status, Aborting"), this));
		 User::Leave(iStatus.Int());
		}

	}

/**
This function is used to update Mbms Monitored Service List
@param None
@return Void
*/
CPcktMbmsMonitoredServiceList* CMBMSServiceRequest::UpdateMonitorServiceListL()
	{
	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In UpdateMonitorServiceListL()"), this));

	//Read the entries in the parameter bundle and pass them to Etel.
	CPcktMbmsMonitoredServiceList* serviceList= CPcktMbmsMonitoredServiceList::NewL();
	CleanupStack::PushL(serviceList);

	if((iScanEngineState == EServiceAdd) || (iScanEngineState == EServiceRemove))
	   {
		const ConnectionServ::CConnectionServParameterBundle* objectBundle1 = static_cast<const CConnectionServParameterBundle*>(iObjectBundleOwner->Ptr());
	    ConnectionServ::CConnectionServParameterBundle* objectBundle = const_cast<CConnectionServParameterBundle *>(objectBundle1);

	    TUint numObjectPSCs = objectBundle->CountParamSetContainers();

		for(TUint i = 0; i < numObjectPSCs; i++)
			{
			CParameterSetContainer* objectPSC = objectBundle->GetParamSetContainer(i);
			XMBMSServiceParameterSet* objectMPS = XMBMSServiceParameterSet::FindInParamSetContainer(*objectPSC);
			RPacketService::TMbmsServiceAvailabilityV1 mbmsService;
			mbmsService.iTmgi.SetServiceId(objectMPS->GetChannelInfo()->GetTmgi().GetServiceId());
			mbmsService.iTmgi.SetMCC(objectMPS->GetChannelInfo()->GetTmgi().GetMCC());

			mbmsService.iTmgi.SetMNC(objectMPS->GetChannelInfo()->GetTmgi().GetMNC());
			mbmsService.iMbmsServiceMode = objectMPS->GetServiceMode();
			mbmsService.iMbmsAccessBearer = objectMPS->GetChannelInfo()->GetScope();
			serviceList->AddEntryL(mbmsService);
			}
	   }
	CleanupStack::Pop(serviceList);
	return serviceList;
	}

/**
This function is used to send result parameter bundle to the client
@param None
@return Void
*/
void CMBMSServiceRequest::SendResultBundleL()
	{
	CConnectionServParameterBundle* returnBundle = NULL;
	if(iScanEngineState == EGetMonitorList)
	   returnBundle = PrepareMonitorResultBundleL();
	else if(iScanEngineState == ERetrieveBearerAvailability)
	   returnBundle = PrepareBearerResultBundleL();
	else if (iScanEngineState == ERemoveAllComplete)
	   returnBundle = PrepareRemoveAllBundleL();
	else if (iScanEngineState == EResultCount)
	   returnBundle = PrepareCountBundleL();

	CleanupStack::PushL(returnBundle);// Ownership of the bundle passes to the Server
	CRefCountOwnedParameterBundle* returnBundleOwner = new (ELeave) CRefCountOwnedParameterBundle(returnBundle);
	returnBundleOwner->Open(); // Open the initial reference
	CleanupClosePushL(*returnBundleOwner);

	if (iRequestType == TCFTierStatusProvider::TTierNotificationRegistration::Id())
		{
		iRequestOriginator.PostMessage(
						iMBMSTMCommsId,
						TCFTierStatusProvider::TTierNotification(returnBundleOwner).CRef()
						);
		}
	CleanupStack::Pop(returnBundleOwner);
	CleanupStack::Pop(returnBundle);
	}

/**
This function is used to prepare result bundle.
@param None
@return CConnectionServParameterBundle*
*/
CConnectionServParameterBundle* CMBMSServiceRequest::PrepareMonitorResultBundleL() const
	{
	CConnectionServParameterBundle* returnBundle = CConnectionServParameterBundle::NewL();
	CleanupStack::PushL(returnBundle);

	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In PrepareMonitorResultBundleL function"), this));
	//read the MBMS Service list from Etel after receiving the MBMS Service Availability Notification.

	CPcktMbmsMonitoredServiceList* serviceList = iRetrievePcktMbms->RetrieveListL();
	TInt count = serviceList->Enumerate();
	CleanupStack::PushL(serviceList);

	//Read the monitor list from  Etel and send the list to client as parameter bundle.
    for(TUint i = 0; i < count; i++)
		{
		RPacketService::TMbmsServiceAvailabilityV1 serviceAvailability;
		CParameterSetContainer* returnPSC = CParameterSetContainer::NewL(*returnBundle);
		CleanupStack::PushL(returnPSC);
		serviceAvailability = serviceList->GetEntryL(i);

		TTmgi tmgi;
		tmgi.SetMCC(serviceAvailability.iTmgi.GetMCC());
		tmgi.SetMNC(serviceAvailability.iTmgi.GetMNC());
		tmgi.SetServiceId(serviceAvailability.iTmgi.GetServiceId());
												 			
		XMBMSServiceParameterSet* returnPS = XMBMSServiceParameterSet::NewL(*returnPSC);
		TMBMSChannelInfoV1* serviceInfo = returnPS->GetChannelInfo();

		serviceInfo->SetTmgi(tmgi);
		serviceInfo->SetScope(serviceAvailability.iMbmsAccessBearer);
		
	    returnPS->SetServiceMode(serviceAvailability.iMbmsServiceMode);
	    returnPS->SetMBMSServiceAvailability(serviceAvailability.iMbmsAvailabilityStatus);

	    CleanupStack::Pop(returnPSC);
	    }

	CleanupStack::PopAndDestroy(serviceList);
	CleanupStack::Pop(returnBundle);

	return returnBundle;
	}

/**
This function is used to prepare result bundle containing bearer availability.
@param None
@return CConnectionServParameterBundle*
*/
CConnectionServParameterBundle* CMBMSServiceRequest::PrepareBearerResultBundleL() const
	{
	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In PrepareBearerResultBundleL function"), this));

	CConnectionServParameterBundle* returnBundle = CConnectionServParameterBundle::NewL();
	CleanupStack::PushL(returnBundle);

	CParameterSetContainer* returnPSC = CParameterSetContainer::NewL(*returnBundle);
	CleanupStack::PushL(returnPSC);

	XMBMSServiceQuerySet* returnQuery = XMBMSServiceQuerySet::NewL(*returnPSC);
	returnQuery->SetMBMSBearerAvailability(iNetworkServiceStatus);

	CleanupStack::Pop(returnPSC);
	CleanupStack::Pop(returnBundle);

	return returnBundle;
	}

/**
This function is used to prepare result bundle containing current and max count of entries
in Monitor/Serice list table.
@param None
@return CConnectionServParameterBundle*
*/
CConnectionServParameterBundle* CMBMSServiceRequest::PrepareCountBundleL() const
	{
	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In PrepareCountBundleL function"), this));

	CConnectionServParameterBundle* returnBundle = CConnectionServParameterBundle::NewL();
	CleanupStack::PushL(returnBundle);

	CParameterSetContainer* returnPSC = CParameterSetContainer::NewL(*returnBundle);
	CleanupStack::PushL(returnPSC);

	XMBMSServiceQuerySet* returnQuery = XMBMSServiceQuerySet::NewL(*returnPSC);
	returnQuery->SetListCount(iCount);
	returnQuery->SetListMaxCount(iMaxCount);

	CleanupStack::Pop(returnPSC);
	CleanupStack::Pop(returnBundle);

	return returnBundle;
	}

/**
This function creates an empty bundle.
@param None
@return CConnectionServParameterBundle*
*/
CConnectionServParameterBundle* CMBMSServiceRequest::PrepareRemoveAllBundleL() const
	{
	//pass empty bundle
	__CFLOG_VAR((KPDPTierMgrTag, KMBMSObjectTag, _L8("CMBMSServiceRequest[%08x]::In PrepareRemoveAllBundleL function"), this));

	CConnectionServParameterBundle* returnBundle = CConnectionServParameterBundle::NewL();
	return returnBundle;
	}
