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

/**
 @file
 @internalComponent
*/

#include <comms-infras/ss_nodemessages.h>
#include <comms-infras/corecpractivities.h>

#include <comms-infras/ss_metaconnprov.h>
#include <comms-infras/sockmes.h> // for ioctl ipc

#include "IPProtoCprStates.h"
#include "IPProtoMessages.h"
#include "linkcprextensionapi.h"
#include <comms-infras/ss_datamonitoringprovider.h>
#include <comms-infras/ss_connlegacy.h>
#include <nifvar.h> // KLinkLayerOpen
#include <comms-infras/ss_nodemessages_internal.h>
#include <comms-infras/ss_legacyinterfaces.h>
#include <comms-infras/simpleselectorbase.h>
#include <commsdattypesv1_1_partner.h>
#include <es_prot_internal.h>
#include <elements/nm_messages_errorrecovery.h>

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

DEFINE_SMELEMENT(TProvisionActivation, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TProvisionActivation::DoL()
    {
    CIPProtoConnectionProvider& node = iContext.Node();
    
    //Trap if memory allocation fails
    TRAP( node.iProvisionError, node.iSubConnProvisioningInfo = new (ELeave) TDataMonitoringSubConnProvisioningInfo(NULL, NULL));
    }

DEFINE_SMELEMENT(THandleProvisionError, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void THandleProvisionError::DoL()
    {
    //Set node error
    iContext.iNodeActivity->SetError(iContext.Node().iProvisionError);
    }

DEFINE_SMELEMENT(TStoreProvision, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TStoreProvision::DoL()
	{
	PRStates::TStoreProvision storeProvision(iContext);
	storeProvision.DoL();

	CIPProtoConnectionProvider& node = iContext.Node();

	// Retrieve the idle timer values from the provisioning information bucket
	const Meta::SMetaData* extension =
		node.AccessPointConfig().FindExtension(STypeId::CreateSTypeId(TIdleTimerValues::iUid, TIdleTimerValues::iId));

    if (extension)
        {
        const TIdleTimerValues* vals = static_cast<const TIdleTimerValues*>(extension);
        node.SetTimers(vals->iShortTimer, vals->iMediumTimer, vals->iLongTimer);
        }

    if (!node.iNodeLocalExtensionsCreated)
    	{
    	node.iNodeLocalExtensions.Open();

    	TPacketActivity* packetActivity = new(ELeave)TPacketActivity(&iContext.Node().iPeriodActivity);
        CleanupStack::PushL(packetActivity);
        node.iNodeLocalExtensions.AppendExtensionL(packetActivity);
        CleanupStack::Pop(packetActivity);
        
        // Allocate and add the data monitoring shared memory pointers to the provisioning info
    	TDataMonitoringConnProvisioningInfo* connProvisioningInfo = new (ELeave) TDataMonitoringConnProvisioningInfo(&node.iDataVolumes, &node.iThresholds);
    	CleanupStack::PushL(connProvisioningInfo);
    	node.iNodeLocalExtensions.AppendExtensionL(connProvisioningInfo);
    	CleanupStack::Pop(connProvisioningInfo);

    	// Allocate the data monitoring subconnection data structure.
    	//
    	// This used to be allocated in the SCPR, but it has been moved here to the CPR.  This is because there can be
    	// temporary circumstances where there is more than one SCPR present - one or more of these in a leaving
    	// state and one in an active state.  The access point config cannot presently hold provisioning information
    	// specific to an SCPR instance, so it is allocated and managed here in the CPR as a shared entity.  Only
    	// one SCPR instance should be using this at a time, however.
    	
    	//this allocation taken care in previous activity entry
    	//just append here
    	//TDataMonitoringSubConnProvisioningInfo* subConnProvisioningInfo = new (ELeave) TDataMonitoringSubConnProvisioningInfo(NULL, NULL);
    	CleanupStack::PushL(node.iSubConnProvisioningInfo);
    	node.iNodeLocalExtensions.AppendExtensionL(node.iSubConnProvisioningInfo);
    	CleanupStack::Pop(node.iSubConnProvisioningInfo);
    	
       	// The CLinkCprExtensionApi may have been added previously if this is a reconnect scenario
    	// We add it again in this new override of the extensions, if the old container is fully superceded
    	// it will be closed and destroyed.
       	extension = CLinkCprExtensionApi::NewLC(iContext.Node());
       	node.iNodeLocalExtensions.AppendExtensionL(extension);
    	CleanupStack::Pop(); // CLinkCprExtensionApi
    	
    	node.iNodeLocalExtensionsCreated = ETrue;
    	}
    
    RMetaExtensionContainer mec;
    mec.Open(iContext.Node().AccessPointConfig());
    CleanupClosePushL(mec);
    mec.AppendContainerL(node.iNodeLocalExtensions);
    
	iContext.Node().iAccessPointConfig.Close();
    iContext.Node().iAccessPointConfig.Open(mec);
	CleanupStack::PopAndDestroy(&mec);
	
	// For the case that this is an update of the provisioned info we forward it to non-leaving data clients
	TClientIter<TDefaultClientMatchPolicy> iter = iContext.Node().GetClientIter<TDefaultClientMatchPolicy>(
		TClientType(TCFClientType::EData),
		TClientType(0, TClientType::ELeaving)
		);

	for (TInt i = 0; iter[i]; i++)
		{
		iter[i]->PostMessage(
			iContext.NodeId(),
			TCFDataClient::TProvisionConfig(iContext.Node().iAccessPointConfig).CRef()
			);
		}
	}

DEFINE_SMELEMENT(TSendStoppedAndGoneDown, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TSendStoppedAndGoneDown::DoL()
	{
	ASSERT(iContext.iNodeActivity);

	// stop has been caused by timer expiry, remove self from originators list, because we
	// are not waiting for TStopped and in certain situations it would arrive after the node has been
	// destroyed
	if (iContext.Node().iTimerExpired)
		{
		TInt selfidx = iContext.iNodeActivity->FindOriginator(iContext.Node().SelfInterface());
		ASSERT(selfidx != KErrNotFound);
		iContext.iNodeActivity->RemoveOriginator(selfidx);
		}
		
	TInt stopCode = KErrCancel;
    MeshMachine::CNodeActivityBase* activity = iContext.iNodeActivity;
    
    if (activity && activity->Error() != KErrNone)
        {
        stopCode = activity->Error();
        activity->SetError(KErrNone);
        }

	// Could be TStop, TStopped or TDataClientStopped (usually TStopped)
	if  ( (iContext.iMessage.IsMessage<TCFServiceProvider::TStopped>()) ||
		(iContext.iMessage.IsMessage<TCFServiceProvider::TStop>()) ||
		(iContext.iMessage.IsMessage<TCFDataClient::TStopped>()) ||
		(iContext.iMessage.IsMessage<TCFDataClient::TStop>()) )
		{
		stopCode = static_cast<const Messages::TSigNumber&>(iContext.iMessage).iValue;
		}
	else if ( (iContext.iMessage.IsMessage<TCFControlClient::TGoneDown>()) ||
		(iContext.iMessage.IsMessage<TCFControlProvider::TDataClientGoneDown>()) )
		{
		stopCode = static_cast<const Messages::TSigNumberNumber&>(iContext.iMessage).iValue1;
		}
	else if ( iContext.iMessage.IsMessage<TEErrorRecovery::TErrorRecoveryResponse>() )
		{
		// Action must be propagate or there is no error code (your activity flow is faulty)!
		const Messages::TSigErrResponse& sig = static_cast<const Messages::TSigErrResponse&>(iContext.iMessage);
		__ASSERT_DEBUG(sig.iErrResponse.iAction == Messages::TErrResponse::EPropagate, User::Invariant());
   		stopCode = sig.iErrResponse.iError;
		}

	TCFServiceProvider::TStopped msg(stopCode);
	iContext.iNodeActivity->PostToOriginators(msg);

    const TProviderInfo& providerInfo = static_cast<const TProviderInfoExt&>(iContext.Node().AccessPointConfig().FindExtensionL(
            STypeId::CreateSTypeId(TProviderInfoExt::EUid, TProviderInfoExt::ETypeId))).iProviderInfo;

	TCFControlClient::TGoneDown goneDown(stopCode, providerInfo.APId());
	TClientIter<TDefaultClientMatchPolicy> iter = iContext.Node().GetClientIter<TDefaultClientMatchPolicy>(TClientType(TCFClientType::ECtrl));
    CNodeActivityBase* startActivity = iContext.Node().FindActivityById(ECFActivityStart);

    for (TInt i = 0; iter[i]; i++)
		{
		//Send TGoneDown to every Ctrl client except
		// * the originator (who would be recieving TStopped)
		// * originators of the start activity (these will be errored separately)
        if (iContext.iNodeActivity && iContext.iNodeActivity->FindOriginator(*iter[i]) >= 0)
            {
            continue; // ControlClient is a Stop originator
            }
		
        // So far the control client is not a Stop originator
        if (startActivity == NULL || startActivity->FindOriginator(*iter[i]) == KErrNotFound)
			{
            // ControlClient is not a Start originator
			iter[i]->PostMessage(TNodeCtxId(iContext.ActivityId(), iContext.NodeId()), goneDown);
			}
		}

	if (iContext.iNodeActivity)
    	{
        iContext.iNodeActivity->SetError(KErrNone);
    	}
	}


DEFINE_SMELEMENT(TAwaitingStop, NetStateMachine::MState, IpProtoCpr::TContext)
TBool TAwaitingStop::Accept()
    {
	/*
	Please note, in order for this to work, ESock needs to export the Accept function
	of the CoreNetStates::TAwaitingStop.
	*/
	CoreNetStates::TAwaitingStop state(iContext);
    if (state.Accept())
        {
		if (iContext.Node().CountActivities(ECFActivityDestroy) == 0)
            {
            return ETrue;
            }
		else
            {
            //The messageId is cleared so the CoreNetStates::TAwaitingStop is not picking up this TStop message after this Accept has rejected it.
            //If this activity is kicked off while there is a Destroy activity, most probably the phone will crash, as there will be a SelfStop
            //which will arrive after the node has destructed.
            iContext.iMessage.ClearMessageId();
            }
        }
    return EFalse;
    }


DEFINE_SMELEMENT(TAwaitingDataMonitoringNotification, NetStateMachine::MState, IpProtoCpr::TContext)
TBool TAwaitingDataMonitoringNotification::Accept()
	{
	return iContext.iMessage.IsMessage<TCFDataMonitoringNotification::TDataMonitoringNotification>();
	}


DEFINE_SMELEMENT( TAwaitingGoneDown , NetStateMachine::MState, IpProtoCpr::TContext)
TBool TAwaitingGoneDown::Accept()
	{
	if  (iContext.iMessage.IsMessage<TCFControlClient::TGoneDown>())
		{
		iContext.Node().LinkDown();
		}
	// return EFalse to allow further PRActivities processing
	return EFalse;
	}


DEFINE_SMELEMENT(TProcessDataMonitoringNotification, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TProcessDataMonitoringNotification::DoL()
	{
	TCFDataMonitoringNotification::TDataMonitoringNotification& msg =
		message_cast<TCFDataMonitoringNotification::TDataMonitoringNotification>(iContext.iMessage);

	iContext.Node().DataNotificationL(msg);
	}

DEFINE_SMELEMENT(TAwaitingStart, NetStateMachine::MState, IpProtoCpr::TContext)
TBool TAwaitingStart::Accept()
	{
	CoreNetStates::TAwaitingStart state(iContext);
	if (state.Accept())
		{
		iContext.Node().DisableTimers();
		return ETrue;
		}
	return EFalse;
	}

DEFINE_SMELEMENT(TCleanupStart, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TCleanupStart::DoL()
 	{
 	//Re-enable idle timers disabled by IpProtoCpr::TAwaitingStart
	iContext.Node().EnableTimers();
	}

DEFINE_SMELEMENT(TCheckIfLastControlClientLeaving, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TCheckIfLastControlClientLeaving::DoL()
	{
	CIPProtoConnectionProvider& node = iContext.Node();
	node.ForceCheckShortTimerMode();
	}

DEFINE_SMELEMENT(TAwaitingOpenCloseRoute, NetStateMachine::MState, IpProtoCpr::TContext)
TBool TAwaitingOpenCloseRoute::Accept()
	{
	return (iContext.iMessage.IsMessage<TCFIPProtoMessage::TOpenCloseRoute>());
	}

DEFINE_SMELEMENT(TDoOpenCloseRoute, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TDoOpenCloseRoute::DoL()
	{
	TCFIPProtoMessage::TOpenCloseRoute& msg = message_cast<TCFIPProtoMessage::TOpenCloseRoute>(iContext.iMessage);
	if (msg.iValue)
		iContext.Node().OpenRoute();
	else
		iContext.Node().CloseRoute();
	}





DEFINE_SMELEMENT(TLinkUp, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TLinkUp::DoL()
	{
	iContext.Node().LinkUp();
	}

DEFINE_SMELEMENT(TLinkDown, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TLinkDown::DoL()
	{
	iContext.Node().LinkDown();
	}

DEFINE_SMELEMENT(TStoreAndFilterDeprecatedAndForwardStateChange, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TStoreAndFilterDeprecatedAndForwardStateChange::DoL()
	{
	TCFMessage::TStateChange& msg = message_cast<TCFMessage::TStateChange>(iContext.iMessage);
	//Forward to control clients if there are any

    ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x TStateChange\tProgress: %d, Error: %d"), &iContext.Node(), msg.iStateChange.iStage, msg.iStateChange.iError) );

	//We are the only node accessing this CLinkCprExtensionApi interface so we can do it safely here.
	//This is rather an exceptional situation, please keep the const_cast.
	const CLinkCprExtensionApi* linkCPR = static_cast<const CLinkCprExtensionApi*>(iContext.Node().AccessPointConfig().FindExtension(CLinkCprExtensionApi::TypeId()));
	const_cast<CLinkCprExtensionApi*>(linkCPR)->SetLastProgress(msg.iStateChange);

	if ((KLinkLayerOpen != msg.iStateChange.iStage) && (KLinkLayerClosed != msg.iStateChange.iStage) )
		{
		//KLinkLayerOpen is now redundant with TCFControlClient::TGoneUp & TCFServiceProvider::TStarted 
		//KLinkLayerClosed is now redundant with TCFControlClient::TGoneDown & TCFServiceProvider::TStopped
		//They have therefore been deprecated. Stack nodes are requested not to send the deprecated
		//signals anymore. If stack nodes do send the deprecated signals, the signals will be eaten here 
		//because they confuse convergence and mobility layers, which may live just above. 
		TInt ctrlClientCount = iContext.Node().PostToClients<TDefaultClientMatchPolicy>(TNodeCtxId(iContext.ActivityId(), iContext.NodeId()), iContext.iMessage, TClientType(TCFClientType::ECtrl));
		if (0==ctrlClientCount)
			{ //If there are no control clients any more, forward to the control provider
			iContext.Node().PostToClients<TDefaultClientMatchPolicy>(TNodeCtxId(iContext.ActivityId(), iContext.NodeId()), iContext.iMessage, TClientType(TCFClientType::ECtrlProvider));
			}
		}
	}

DEFINE_SMELEMENT(TSendStopToSelf, NetStateMachine::MStateTransition, TContext)
void TSendStopToSelf::DoL()
 	{
	iContext.iNodeActivity->PostRequestTo(iContext.NodeId(),
		TCFServiceProvider::TStop(iContext.iNodeActivity->Error()).CRef());
	}


DEFINE_SMELEMENT(TSendStarted, NetStateMachine::MStateTransition, TContext)
void TSendStarted::DoL()
 	{
 	//Set the idle timers
	iContext.Node().EnableTimers();
	iContext.Node().SetUsageProfile(KConnProfileMedium);
	iContext.Node().SetTimerMode(CIPProtoConnectionProvider::ETimerMedium);

	CoreNetStates::TSendStarted transition(iContext);
	transition.DoL();
	}

DEFINE_SMELEMENT(IpProtoCpr::TProcessDataClientStatusChange, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void IpProtoCpr::TProcessDataClientStatusChange::DoL()
    {
    TCFControlProvider::TDataClientStatusChange& msg = message_cast<TCFControlProvider::TDataClientStatusChange>(iContext.iMessage);

   	if(msg.iValue == TCFControlProvider::TDataClientStatusChange::EStarted)
		{
		iContext.iPeer->SetFlags(TCFClientType::EStarted);
		}
	else
		{
		iContext.iPeer->ClearFlags(TCFClientType::EStarted);
		}

    if(!msg.iValue && !iContext.Node().iSubConnEventDataSent)
    	{ // We're only interested in the provider being stopped.
    	ADataMonitoringProtocolReq* dmInterface = NULL;
		iContext.Node().ReturnInterfacePtrL(dmInterface);

		ADataMonitoringProvider* dmProvider = static_cast<ADataMonitoringProvider*>(dmInterface);

    	ASSERT(dmProvider); // Must always support this interface

    	TCFMessage::TSubConnDataTransferred wholeConnMsg(KNifEMCompatibilityLayerEntireSubConnectionUid, dmProvider->DataVolumesPtr()->iSentBytes, dmProvider->DataVolumesPtr()->iReceivedBytes);
    	TCFMessage::TSubConnDataTransferred defaultSubConnMsg(KNifEMCompatibilityLayerFakeSubConnectionId, dmProvider->DataVolumesPtr()->iSentBytes, dmProvider->DataVolumesPtr()->iReceivedBytes);

    	RNodeInterface* ctrlClient = iContext.Node().GetFirstClient<TDefaultClientMatchPolicy>(TClientType(TCFClientType::ECtrl));
		if(ctrlClient)
			{ // Can't send this if the client's gone
			ctrlClient->PostMessage(iContext.NodeId(), wholeConnMsg);
			ctrlClient->PostMessage(iContext.NodeId(), defaultSubConnMsg);

			iContext.Node().iSubConnEventDataSent = ETrue;
			}
    	}
    }

DEFINE_SMELEMENT(TAwaitingIoctlMessage, NetStateMachine::MState, IpProtoCpr::TContext)
TBool TAwaitingIoctlMessage::Accept()
	{
	if (iContext.iMessage.IsTypeOf(Meta::STypeId::CreateSTypeId(TCFSigLegacyRMessage2Ext::EUid, TCFSigLegacyRMessage2Ext::ETypeId)))
		{
		TCFSigLegacyRMessage2Ext& msg = static_cast<TCFSigLegacyRMessage2Ext&>(iContext.iMessage);
		if (msg.iMessage.Function() == ECNIoctl)
			{
			return ETrue;
			}
		}
	return EFalse;
	}

DEFINE_SMELEMENT(TForwardToDefaultDataClient, NetStateMachine::MStateTransition, IpProtoCpr::TContext)
void TForwardToDefaultDataClient::DoL()
	{
	ASSERT(iContext.iMessage.IsTypeOf(Meta::STypeId::CreateSTypeId(TCFSigLegacyRMessage2Ext::EUid, TCFSigLegacyRMessage2Ext::ETypeId)));

	RNodeInterface* dc = iContext.Node().GetFirstClient<TDefaultClientMatchPolicy>(TClientType(TCFClientType::EData));	
	iContext.Activity()->PostRequestTo(*dc, iContext.iMessage);
	}
