// 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:
// PDP Connection Provider implementation
// 
//

/**
 @file
 @internalComponent
*/

#include "PDPCPR.h"
#include <comms-infras/ss_log.h>
#include <comms-infras/ss_msgintercept.h>
#include <comms-infras/corecpractivities.h>
#include <elements/nm_address.h>
#include <comms-infras/ss_nodemessages_parameters.h>
#include <comms-infras/corescprstates.h>
#include <elements/mm_context.h>
#include <etelmm.h>
#include <etelpckt.h>
#include <comms-infras/ss_metaconnprov.h>
#include "pdpprovision.h"
#include <etel.h>
#include <networking/cfbearers.h>
#include "pdpcpravailabilitylistener.h"
#include <comms-infras/ss_nodemessages.h>
#include <networking/ipcpr_states.h>
#include <comms-infras/linkmessages.h>
#include <elements/nm_interfaces.h>
#include <cs_genevent.h>
#include <networking/etelbearers.h>

using namespace ESock;


//-=========================================================
//
// CPDPConnectionProvider methods
//
//-=========================================================	

//We reserve space for two preallocated activities that may start concurrently on the CPR
//node: destroy and data client stop.
static const TUint KDefaultMaxPreallocatedActivityCount = 2;
static const TUint KMaxPreallocatedActivitySize = sizeof(MeshMachine::CNodeRetryParallelActivity) + sizeof(MeshMachine::APreallocatedOriginators<4>);
static const TUint KPDPCPRPreallocatedActivityBufferSize = KDefaultMaxPreallocatedActivityCount * KMaxPreallocatedActivitySize;

namespace PDPCprLinkCharacteristicActivity
{
DECLARE_DEFINE_NODEACTIVITY(ECFActivityParamRequest, PDPCprLinkCharacteristic, TCFScpr::TGetParamsRequest)
    NODEACTIVITY_ENTRY(MeshMachine::KNoTag, PDPCprStates::TUpdateBundleAndRespondWithRetrievedParams, PRStates::TAwaitingParamRequest, MeshMachine::TNoTag)
NODEACTIVITY_END()
}

// Agent SCPR Going Down Activity
namespace PDPDataClientGoneDownActivity
{
DECLARE_DEFINE_NODEACTIVITY(ECFActivityDataClientGoneDown, PDPScprGoneDown, TCFControlProvider::TDataClientGoneDown)
    // AwaitingDataClientGoneDown used rather than AlwaysAccept to mark data client EStopped
    FIRST_NODEACTIVITY_ENTRY(CoreNetStates::TAwaitingDataClientGoneDown, CoreNetStates::TNoTagOrNonDefault)
    LAST_NODEACTIVITY_ENTRY(MeshMachine::KNoTag, PRStates::TSendGoneDown)
    LAST_NODEACTIVITY_ENTRY(CoreNetStates::KNonDefault, MeshMachine::TDoNothing)    
NODEACTIVITY_END()
}

namespace PDPCprActivities
{
DEFINE_ACTIVITY_MAP(activityMap)
    ACTIVITY_MAP_ENTRY(PDPDataClientGoneDownActivity, PDPScprGoneDown)
	ACTIVITY_MAP_ENTRY(PDPCprLinkCharacteristicActivity, PDPCprLinkCharacteristic)
ACTIVITY_MAP_END_BASE(CprActivities, coreCprActivities)
}


CPDPConnectionProvider* CPDPConnectionProvider::NewL(ESock::CConnectionProviderFactoryBase& aFactory)
    {
    CPDPConnectionProvider* provider = new (ELeave) CPDPConnectionProvider(aFactory);
    CleanupStack::PushL(provider);
    provider->ConstructL();
    CleanupStack::Pop(provider);
	return provider;
    }        


void CPDPConnectionProvider::StartListener()
/**
 * Start listening for dynamic caps or network mode changes.
 * @param None
 * @return void
 */
	{
	iDynamicCapsEventListener->NotifyDynamicCapsChange(this);
	iNetworkModeEventListener->NotifyNetworkModeChange(this);
	}

void CPDPConnectionProvider::ConstructL()
/**
 * PDP Connection Provider Second Phase Constructor
 * @param None
 * @return void
 */
	{
	CCoreConnectionProvider::ConstructL(KPDPCPRPreallocatedActivityBufferSize);
	}

void CPDPConnectionProvider::StopListener()
/**
 * Start listening for dynamic caps or network mode changes.
 */
	{
	if(iDynamicCapsEventListener)
		{
		iDynamicCapsEventListener->Cancel();
		}
	if(iNetworkModeEventListener)
		{
		iNetworkModeEventListener->Cancel();
		}
	}

CPDPConnectionProvider::CPDPConnectionProvider(ESock::CConnectionProviderFactoryBase& aFactory)
	: CCoreConnectionProvider(aFactory, PDPCprActivities::activityMap::Self())
/**
 * Construct PDP connection provider.
 */	
    {        
    LOG_NODE_CREATE(KESockConnectionTag, CPDPConnectionProvider);
    }

CPDPConnectionProvider::~CPDPConnectionProvider()
/**
 * Destroy PDP connection provider.
 */
    {    
    LOG_NODE_DESTROY(KESockConnectionTag, CPDPConnectionProvider);
    StopListener();
	delete iDynamicCapsEventListener;
	delete iNetworkModeEventListener;
    }
    
void CPDPConnectionProvider::ReceivedL(const Messages::TRuntimeCtxId& aSender, const Messages::TNodeId& aRecipient, Messages::TSignatureBase& aMessage)
	{
	//ESOCK_DEBUG_MESSAGE_INTERCEPT(aSender, aMessage, aRecipient);
	MeshMachine::TNodeContext<CPDPConnectionProvider> ctx(*this, aMessage, aSender, aRecipient);
	CCoreConnectionProvider::Received(ctx);
	User::LeaveIfError(ctx.iReturn);
	}
	

void CPDPConnectionProvider::BearerChangeDetectedL()
/**
 * Update the parmeter set in case of modulation change and send TPlaneNotification message to
 * all the control client.
 */
	{
	//Update bearers.
	UpdateBearer();
	
	//Send msg to all control clients
	CEventParamsChanged* eventChanged = CEventParamsChanged::NewL(KBearerInfo);
	
	CleanupStack::PushL(eventChanged);
	//Create the family and set it to bearer type 
	XBearerInfo* bearerInfo = XBearerInfo::NewL();
	CleanupStack::PushL(bearerInfo);
	
	bearerInfo->SetBearerType(iBearerType);
	
	//Add bearer family to parameter set.
	eventChanged->AddParameterSetL(KBearerInfo, bearerInfo);
	CleanupStack::Pop(2,eventChanged);
	
	//Create the event node for bearer change notification.
	CRefCountOwnedNotification* eventNode = new (ELeave) CRefCountOwnedNotification(eventChanged);
	
	//Build the CPR plane notification message, which will be retrieved by IPCPR.
	TCFSubConnControlClient::TPlaneNotification msg(eventNode);
	
   	Messages::TClientIter<Messages::TDefaultClientMatchPolicy> iter = this->GetClientIter<Messages::TDefaultClientMatchPolicy>(Messages::TClientType(TCFClientType::ECtrl));
    Messages::RNodeInterface* ctl;
	while ((ctl = iter++) != NULL)
		{
		msg.iRefCountOwnedNotification->Open();
		ctl->PostMessage(this->NodeId(), msg);
		}

	}


void CPDPConnectionProvider::UpdateBearer()
/**
 * Update bearer type based on the change of the dynamic caps or network mode.
 */
	{
	//Get dynamic caps
	TUint dynamicCaps = iDynamicCapsEventListener->DynamicCaps();		
	//Get network mode
	RMobilePhone::TMobilePhoneNetworkMode networkMode = iNetworkModeEventListener->NetworkMode();
	
	//Resolve the bearer based on both dynamic caps and network mode
	iBearerType = Bearer(dynamicCaps, networkMode);
	iBearerSet = ETrue;
	}


TUint32 CPDPConnectionProvider::Bearer(TUint aDynamicCaps, RMobilePhone::TMobilePhoneNetworkMode& aNetworkMode)
/**
 * Determine bearer based on dynamic caps and network mode.
 * @param aDynamicCaps a value of dynamic caps
 * @param aNetworkMode a value of network mode
 * @return Type of Bearer
 *
 */
	{
	
	if ((aDynamicCaps & RPacketService::KCapsHSUPA) || (aDynamicCaps & RPacketService::KCapsHSDPA))
		{
		return KHsdpaBearer;
		}
	else if (aDynamicCaps & RPacketService::KCapsEGPRS)
		{
		return KEGprsBearer;
		}
	else
		{
		if(aNetworkMode == RMobilePhone::ENetworkModeGsm)
			{
		    return KGprsBearer;
			}
		else if(aNetworkMode == RMobilePhone::ENetworkModeWcdma)
			{
		    return KUmtsBearer;
			}
		else
			{
		   	return KDefaultBearer;
			}
		}
	}


DEFINE_SMELEMENT(PDPCprStates::TUpdateBundle, NetStateMachine::MStateTransition, PDPCprStates::TContext)
void PDPCprStates::TUpdateBundle::DoL()
/**
 * Process TGetParamsRequest
 */
	{
	// Node receives TGetParamsRequest msg containing the bundle.
	// Determine current network type - GPRS/EDGE/UMTS/HSDPA and update the bundle.
	const CTSYProvision& tsyProvision = static_cast<const CTSYProvision&>(iContext.Node().AccessPointConfig().FindExtensionL(
	        STypeId::CreateSTypeId(CTSYProvision::EUid, CTSYProvision::ETypeId)));
	if(!iContext.Node().iDynamicCapsEventListener)
		{
		iContext.Node().iDynamicCapsEventListener = CPDPCPRDynamicCapsListener::NewL(&tsyProvision);
		}
	if (!iContext.Node().iNetworkModeEventListener)
		{
		iContext.Node().iNetworkModeEventListener = CPDPCPRNetworkModeListener::NewL(&tsyProvision);
		}

	if(iContext.Node().iBearerSet == EFalse)
		{
		iContext.Node().UpdateBearer();
		}
	
	TCFScpr::TGetParamsRequest& paramRequest = Messages::message_cast<TCFScpr::TGetParamsRequest>(iContext.iMessage);
	if( (! paramRequest.iFamilyBundle.IsNull()) && (iContext.Node().GetParameterBundle() != paramRequest.iFamilyBundle))
		{
		iContext.Node().GetParameterBundle().Open(paramRequest.iFamilyBundle);
		RParameterFamily family = iContext.Node().GetParameterBundle().FindFamily(KBearerInfo);	
 
		if(!family.IsNull())
			{
			XBearerInfo *bearerType = static_cast<XBearerInfo*>(family.FindParameterSet(STypeId::CreateSTypeId(KIpBearerInfoUid, KIpBearerInfoParameterType), RParameterFamily::ERequested));
		
			if(bearerType)
				{
				bearerType->SetBearerType(iContext.Node().iBearerType);
				}
			}
		}

	if(iContext.Node().iDynamicCapsEventListener->IsDynamicCapsSupported())
		{
		// if Dynamic Caps supported, start listener, which would Notify of Dynamic Caps change
		// no check required for network mode, since atleast one network mode is supported.
		iContext.Node().StartListener();
		}
	}


