// Copyright (c) 2007-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:
// Mobility Connection Provider implementation
// 
//
//

/**
 @file
 @internalComponent
*/

#include <comms-infras/corecprstates.h>
#include <comms-infras/corecpractivities.h>
#include "mobilitycpr.h"
#include <comms-infras/ss_log.h>
#include <comms-infras/api_ext_list.h>

#include <comms-infras/ss_msgintercept.h>


#ifdef _DEBUG
// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module
// (if it could happen through user error then you should give it an explicit, documented, category + code)
_LIT(KSpecAssert_ESockMbCrCPR, "ESockMbCrCPR");
#endif

#ifdef SYMBIAN_TRACE_ENABLE
#define KIPCprTag KESockConnectionTag
// _LIT8(KIPCprSubTag, "mobilitycpr");
#endif // SYMBIAN_TRACE_ENABLE

using namespace ESock;
using namespace NetStateMachine;
using namespace Messages;
using namespace MeshMachine;

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

//-=========================================================
//
// CMobilityConnectionProvider methods
//
//-=========================================================
EXPORT_C CMobilityConnectionProvider* CMobilityConnectionProvider::NewL(CConnectionProviderFactoryBase& aFactory, const MeshMachine::TNodeActivityMap& aActivityMap)
    {
    CMobilityConnectionProvider* provider = new (ELeave) CMobilityConnectionProvider(aFactory, aActivityMap);
    CleanupStack::PushL(provider);
    provider->ConstructL(KMobilityCPRPreallocatedActivityBufferSize);
    CleanupStack::Pop(provider);
	return provider;
    }

EXPORT_C CMobilityConnectionProvider::CMobilityConnectionProvider(CConnectionProviderFactoryBase& aFactory, const MeshMachine::TNodeActivityMap& aActivityMap)
:	CCoreConnectionProvider(aFactory, aActivityMap),
	TIfStaticFetcherNearestInHierarchy(this)
    {
    LOG_NODE_CREATE(KIPCprTag, CMobilityConnectionProvider);
    }

EXPORT_C CMobilityConnectionProvider::~CMobilityConnectionProvider()
    {
    LOG_NODE_DESTROY(KIPCprTag, CMobilityConnectionProvider);
    }

EXPORT_C void CMobilityConnectionProvider::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& aRecipient, TSignatureBase& aMessage)
	{
	ESOCK_DEBUG_MESSAGE_INTERCEPT(aSender, aMessage, aRecipient);
	CCoreConnectionProvider::ReceivedL(aSender, aRecipient, aMessage);
	}

EXPORT_C void CMobilityConnectionProvider::ReturnInterfacePtrL(AMobilityProtocolReq*& aInterface)
	{
	aInterface = this;
	}

EXPORT_C void CMobilityConnectionProvider::OpenExtensionInterface(TSubSessionUniqueId aClientId, const Messages::TRuntimeCtxId& aControlClient, CCommsApiExtResponder* aResponder)
	{
	__ASSERT_DEBUG(aResponder, User::Panic(KSpecAssert_ESockMbCrCPR, 1)); //Responder must be valid here
	__ASSERT_DEBUG(aClientId != 0, User::Panic(KSpecAssert_ESockMbCrCPR, 2)); //Client id must be valid here.

	if (iMobilityActivity)
		{
		//Only one mobility client is supported by this implementation,
		//and this client has already opened the interface.
		CCommsApiExtResponder::Complete(aResponder,KErrInUse);
		return;
		}

	TCFMobilityProvider::TStartMobility msg(aResponder, aClientId);
	__ASSERT_DEBUG_NO_LEAVE(ReceivedL(aControlClient, Id(), msg)); //ReceivedL can never leave on MM nodes

	if (iMobilityActivity==NULL)
		{
		CCommsApiExtResponder::Complete(aResponder,KErrNoMemory);
		}
	}

EXPORT_C void CMobilityConnectionProvider::CloseExtensionInterface(TSubSessionUniqueId aClientId)
	{
	//Ignore all close requests for any other client than ours
	if (iMobilityActivity==NULL || iMobilityActivity->iClientId!=aClientId)
		{
		return;
		}

	//The activity has no originators, cancel it explicitly
	TNodeNullContext ctx(*this,iMobilityActivity);
	iMobilityActivity->Cancel(ctx);
	iMobilityActivity = NULL; //New mobility interface can be opened now
	}

EXPORT_C void CMobilityConnectionProvider::SubscribeForMessage(TSubSessionUniqueId aClientId, CCommsMobilitySrvResp* aRespExt)
	{
	__ASSERT_DEBUG(aRespExt, User::Panic(KSpecAssert_ESockMbCrCPR, 3)); //Responder must be valid here
	//If the interface was opened successfuly, iMobilityActivity is set.
	//It could only be cleared again if:
	//1) The mobility activity generated or not handled received error message.
	//But the activity is supposed to do that.
	//2) The node received and is currently processing TDestroy before this
	//activity was terminated, which can never happen, as the Control Client
	//would cancel it before leaving or it would be aborted on the Control Client
	//leaving (so before sending TDataClientIdle).
	__ASSERT_DEBUG(iMobilityActivity, User::Panic(KSpecAssert_ESockMbCrCPR, 4));
	__ASSERT_DEBUG(iMobilityActivity->iClientId==aClientId, User::Panic(KSpecAssert_ESockMbCrCPR, 5));
	(void)(iMobilityActivity->iClientId==aClientId);   //keep compiler happy
	iMobilityActivity->SetResponder(*aRespExt);
	}

EXPORT_C void CMobilityConnectionProvider::CancelSubscription(TSubSessionUniqueId aClientId)
	{
	//Ignore all cancel requests for any other client than ours
	if (iMobilityActivity==NULL || iMobilityActivity->iClientId!=aClientId)
		{
		return;
		}

	__ASSERT_DEBUG(iMobilityActivity, User::Panic(KSpecAssert_ESockMbCrCPR, 7));
	CCommsApiExtResponder::Complete(iMobilityActivity->iResponder, KErrCancel);
	}

EXPORT_C void CMobilityConnectionProvider::MigrateToPreferredCarrier(TSubSessionUniqueId aClientId)
	{
	//If the interface was opened successfuly, iMobilityActivity is set.
	//It could only be cleared again if:
	//1) The mobility activity generated or not handled received error message.
	//But the activity is supposed to do that.
	//2) The node received and is currently processing TDestroy before this
	//activity was terminated, which can never happen, as the Control Client
	//would cancel it before leaving or it would be aborted on the Control Client
	//leaving (so before sending TDataClientIdle).
	__ASSERT_DEBUG(iMobilityActivity, User::Panic(KSpecAssert_ESockMbCrCPR, 8));
	__ASSERT_DEBUG(iMobilityActivity->iClientId==aClientId, User::Panic(KSpecAssert_ESockMbCrCPR, 9));
	(void)(iMobilityActivity->iClientId==aClientId); //keep compiler happy
	TCFMobilityProvider::TMigrationRequested msg;
	__ASSERT_DEBUG_NO_LEAVE(ReceivedL(NodeId(), Id(), msg)); //ReceivedL can never leave on MM nodes
	}

EXPORT_C void CMobilityConnectionProvider::IgnorePreferredCarrier(TSubSessionUniqueId aClientId)
	{
	//If the interface was opened successfuly, iMobilityActivity is set.
	//It could only be cleared again if:
	//1) The mobility activity generated or not handled received error message.
	//But the activity is supposed to do that.
	//2) The node received and is currently processing TDestroy before this
	//activity was terminated, which can never happen, as the Control Client
	//would cancel it before leaving or it would be aborted on the Control Client
	//leaving (so before sending TDataClientIdle).
	__ASSERT_DEBUG(iMobilityActivity, User::Panic(KSpecAssert_ESockMbCrCPR, 10));
	__ASSERT_DEBUG(iMobilityActivity->iClientId==aClientId, User::Panic(KSpecAssert_ESockMbCrCPR, 11));
	(void)(iMobilityActivity->iClientId==aClientId); //keep compiler happy
	TCFMobilityProvider::TMigrationRejected msg(KErrNone);
	__ASSERT_DEBUG_NO_LEAVE(ReceivedL(NodeId(), Id(), msg)); //ReceivedL can never leave on MM nodes
	}

EXPORT_C void CMobilityConnectionProvider::NewCarrierAccepted(TSubSessionUniqueId aClientId)
	{
	//If the interface was opened successfuly, iMobilityActivity is set.
	//It could only be cleared again if:
	//1) The mobility activity generated or not handled received error message.
	//But the activity is supposed to do that.
	//2) The node received and is currently processing TDestroy before this
	//activity was terminated, which can never happen, as the Control Client
	//would cancel it before leaving or it would be aborted on the Control Client
	//leaving (so before sending TDataClientIdle).
	__ASSERT_DEBUG(iMobilityActivity, User::Panic(KSpecAssert_ESockMbCrCPR, 12));
	__ASSERT_DEBUG(iMobilityActivity->iClientId==aClientId, User::Panic(KSpecAssert_ESockMbCrCPR, 13));
	(void)(iMobilityActivity->iClientId==aClientId);//keep compiler happy
	TCFMobilityProvider::TMigrationAccepted msg;
	__ASSERT_DEBUG_NO_LEAVE(ReceivedL(NodeId(), Id(), msg)); //ReceivedL can never leave on MM nodes
	}

EXPORT_C void CMobilityConnectionProvider::NewCarrierRejected(TSubSessionUniqueId aClientId)
	{
	//If the interface was opened successfuly, iMobilityActivity is set.
	//It could only be cleared again if:
	//1) The mobility activity generated or not handled received error message.
	//But the activity is supposed to do that.
	//2) The node received and is currently processing TDestroy before this
	//activity was terminated, which can never happen, as the Control Client
	//would cancel it before leaving or it would be aborted on the Control Client
	//leaving (so before sending TDataClientIdle).
	__ASSERT_DEBUG(iMobilityActivity, User::Panic(KSpecAssert_ESockMbCrCPR, 14));
	__ASSERT_DEBUG(iMobilityActivity->iClientId==aClientId, User::Panic(KSpecAssert_ESockMbCrCPR, 15));
	(void)(iMobilityActivity->iClientId==aClientId);//keep compiler happy
	TCFMobilityProvider::TMigrationRejected msg(KErrNone);
	__ASSERT_DEBUG_NO_LEAVE(ReceivedL(NodeId(), Id(), msg)); //ReceivedL can never leave on MM nodes
	}


