// 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:
// This file provides the implementation of the NetUps Statemachine.
// @internalAll
// @prototype
// 
//

#include "e32def.h"			//defines __ASSERT_DEBUG
#include "e32cmn.h"

#include "netupsassert.h"

#include "netupsstatemachine.h"
#include "netupsstate.h"
#include "netupsstatedef.h"

#include "netupsserviceid.h"

#include "netupsimpl.h"

#include <comms-infras/commsdebugutility.h> 		// defines the comms debug logging utility

namespace NetUps
{
__FLOG_STMT(_LIT8(KNetUpsSubsys, 	"esock");)   
__FLOG_STMT(_LIT8(KNetUpsComponent, "NetUps");) /*esockloader*/

CUpsStateMachine* CUpsStateMachine::NewL(CNetUpsImpl& aNetUpsImpl, TInt32 aServiceId)
	{
	__FLOG_STATIC1(KNetUpsSubsys, KNetUpsComponent, _L("CUpsStateMachine::NewL(), aServiceId = %d"), aServiceId);		

	CUpsStateMachine* self = NULL;
	if (aServiceId != EIpServiceId)
		{
		__FLOG_STATIC0(KNetUpsSubsys, KNetUpsComponent, _L("CUpsStateMachine::NewL() Service id not supported"));
		User::Leave(KErrNotSupported);
		}
	else
		{
		self = new (ELeave) CUpsStateMachine(aNetUpsImpl, aServiceId);

		CleanupStack::PushL(self);
		self->ConstructL();
		CleanupStack::Pop(self);
		}
		
	return self;
	}

CUpsStateMachine::CUpsStateMachine(CNetUpsImpl& aNetUpsImpl, TInt32 aServiceId) : iNetUpsImpl(aNetUpsImpl), iServiceId(aServiceId) 
	{
	__FLOG_2(_L("CUpsStateMachine %08x:\t CUpsStateMachine()"), this, aServiceId);			
	}

void CUpsStateMachine::ConstructL()
	{
	__FLOG_2(_L("CUpsStateMachine %08x:\t ConstructL()"), this, iNetUpsImpl.LifeTimeMode());		

	// Note the order in which the states are instantiated must match the 
	// order in which they are defined in the enumeration TNetUpsState - or a panic will occur.

	CState* state=CState::NewL(ENull, *this);
		
	CleanupStack::PushL(state);
	iState.AppendL(state);
	CleanupStack::Pop(state);
		
	switch(iNetUpsImpl.LifeTimeMode())
		{
			
		case CNetUpsImpl::EProcessLifeTimeMode:
			{
			CState* stateone= CState::NewL(EProcLife_NonSession, *this);
			CleanupStack::PushL(stateone);
			iState.AppendL(stateone);	
			
			CState* statetwo = CState::NewL(EProcLife_Transit_SessionYes, *this);
			CleanupStack::PushL(statetwo);
			iState.AppendL(statetwo); // a transient state is entered when the UPS Server responds with either SessionYes or SessionNo and there are 1 or more UPS requests outstanding to other subsessions which are associated with the same process.			
			
			CState* statethree = CState::NewL(EProcLife_SessionYes, *this);
			CleanupStack::PushL(statethree);
			iState.AppendL(statethree);
			
			CState* statefour = CState::NewL(EProcLife_Transit_SessionNo, *this);
			CleanupStack::PushL(statefour);
			iState.AppendL(statefour);
			
			CState* statefive = CState::NewL(EProcLife_SessionNo, *this);
			CleanupStack::PushL(statefive);
			iState.AppendL(statefive);
			
			CleanupStack::Pop(5);
			break;
			}
		case CNetUpsImpl::ENetworkLifeTimeMode:
			{
			CState* stateone = CState::NewL(EProcLife_NonSession, *this);
			CleanupStack::PushL(stateone);
			iState.AppendL(stateone);
			
			CState* statetwo = CState::NewL(ENetLife_SessionNo_Transit_WithoutConnections, *this);
			CleanupStack::PushL(statetwo);
			iState.AppendL(statetwo);		
			
			CState* statethree = CState::NewL(ENetLife_SessionNo_WithOutConnections, *this);
			CleanupStack::PushL(statethree);
			iState.AppendL(statethree);
			
			CState* statefour = CState::NewL(ENetLife_SessionNo_Transit_WithConnections, *this);
			CleanupStack::PushL(statefour);
			iState.AppendL(statefour);
			
			CState* statefive = CState::NewL(ENetLife_SessionNo_WithConnections, *this);
			CleanupStack::PushL(statefive);
			iState.AppendL(statefive);
			
			CState* statesix = CState::NewL(ENetLife_Transit_SessionYes, *this);
			CleanupStack::PushL(statesix);
			iState.AppendL(statesix);
								
			CState* stateseven = CState::NewL(ENetLife_SessionYes, *this);
			CleanupStack::PushL(stateseven);
			iState.AppendL(stateseven);
			
			CleanupStack::Pop(7);
			break;				
			}
		default:
			{
			User::Leave(KErrNotSupported);			
			}
		}

	__FLOG_OPEN(KNetUpsSubsys, KNetUpsComponent);
	__FLOG_1(_L("CUpsStateMachine %08x:\t ConstructL()"), this);				
	}

TUint32 CUpsStateMachine::GetIndex(TNetUpsState aNetUpsState)
	{		
	__FLOG_2(_L("CUpsStateMachine %08x:\t GetIndex() aNetUpsState = %d"), this, aNetUpsState );		

	TUint32 index = 0;
	if (aNetUpsState != ENull)
		{
		switch(iNetUpsImpl.LifeTimeMode())
			{
			case CNetUpsImpl::EProcessLifeTimeMode:
				index = aNetUpsState  - EProcLife_NonSession + 1;
				//__FLOG_3(_L("CUpsStateMachine %08x:\t GetIndex() aNetUpsState = %d, index = %d, mode = iNetUpsImpl.LifeTimeMode()"), this, index, iNetUpsImpl.LifeTimeMode() );		
				break;
			case CNetUpsImpl::ENetworkLifeTimeMode:
				index = aNetUpsState -  ENetLife_NonSession + 1;
				//__FLOG_3(_L("CUpsStateMachine %08x:\t GetIndex() aNetUpsState = %d, index = %d, mode = iNetUpsImpl.LifeTimeMode()"), this, index, iNetUpsImpl.LifeTimeMode() );		

				break;
			default:				
				__FLOG_2(_L("CUpsStateMachine %08x:\t GetIndex() aNetUpsState = %d, mode = iNetUpsImpl.LifeTimeMode()"), this, iNetUpsImpl.LifeTimeMode() );		
				User::Panic(KNetUpsPanic, KPanicSessionTypeOutOfRange);
				break;
			}
		}
	return index;	
	}


CUpsStateMachine::~CUpsStateMachine()
	{
	__FLOG_1(_L("CUpsStateMachine %08x:\t ~CUpsStateMachine()"), this);		

	for (TInt i = iState.Count() - 1; i >= 0; i--)
			{
			delete iState.operator[](i);
			__FLOG_2(_L("CUpsStateMachine %08x:\t ~CUpsStateMachine(), iState[i] = %08x"), this, iState[i]);		
			iState.Remove(i);
			}
	iState.Reset();
	iState.Close();		

	__FLOG_CLOSE;	
	}

TUint32 CUpsStateMachine::GetIndex(CProcessEntry& aProcessEntry)
{
	TNetUpsState netUpsState = CUpsStateMachine::GetState(aProcessEntry);
	TInt32 index = static_cast<TInt32>(GetIndex(netUpsState));

	__FLOG_3(_L("CUpsStateMachine %08x:\t GetIndex(), index = %d, netUpsState = %d"),
			 this, index, netUpsState); 
		
	__ASSERT_DEBUG((index < iState.Count()), User::Panic(KNetUpsPanic, KPanicStateMachineIndexOutOfRange));
	__ASSERT_DEBUG((iState.operator[](index)->State() == netUpsState), User::Panic(KNetUpsPanic, KPanicInvalidStateMachineIndex));

	return index;
}

void CUpsStateMachine::ProcessPolicyCheckRequestL(TThreadKey& aThreadKey, const TPolicyCheckRequestData& aPolicyCheckRequestData, TRequestId aRequestId)
	{
	__FLOG_8(_L("CUpsStateMachine %08x:\t ProcessPolicyCheckRequestL(), processId = %d, threadId = %d, serviceId  = %d, platSecCheckResult  = %d, commsId  = %d, originator  = %08x, request id = %d"),
				 this, aPolicyCheckRequestData.iProcessId.Id(),
				 aPolicyCheckRequestData.iThreadId.Id(),		aPolicyCheckRequestData.iServiceId,
				 aPolicyCheckRequestData.iPlatSecCheckResult, 	&aPolicyCheckRequestData.iCommsId.Node(),
				 &(aPolicyCheckRequestData.iPolicyCheckRequestOriginator), aRequestId); 

	TUint32 index = GetIndex(aThreadKey.iProcessEntry);
	(iState.operator[](index))->ProcessPolicyCheckRequestL(aThreadKey, aPolicyCheckRequestData, aRequestId);
	}

void CUpsStateMachine::ProcessPolicyCheckRequestL(TProcessKey& aProcessKey, const TPolicyCheckRequestData& aPolicyCheckRequestData, TRequestId aRequestId)
	{
	__FLOG_8(_L("CUpsStateMachine %08x:\t ProcessPolicyCheckRequestL(), processId = %d, threadId = %d, serviceId  = %d, platSecCheckResult  = %d, commsId  = %08x, originator  = %08x, request id = %d"),
				 this, aPolicyCheckRequestData.iProcessId.Id(),
				 aPolicyCheckRequestData.iThreadId.Id(),		aPolicyCheckRequestData.iServiceId,
				 aPolicyCheckRequestData.iPlatSecCheckResult, 	&aPolicyCheckRequestData.iCommsId.Node(),
				 &(aPolicyCheckRequestData.iPolicyCheckRequestOriginator), aRequestId); 

	// Suspect that this method is not required, when threadEntry is not known, we are in session lifetime.
	(void) aProcessKey;
	(void) aPolicyCheckRequestData;
	(void) aRequestId;
	User::Panic(KNetUpsPanic, KPanicMethodNotSupported);
	}

void CUpsStateMachine::IncrementConnectionCountL(TCommsIdKey& aCommsIdKey)
	{
	__FLOG_4(_L("CUpsStateMachine %08x:\t IncrementConnectionCountL(), processId = %d, threadId = %d, commsId = %d"),
				 this, aCommsIdKey.iProcessEntry.ProcessId().Id(),
				 aCommsIdKey.iThreadEntry.ThreadId().Id(),
				 &aCommsIdKey.iCommsId.Node() ); 

	(void) aCommsIdKey;
	
	// Suspect that this method is not required, connection counts are incremented without the request going via the state machine.

	/*
	TUint32 index = GetIndex(aCommsIdKey.iProcessEntry);
	(iState.operator[](index))->IncrementConnectionCountL(aCommsIdKey);
	*/
	User::Panic(KNetUpsPanic, KPanicMethodNotSupported);

	}

void CUpsStateMachine::DecrementConnectionCountL(TCommsIdKey& aCommsIdKey)
	{
	__FLOG_4(_L("CUpsStateMachine %08x:\t DecrementConnectionCountL(), processId = %d, threadId = %d, commsId = %d"),
				 this, aCommsIdKey.iProcessEntry.ProcessId().Id(),
				 aCommsIdKey.iThreadEntry.ThreadId().Id(),
				 &aCommsIdKey.iCommsId.Node() ); 

	TUint32 index = GetIndex(aCommsIdKey.iProcessEntry);
	(iState.operator[](index))->DecrementConnectionCountL(aCommsIdKey);
	}
		
void CUpsStateMachine::HandleUPSRequestCompletionL(TCommsIdKey& aCommsIdKey, MPolicyCheckRequestOriginator& aPolicyCheckRequestOriginator , TNetUpsDecision aNetUpsDecision)
	{
	__FLOG_5(_L("CUpsStateMachine %08x:\t HandleUPSRequestCompletionL(), processId = %d, threadId = %d, commsId = %d, netUpsDecision = %d"),
				 this, aCommsIdKey.iProcessEntry.ProcessId().Id(),
				 aCommsIdKey.iThreadEntry.ThreadId().Id(),
				 &aCommsIdKey.iCommsId.Node(),
				 aNetUpsDecision ); 

	TUint32 index = GetIndex(aCommsIdKey.iProcessEntry);
	(iState.operator[](index))->HandleUPSRequestCompletionL(aCommsIdKey, aPolicyCheckRequestOriginator, aNetUpsDecision);
	}

void CUpsStateMachine::HandleUPSErrorOnCompletionL(TCommsIdKey& aCommsIdKey, MPolicyCheckRequestOriginator& aPolicyCheckRequestOriginator, TInt aError)
	{
	__FLOG_5(_L("CUpsStateMachine %08x:\t HandleUPSErrorOnCompletionL(), processId = %d, threadId = %d, commsId = %d, aError = %d"),
				 this, aCommsIdKey.iProcessEntry.ProcessId().Id(),
				 aCommsIdKey.iThreadEntry.ThreadId().Id(),
				 &aCommsIdKey.iCommsId.Node(),
				 aError ); 

	TUint32 index = GetIndex(aCommsIdKey.iProcessEntry);
	(iState.operator[](index))->HandleUPSErrorOnCompletionL(aCommsIdKey, aPolicyCheckRequestOriginator, aError);
	}

void CUpsStateMachine::HandleProcessTermination(TProcessKey& aProcessKey)
	{
	__FLOG_2(_L("CUpsStateMachine %08x:\t HandleProcessTermination(), processId %d"),
				 this, aProcessKey.iProcessEntry.ProcessId().Id()); 

	TUint32 index = GetIndex(aProcessKey.iProcessEntry);
	(iState.operator[](index))->HandleProcessTermination(aProcessKey);
	}

void CUpsStateMachine::HandleThreadTermination(TThreadKey& aThreadKey)
	{
	__FLOG_3(_L("CUpsStateMachine\t HandleThreadTermination() processId = %d, threadId = %d"),
				 this, aThreadKey.iProcessEntry.ProcessId().Id(), aThreadKey.iThreadEntry.ThreadId().Id() ); 

	TUint32 index = GetIndex(aThreadKey.iProcessEntry);
	(iState.operator[](index))->HandleThreadTermination(aThreadKey);
	}
		
TInt32	CUpsStateMachine::ServiceId() 
	{
	return iServiceId;
	}

TNetUpsState CUpsStateMachine::GetState(CProcessEntry& aProcessEntry)
	{
	__FLOG_STATIC2(KNetUpsSubsys, KNetUpsComponent, _L("CUpsStateMachine\t GetState() processId = %d, netUpsState = %d"),
				 	aProcessEntry.ProcessId().Id(), aProcessEntry.NetUpsState()); 

	return aProcessEntry.NetUpsState();
	}

CNetUpsImpl& CUpsStateMachine::NetUpsImpl()
	{
	return iNetUpsImpl;	
	}

} // end of namespace

