// Copyright (c) 1997-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:
// SS_CONN.CPP
// 
//
#include "ss_connselect.h"

#include <comms-infras/ss_log.h>
#include "SS_conn.H"
#include <comms-infras/esock_params_internal.h>
#include <comms-infras/ss_nodemessages_selector.h>

#include <elements/nm_messages_base.h>
#include <elements/nm_messages_peer.h>

#include <comms-infras/ss_nodemessages_dataclient.h>
#include <comms-infras/ss_nodemessages_factory.h>
#include <comms-infras/ss_nodemessages_internal.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_ESockSSockscnslc, "ESockSSockscnslc");
#endif

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

//
//CSelectionRequest
CSelectionRequest* CSelectionRequest::NewL(const TRuntimeCtxId& aRequestingClient, const TNodeId& aTierManagerId)
	{
	CSelectionRequest* self = new (ELeave) CSelectionRequest(aRequestingClient,aTierManagerId);

	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::NewL"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x]"), self));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), aRequestingClient, _L8("Client:"));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), aTierManagerId, _L8("TierMgr:"));
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());
	return self;
	}

CSelectionRequest::CSelectionRequest(const TRuntimeCtxId& aRequestingClient, const TNodeId& aTierManagerId)
:	Messages::ASimpleNodeIdBase(),
	TIfStaticFetcherNearestInHierarchy(this),
	iTierManagerId(aTierManagerId),
	iSelectionStatus(EIdle),
	iPlatsecApiExt(NULL)
	{
	LOG_NODE_CREATE(KESockMetaConnectionTag, CSelectionRequest);
	iRequestingNode.Open(address_cast<TNodeId>(aRequestingClient));
	iRequest.Open(iRequestingNode, aRequestingClient);
	}

CSelectionRequest::~CSelectionRequest()
	{
	if (iPlatsecApiExt != NULL)
		delete iPlatsecApiExt;
		

	iRequestingNode.Close();

	//Properly handled CSR should either complete the request or be cancelled before being destroyed.
	//If the requesting client wants to cancel its request with this CSR, it sends a cancel message.
	//The cancel message is processed (all active requests are being cancelled) and when all complete
	//with error (or select complete) messages, then the error is being send to the requesting client.
	//The requesting client must wait for this error (confirmation to its cancel message) and then
	//it can destroy this CSR, not earlier.
	//This is why we must make sure that iActiveRequests is empty.
	//If this ASSERT fires in your case, make sure that the requesting client obeys the above mentioned
	//protocol.
	__ASSERT_DEBUG(iActiveRequests.Count()==0, User::Panic(KSpecAssert_ESockSSockscnslc, 1));
	iActiveRequests.Close();

	//The client decided to destroy us.
	//Regardless if we received or didn't receive TSelect, iTopMcprId must be empty now.
	__ASSERT_DEBUG(iTopMcprId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 2));
	LOG_NODE_DESTROY(KESockMetaConnectionTag, CSelectionRequest);
	}

//The entry point to the selector. It may be replaced by the mesh machine in the future.
void CSelectionRequest::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& /*aRecipient*/, TSignatureBase& aCFMessage)
    {
	if (TEBase::ERealmId == aCFMessage.MessageId().Realm())
		{
		switch (aCFMessage.MessageId().MessageId())
			{
		case TEBase::TCancel::EId :
			__ASSERT_DEBUG(iRequestingNode == aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 3));
			Cancel();
			break;
		case TEBase::TError::EId:
			{
			if (iSelectionStatus==ERequestingCommsBinder)
				{
				CommsBinderRequestError(aSender, message_cast<TEBase::TError>(aCFMessage));
				}
			else
				{
				TEBase::TError& error = message_cast<TEBase::TError>(aCFMessage);
				if (error.iMsgId == TCFSelector::TSimpleSelect::Id()
					|| error.iMsgId == TCFSelector::TSelect::Id())
					{
					//TSelect may be used by CConnection or the TopMcpr
					//In both cases the control client must leave on an error.
					TInt idx = FindActiveRequest(aSender);
					if (idx != KErrNotFound && !(iActiveRequests[idx].Flags() & TClientType::ELeaving))
						{
						RNodeInterface::OpenPostMessageClose(Id(), aSender, TEChild::TLeft().CRef());
						iActiveRequests[idx].SetFlags(TClientType::ELeaving);
						}
										
					if (aSender == iTopMcprId)
						{
						iTopMcprId.SetNull();
						}
					}
				SelectionError(aSender, error.iValue);
				}
			}
			break;
		default:
//TODO - logging
			NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest:ReceivedL"));
			NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x] ERROR: KErrNotSupported "), this));
			NM_LOG_MESSAGE(KESockMetaConnectionTag(), aCFMessage);
			NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());
	    		__ASSERT_DEBUG(iRequestingNode==aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 4));

			__ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_ESockSSockscnslc, 5)); //For debug configurations
			User::Leave(KErrNotSupported); //For release configurations
			}
		}
	else if ( aCFMessage.IsMessage<TEChild::TDestroy>() )
		{
		__ASSERT_DEBUG(iRequestingNode==aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 6));
		InitialiseDestroy();
		}
	else if (TEPeer::ERealmId == aCFMessage.MessageId().Realm())
		{
		switch (aCFMessage.MessageId().MessageId())
			{
		case TEPeer::TLeaveComplete::EId:
			__ASSERT_DEBUG(aSender==iTopMcprId || iTopMcprId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 7));
			iTopMcprId.SetNull();
			delete this;
			break;
		default:
//TODO - logging
			NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest:ReceivedL"));
			NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x] ERROR: KErrNotSupported "), this));
			NM_LOG_MESSAGE(KESockMetaConnectionTag(), aCFMessage);
			NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

			__ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_ESockSSockscnslc, 8)); //For debug configurations
			User::Leave(KErrNotSupported); //For release configurations
			}
		}
	else if (TCFSelector::ERealmId == aCFMessage.MessageId().Realm())
		{
		switch (aCFMessage.MessageId().MessageId())
			{
    	case TCFSelector::TSelect::EId:
    		__ASSERT_DEBUG(aSender == iRequestingNode.RecipientId(), User::Panic(KSpecAssert_ESockSSockscnslc, 9));
    		SelectConnPrefList(message_cast<const TCFSelector::TSelect>(aCFMessage).iConnPrefList);
    		break;

		case TCFSelector::TSimpleSelect::EId:
			__ASSERT_DEBUG(iRequestingNode == aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 10));
			iRequest.Open(iRequestingNode, aSender);
			Select(message_cast<const TCFSelector::TSimpleSelect>(aCFMessage).iSelectionPrefs);
			break;
		case TCFSelector::TSelectComplete::EId:
			{
			TCFSelector::TSelectComplete& msg = message_cast<TCFSelector::TSelectComplete>(aCFMessage);
			SelectComplete(address_cast<TNodeId>(aSender), msg.iNodeId, msg.iProviderInfo);
			}
			break;
		default:
//TODO - logging
			NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest:ReceivedL"));
			NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x] ERROR: KErrNotSupported "), this));
			NM_LOG_MESSAGE(KESockMetaConnectionTag(), aCFMessage);
			NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

			__ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_ESockSSockscnslc, 11)); //For debug configurations
			User::Leave(KErrNotSupported); //For release configurations
			}
		}
	else if ( aCFMessage.IsMessage<TCFServiceProvider::TCommsBinderResponse>() )
		{
		CommsBinderResponse(message_cast<TCFServiceProvider::TCommsBinderResponse>(aCFMessage));
		}
	else if ( aCFMessage.IsMessage<TCFDataClient::TBindToComplete>() )
		{
		__ASSERT_DEBUG(KErrNone==message_cast<TCFDataClient::TBindToComplete>(aCFMessage).iValue || iRequestingNode==aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 12));
		__ASSERT_DEBUG(KErrNone==message_cast<TCFDataClient::TBindToComplete>(aCFMessage).iValue || iSelectionStatus==ERequestingCommsBinder || iSelectionStatus==EIdle, User::Panic(KSpecAssert_ESockSSockscnslc, 13));
		__ASSERT_DEBUG(KErrNone==message_cast<TCFDataClient::TBindToComplete>(aCFMessage).iValue || !iTopMcprId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 14)); //iTopMcprId must be selected by now!
		RNodeInterface::OpenPostMessageClose(Id(), iTopMcprId, aCFMessage);
		}
	else if ( aCFMessage.IsMessage<TCFMessage::TStateChange>() )
		{
		if (!iDestroying)
			{
			// When destroying prevent the status change being posted to
			// the Implicit Flow Request which may already have been deleted.
			iRequestingNode.PostMessage(Id(), aCFMessage);
			}
		}
	else if ( aCFMessage.IsMessage<TCFMessage::TProvisionConnectionInfo>() )
		{
		__ASSERT_DEBUG(iRequestingNode == aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 15));
		Provision(message_cast<const TCFMessage::TProvisionConnectionInfo>(aCFMessage).iPtr);
		}
	else if ( aCFMessage.IsMessage<TCFPeer::TJoinComplete>() )
		{
		JoinComplete(address_cast<TNodeId>(aSender), message_cast<TCFPeer::TJoinComplete>(aCFMessage));
		}
	else
		{
//TODO - logging
		NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest:ReceivedL"));
		NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x] ERROR: KErrNotSupported "), this));
		NM_LOG_MESSAGE(KESockMetaConnectionTag(), aCFMessage);
		NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

		__ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_ESockSSockscnslc, 16)); //For debug configurations
		User::Leave(KErrNotSupported); //For release configurations
		}
	}

void CSelectionRequest::InitialiseDestroy()
	{
	__ASSERT_DEBUG(!iDestroying, User::Panic(KSpecAssert_ESockSSockscnslc, 17));
	iDestroying = ETrue;

    //We are not ready to destruct ourselves yet, waiting for TErrors
    if (iActiveRequests.Count()!=0)
    	{
    	return;
    	}

	//We have never joined the top mcpr, clear it now
	if (!iJoined)
		{
		iTopMcprId.SetNull();
		}

	//We are ready but we have to leave the iTopMcprId first
	if (!iTopMcprId.IsNull())
    	{
    	RNodeInterface::OpenPostMessageClose(Id(), iTopMcprId, TEPeer::TLeaveRequest().CRef());
		return;
    	}

   	//We are ready to be deleted
   	delete this;
   	return;
	}

//Selection requests start here.
void CSelectionRequest::Select(const TSelectionPrefs& aSelectionPreferences)
	{
    NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::Select"));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), iRequestingNode.RecipientId(), _L8("Client:"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x]"), this));
    NM_LOG_STMT(TUint selectionScope = aSelectionPreferences.Scope());
	NM_LOG((KESockMetaConnectionTag(), _L8("%s"), (selectionScope & TSelectionPrefs::EExplicitConnection)?_S8("EExplicitConnection"):_S8("SelectTopProviderOnly")));

	LOG( ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest %08x:\tSelect: %s."), this, (selectionScope&TSelectionPrefs::ESelectFromExisting)?_S8("ESelectFromExisting"):_S8("SelectAndCreate")));
	LOG( ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest %08x:\tSelect: %s."), this, (selectionScope&TSelectionPrefs::ERequestCommsBinder)?_S8("ERequestCommsBinder"):_S8("DoNotRequestCommsBinder")));

    NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

	//The requesting client may only request selection once.
	//If this assert fires in your case, please make sure the requesting client obeys the protocol.
	__ASSERT_DEBUG(iSelectionStatus==EIdle, User::Panic(KSpecAssert_ESockSSockscnslc, 18));
	__ASSERT_DEBUG(iTopMcprId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 19));
	iSelectionStatus = ESelecting;
	iSelectionPreferences.Copy(aSelectionPreferences);
	__ASSERT_DEBUG(iActiveRequests.Count()==0, User::Panic(KSpecAssert_ESockSSockscnslc, 20));
	if (KErrNone==iActiveRequests.Append(RNodeInterface()))
		{
		RNodeInterface& tierManager = iActiveRequests[0];
		tierManager.Open(iTierManagerId);
		tierManager.PostMessage(Id(), TCFPeer::TJoinRequest(Id(), TClientType(TCFClientType::ECtrl)).CRef());
		}
	else
		{
		//We have an OOM condition here. Since we have just started serving this request,
		//we must report this error to the requesting client (see SelectError()).
		SelectionError(Id(), KErrNoMemory);
		}
	}

void CSelectionRequest::SelectConnPrefList(const RConnPrefList& aConnPrefList)
	{
	//The requesting client may only request selection once.
	//If this assert fires in your case, please make sure the requesting client obeys the protocol.
	__ASSERT_DEBUG(iSelectionStatus == EIdle, User::Panic(KSpecAssert_ESockSSockscnslc, 21));
	__ASSERT_DEBUG(iTopMcprId == TNodeId::NullId(), User::Panic(KSpecAssert_ESockSSockscnslc, 22));
	iSelectionStatus = ESelecting;
	iConnPrefList = aConnPrefList;

	ESock::RConnPrefList::TIter<TConnCSRPref> iterCSR = iConnPrefList.getIter<TConnCSRPref>();
	//There should be one and only one CSR pref
	__ASSERT_DEBUG(iterCSR[0] != NULL || iterCSR[1] == NULL , User::Panic(KSpecAssert_ESockSSockscnslc, 23));
	TConnCSRPref* pref = iterCSR[0];
	iSelectionPreferences.SetScope(pref->Scope());
	iSelectionPreferences.SetFlags(pref->Flags());
	iSelectionPreferences.SetSubSessionUniqueId(pref->Scope());

	__ASSERT_DEBUG(iActiveRequests.Count()==0, User::Panic(KSpecAssert_ESockSSockscnslc, 24));
	if (KErrNone==iActiveRequests.Append(RNodeInterface()))
		{
		RNodeInterface& tierManager = iActiveRequests[iActiveRequests.Count()-1];
		tierManager.Open(iTierManagerId);
		tierManager.PostMessage(
			Id(),
			TCFPeer::TJoinRequest(Id(), TClientType(TCFClientType::ECtrl)).CRef()
			);
		}
	else
		{
		//We have an OOM condition here. Since we have just started serving this request,
		//we must report this error to the requesting client (see SelectError()).
		SelectionError(Id(), KErrNoMemory);
		}
	}

void CSelectionRequest::Provision(const Meta::SMetaData* aProvisionConfig)
	{
/*	if (!aProvisionConfig->IsTypeOf(STypeId::CreateSTypeId(CConnectionInfo::EUid, CConnectionInfo::ETypeId)))
	return;*/
	iProvision = aProvisionConfig;
	}

void CSelectionRequest::Cancel()
	{
	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::Cancel"));
	NM_LOG((KESockMetaConnectionTag, _L8("[this=0x%08x]"), this));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), iRequestingNode.RecipientId(), _L8("Client:"));
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

	//Is the requesting client trying to cancel its request again?
	__ASSERT_DEBUG(iSelectionStatus!=ECancelling, User::Panic(KSpecAssert_ESockSSockscnslc, 25));
	if (iSelectionStatus!=EIdle)
		{
		iSelectionStatus = ECancelling;
		iOriginalError = KErrCancel;

		TEBase::TCancel cancel;
		for (TInt i = iActiveRequests.Count()-1; i >= 0; i--)
			{
			//Send cancel and wait for error messages before destructing this object.
			iActiveRequests[i].PostMessage(Id(), cancel);
			}
		//We have not completed the original request yet, do not send
		//an error message to the requesting client from here.
		//When the last error (or select complete) message has been returned
		//from the last MCpr we have cancelled, then we send our cancel's
		//confirmation from SelectionFinished().
		}
	}

void CSelectionRequest::JoinComplete(const TNodeId& aSender, TCFPeer::TJoinComplete& /*aCFMessage*/)
	{
	if (KErrNone==iOriginalError)
		{
		if (iTopMcprId.IsNull())
			{
			__ASSERT_DEBUG(iActiveRequests[0].RecipientId()==aSender && iTierManagerId==aSender, User::Panic(KSpecAssert_ESockSSockscnslc, 26)); //It has surely been sent by the tier manager.

			if(iConnPrefList.Count() < 1)
				{
				iActiveRequests[0].PostMessage(Id(), TCFSelector::TSimpleSelect(iSelectionPreferences).CRef());
				}
			else
				{
				iActiveRequests[0].PostMessage(Id(), TCFSelector::TSelect(iConnPrefList).CRef());
				}
			}
		else
			{
			if (aSender==iTopMcprId)
				{
				iJoined = ETrue;
				
				TUint selectionScope = iSelectionPreferences.Scope();
				if (!(selectionScope & TSelectionPrefs::EExplicitConnection))
					{
					return;
					}
				}

			if(iConnPrefList.Count() < 1)
				{
				RNodeInterface::OpenPostMessageClose(Id(), aSender, TCFSelector::TSimpleSelect(iSelectionPreferences).CRef());
				}
			else
				{
				RNodeInterface::OpenPostMessageClose(
					Id(),
					aSender,
					TCFSelector::TSelect(iConnPrefList).CRef()
					);
				}
			}
		}
	else
		{
		TInt idx = FindActiveRequest(aSender);
		if (idx != KErrNotFound && !(iActiveRequests[idx].Flags() & TClientType::ELeaving))
			{
			RNodeInterface::OpenPostMessageClose(Id(), aSender, TEChild::TLeft().CRef());
			iActiveRequests[idx].SetFlags(TClientType::ELeaving);
			}
		
		ProviderSelectionFinished(aSender);
		
		if (iActiveRequests.Count()==0)
			{ //The active list is empty which means that we have finished serving the selection request.
			SelectionFinished();
			}
		}
	}

void CSelectionRequest::CommsBinderResponse(const TCFServiceProvider::TCommsBinderResponse& aMsg)
	{
	iRequest.ReplyTo(Id(), TCFDataClient::TBindTo(aMsg.iNodeId).CRef());
	iSelectionStatus = EIdle;
	}

//This fn is called when a provider has been successfuly selected.
void CSelectionRequest::SelectComplete(const TNodeId& aSenderId, const TNodeId& aMcprId, const TProviderInfo& aMcprInfo)
	{
	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::SelectComplete"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x]"), this));
	NM_LOG_ADDRESS(KESockMetaConnectionTag(), aSenderId);
	NM_LOG_ADDRESS(KESockMetaConnectionTag(), aMcprId);
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

	__ASSERT_DEBUG(!aSenderId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 27)); //The sender must exist and be in the active list

	//The sender must be in the active list (or it is the tier manager)
	__ASSERT_DEBUG(KErrNotFound!=FindActiveRequest(aSenderId), User::Panic(KSpecAssert_ESockSSockscnslc, 28));

	if (aMcprId.IsNull())
		{ //Last SelectComplete from aSenderId. Remove from the active list.
		ProviderSelectionFinished(aSenderId);
		}
	else
		{
		HandleProviderSelection(aMcprId);
		}

	if (iTopMcprId.IsNull())
		{
		//Top provider has been selected (it must not be null since we have received SelectComplete and not Error).

    	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag, aMcprId, _L8("Top provider selected: "));

		//We should have never reveive SelectComplete(NULL) as the only select complete.
		//If even one provider cannot be selected we should have received an error message!
		__ASSERT_DEBUG(!aMcprId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 30));
		__ASSERT_DEBUG(iActiveRequests[0].RecipientId()==aSenderId, User::Panic(KSpecAssert_ESockSSockscnslc, 29)); //It has surely been sent by the tier manager.
		iTopMcprId = aMcprId;
		iTopMcprInfo = aMcprInfo;
		}
	else if (iActiveRequests.Count()==0)
		{ //The active list is empty which means that we have finished serving the selection request.
		SelectionFinished();
		}
	}

//This fn is called when we receive or generate an error during selection.
void CSelectionRequest::SelectionError(const TRuntimeCtxId& aSenderId, TInt aError)
	{
	//Remember the error because for legacy selection we need to use it
	if (KErrNone==iOriginalError)
		{
		iOriginalError = aError;
		}

	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::SelectionError"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x] [aError=%d]"), this, aError));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), iRequestingNode.RecipientId(), _L8("Client:"));
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

	__ASSERT_DEBUG(!aSenderId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 31)); //The sender must exist and be in the active list
	__ASSERT_DEBUG(iSelectionStatus!=ERequestingCommsBinder, User::Panic(KSpecAssert_ESockSSockscnslc, 32));

	TInt idx = FindActiveRequest(aSenderId);
	if(KErrNotFound != idx)
		{
		//Rather than leaving the service provider (sender) that has send as the error message,
		//we are removing it from our list. Any further comunication with the node could result
		//in further errors.
		iActiveRequests[idx].Close();
		iActiveRequests.Remove(idx);
		}

	//If the active list is empty, we have unsuccessfuly finished the whole selection.
	if (iActiveRequests.Count()==0)
		{
		SelectionFinished();
		}
	}

//This fn is called when we receive an error instead of CommsBinder.
void CSelectionRequest::CommsBinderRequestError(const TRuntimeCtxId& aSenderId, TEBase::TError& aCFMessage)
	{
	// Preventing unused variable warnings.
	#ifndef _DEBUG
		(void)aSenderId;
	#endif

	//Remember the error because for legacy selection we need to use it
	if (KErrNone==iOriginalError)
		{
		iOriginalError = aCFMessage.iValue;
		}

	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::CommsBinderRequestError"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x]"), this));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), iRequestingNode.RecipientId(), _L8("Client:"));
	NM_LOG_MESSAGE(KESockMetaConnectionTag(), aCFMessage);
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());


	__ASSERT_DEBUG(!aSenderId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 33)); //The sender must exist and be in the active list
	__ASSERT_DEBUG(iSelectionStatus==ERequestingCommsBinder, User::Panic(KSpecAssert_ESockSSockscnslc, 34));
	__ASSERT_DEBUG(aSenderId==iTopMcprId, User::Panic(KSpecAssert_ESockSSockscnslc, 35)); //CommsBinder error, this can only come from the iTopMcprId
	//This is an error that needs to be reported to the requesting client.
	PostError(TCFServiceProvider::TCommsBinderRequest::Id(), iOriginalError);
	}

//We call this fn when we need to report the selection error to the originating client,
//but after the selection is finished
void CSelectionRequest::PostError(const TNodeSignal::TMessageId& aMessageId, TInt aError)
	{
	//Some non-recoverable error has occured and we want to report it back to the requesting client.

	//Because the selection is finished now, there shouldn'd be any more outstanding
	//selections going on below.
	__ASSERT_DEBUG(iActiveRequests.Count()==0, User::Panic(KSpecAssert_ESockSSockscnslc, 36));

	iRequest.ReplyTo(Id(), TEBase::TError(aMessageId,aError).CRef());
	}

void CSelectionRequest::HandleProviderSelection(const TNodeId& aMcprId)
	{
	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::HandleProviderSelection"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x]"), this));
	NM_LOG_ADDRESS(KESockMetaConnectionTag(), aMcprId);
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());


	// Select next layer provider only if the selection mode is EExplicitConnection
	TUint selectionScope = iSelectionPreferences.Scope();
	if (selectionScope & TSelectionPrefs::EExplicitConnection)
		{
		//Are we currently waiting for this provider to complete our selection request?
		if (KErrNotFound==FindActiveRequest(aMcprId))
			{
			if (KErrNone==iActiveRequests.Append(RNodeInterface()))
				{
				
				 RNodeInterface& mcpr = iActiveRequests[iActiveRequests.Count()-1];
				 mcpr.Open(aMcprId, TClientType(TCFClientType::EServProvider));
				 mcpr.PostMessage(Id(), TCFServiceProvider::TJoinRequest(Id(), TClientType::EAdministrative).CRef()); //Join the provider

				}
			else
				{
				//We have an OOM condition here.
				SelectionError(Id(), KErrNoMemory);
				}
			}
		}
	else if (selectionScope & TSelectionPrefs::ERequestCommsBinder && KErrNone==iOriginalError)
		{
		__ASSERT_DEBUG(iTopMcprId.IsNull(), User::Panic(KSpecAssert_ESockSSockscnslc, 37));

		RNodeInterface::OpenPostMessageClose(Id(), aMcprId, TCFServiceProvider::TJoinRequest(Id(), TCFClientType::EAdministrative).CRef());
		}
	}

void CSelectionRequest::ProviderSelectionFinished(const TRuntimeCtxId& aMcprId)
	{
	NM_LOG_START_BLOCK(KESockMetaConnectionTag(), _L8("CSelectionRequest::ProviderSelectionFinished"));
	NM_LOG((KESockMetaConnectionTag(), _L8("[this=0x%08x]"), this));
	NM_LOG_ADDRESS_EXT(KESockMetaConnectionTag(), aMcprId, _L8("aMcprId:"));
	NM_LOG_END_BLOCK(KESockMetaConnectionTag(), KNullDesC8());

	TInt idx = FindActiveRequest(aMcprId);
	__ASSERT_DEBUG(KErrNotFound!=idx, User::Panic(KSpecAssert_ESockSSockscnslc, 38)); //Sender must be in the active list

	//Leave the providers but do not leave:
	//1) The tier manager, as it has never been joined.
	//2) The top provider, as it needs to be alive for as long as the requestor is happy to delete us
	const TNodeId& selectionProvider = iActiveRequests[idx].RecipientId();
	if (iTopMcprId!=selectionProvider) //Do not leave the top MCpr yet
		{
		if (idx != KErrNotFound && !(iActiveRequests[idx].Flags() & TClientType::ELeaving))
			{
			RNodeInterface::OpenPostMessageClose(Id(), aMcprId, TEChild::TLeft().CRef());
			iActiveRequests[idx].SetFlags(TClientType::ELeaving);
			}
		}

	//Remove sender from the active list
	iActiveRequests[idx].Close();
	iActiveRequests.Remove(idx);
	}

//This fn is called when the whole plane (or just one provider) selection is finished.
//Now we can either start the selected (top) provider or reply to the requesting client
//or just cleanup if the client has cancelled us in the meantime.
void CSelectionRequest::SelectionFinished()
	{
	LOG(ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest::SelectionFinished [this=0x%08x]"), this));

	if (iSelectionStatus==ECancelling)
		{ //The requesting client has cancelled us.
		LOG(ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest::SelectionFinished - cancelled"), this));

		PostError(TEBase::TCancel::Id(),KErrCancel);
		if (!iTopMcprId.IsNull() && iJoined)
	    	{
	    	//We are ready to destroy ourselves now but we have the iTopMcprId to leave first
	    	RNodeInterface::OpenPostMessageClose(Id(), iTopMcprId, TEPeer::TLeaveRequest().CRef());
	    	}
		return;
		}

	//We can continue with Starting.
	if (KErrNone!=iOriginalError || iTopMcprId.IsNull())
		{
		LOG(ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest::SelectionFinished - no top provider found"), this));

		//We don't even have the top provider selected.
		//This is an error that needs to be reported to the requesting client.
		PostError(TCFSelector::TSimpleSelect::Id(),iOriginalError);
		return;
		}

	//We can continue with Requesting Comms Binder.
	TUint selectionScope = iSelectionPreferences.Scope();
	if (selectionScope & TSelectionPrefs::ERequestCommsBinder)
		{
		LOG(ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest::SelectionFinished - binding to provider (MCpr Tier Id: %X, AP Id: %d)"), this, iTopMcprInfo.TierId().iUid, iTopMcprInfo.APId()));

		TNodeCtxId sender(ECFActivityBinderRequest, Id()); //TODO[PROD]: Does this make sense to use ECFActivityBinderRequest here?
		RNodeInterface::OpenPostMessageClose(sender, iTopMcprId, TCFServiceProvider::TCommsBinderRequest().CRef());
		iSelectionStatus = ERequestingCommsBinder;
		}
	else
		{
		LOG(ESockLog::Printf(KESockMetaConnectionTag, _L8("CSelectionRequest::SelectionFinished - selected provider (MCpr Tier Id: %X, AP Id: %d)"), this, iTopMcprInfo.TierId().iUid, iTopMcprInfo.APId()));

		//Finish the selection
		//We will leave the top provider in ~CSelectionRequest()
		iRequest.PostMessage(Id(), TCFSelector::TSelectComplete(iTopMcprId,iTopMcprInfo).CRef());
		}
	}

TInt CSelectionRequest::FindActiveRequest(const TRuntimeCtxId& aMcprId)
	{
	for (TInt i = iActiveRequests.Count()-1; i >= 0; i--)
		{
		if (iActiveRequests[i] == aMcprId)
			{
			return i;
			}
		}

	return KErrNotFound;
	}

TInt CSelectionRequest::SecureId(TSecureId& aResult) const
	{
	__ASSERT_DEBUG(iPlatsecApiExt, User::Panic(KSpecAssert_ESockSSockscnslc, 39));
	return iPlatsecApiExt->SecureId(aResult);
	}

TInt CSelectionRequest::VendorId(TVendorId& aResult) const
	{
	__ASSERT_DEBUG(iPlatsecApiExt, User::Panic(KSpecAssert_ESockSSockscnslc, 40));
	return iPlatsecApiExt->VendorId(aResult);
	}

TBool CSelectionRequest::HasCapability(const TCapability aCapability) const
	{
	__ASSERT_DEBUG(iPlatsecApiExt, User::Panic(KSpecAssert_ESockSSockscnslc, 41));
	return iPlatsecApiExt->HasCapability(aCapability);
	}

TInt CSelectionRequest::CheckPolicy(const TSecurityPolicy& aPolicy) const
	{
	__ASSERT_DEBUG(iPlatsecApiExt, User::Panic(KSpecAssert_ESockSSockscnslc, 42));
	return iPlatsecApiExt->CheckPolicy(aPolicy);
	}

NetInterfaces::TInterfaceControl* CSelectionRequest::FetchNodeInterfaceControlL(TInt aInterfaceId)
	{
	if (aInterfaceId == MPlatsecApiExt::KInterfaceId)
    	{
    	if (iPlatsecApiExt)
        	{
        	return this;
        	}
    	
    	if (iProvision && iProvision->GetTypeId() == Meta::STypeId::CreateSTypeId(CConnectionInfo::EUid, CConnectionInfo::ETypeId))
        	{
            iPlatsecApiExt = new(ELeave) ASubSessionPlatsecApiExt(static_cast<const CConnectionInfo*>(iProvision)->SubSessionId());
            return this;
        	}
    	}

	return Messages::ANode::FetchNodeInterfaceControlL(aInterfaceId);
	}

void CSelectionRequest::ReturnInterfacePtrL(MPlatsecApiExt*& aInterface)
	{
	if (iPlatsecApiExt)
		aInterface = this;
	else
		aInterface = NULL;
	}

