// Copyright (c) 2005-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:
// Implementation of CSchStartupStateMgr class which connects to
// the Domain manager to keep aware of the current start-up
// state and distributes the state changes to registered observers.
// 
//

/**
 @file
 @internalComponent
*/

#include "SchSSAMan.h"
#include "SchLogger.h"

/**
constructor of CSchStartupStateMgr
@internalComponent
*/
CSchStartupStateMgr::CSchStartupStateMgr(TDmHierarchyId aHierarchyId, TDmDomainId aDomainId) :
	CDmDomain(aHierarchyId,aDomainId),
	iCurrentStartupState(EStartupStateUndefined)
	 {
	 }
	 
/**
destructor of CSchStartupStateMgr
@internalComponent
*/
CSchStartupStateMgr::~CSchStartupStateMgr()
	{
	Cancel();
	iObserverList.Reset();
	}
	
/**
Getter
@internalComponent
@return the current startup state
*/
TStartupStateIdentifier CSchStartupStateMgr::CurrentStartupState()
	{
	return iCurrentStartupState;
	}
	
/**
updates the MSchStartupStateObserver objects.
@internalComponent
*/
void CSchStartupStateMgr::UpdateStateAwareObjectsL(TStartupStateIdentifier aKnownState)
	{
	for (TInt i = 0; i < iObserverList.Count(); i++)
		{
		iObserverList[i]->ProcessSSAEventL(aKnownState);
		}
	}
	
/**
This method takes a a startup state which can be user defined
state and maps it to a known startup state (KSS). KSS are
Symbian defined states: undefined, critical static, critical dynamic,
and non-critical.
User defined states are refered to as USS - unknown startup state.

@internalComponent
*/
TStartupStateIdentifier CSchStartupStateMgr::GetKnownStartupState(TDmDomainState aStartupState)
	{
	TStartupStateIdentifier knownStartupState = iCurrentStartupState;
	
	if (aStartupState >= EStartupStateNonCritical)
		{
		knownStartupState = EStartupStateNonCritical;
		}
	else if(aStartupState >= EStartupStateCriticalDynamic)
		{
		knownStartupState = EStartupStateCriticalDynamic;
		}
	else if(aStartupState >= EStartupStateCriticalStatic)
		{
		knownStartupState = EStartupStateCriticalStatic;
		}
	else 
		{
		knownStartupState = EStartupStateUndefined;
		}
	
	return knownStartupState;
	}

/**
Register the MSchStartupStateObserver object with the CServerStartupMgr.
@internalComponent
@param aObs the pointer to the MStartupStateObserver object to be registered with CServerStartupMgr.
*/
void CSchStartupStateMgr::RegisterObserverL(const MSchStartupStateObserver* aObs)
	{
	User::LeaveIfError(iObserverList.Append(aObs));
	}

/**
Handle the error if RunL leaves. Just tell the observers that
we have reached the final state.

@internalComponent
@param aError error code generated by the RunL(), not used here.
@return KErrNone to avoid CActiveScheduler to panic.
*/
TInt CSchStartupStateMgr::RunError(TInt /*aError*/)
	{
	LOGSTRING("CSchStartupStateMgr::RunL() leaves, set SS to final state.");
	TRAP_IGNORE(UpdateStateAwareObjectsL(KSchFinalStartupState));
	return KErrNone;
	}
	
/**
Finsish constructing the CSchStartupStateMgr and start
interacting with Domain Manager to receive startup state
changes. 
All observers should have been registered before calling this method

@internalComponent
*/
void CSchStartupStateMgr::InitialiseL()
	{
	TRAPD(err, CDmDomain::ConstructL());
	if (err != KErrNone)
		{
		// the ConstructL leaves, go to final state
		LOGSTRING2("CDmDomain::ConstructL leaves with %d. Goto final state", err);
		UpdateStateAwareObjectsL(KSchFinalStartupState);
		iCurrentStartupState = KSchFinalStartupState;
		return;
		}
			
	// Typical pattern of using P&S is to subscribe first then get
	// current state. Example implementation given
	// in SSA Adaptation How-to also does it this way.
	RequestTransitionNotification();
	
	// get the start up state from the Domain Manager.
	TDmDomainState rawstate = GetState();

	// rawstate may be user defined. Map to known states.
	TStartupStateIdentifier nextKnownState = GetKnownStartupState(rawstate);
		
	// NB: nextKnownState can be KStartupStateUndefined for 2 reasons.
	// One is rawstate == 0 which we must check first. Second:
	// rawstate is user defined values lower than critical static. In
	// the second case we must wait for next state change.
	if (rawstate == EStartupStateUndefined || 
		nextKnownState == KSchFinalStartupState)
		{
		// If either something wrong with DM or final state is reached,
		// go to final state
		iCurrentStartupState = KSchFinalStartupState;
		UpdateStateAwareObjectsL(KSchFinalStartupState);
		Cancel();
		return;
		}
			
	iCurrentStartupState = nextKnownState;
	}

/**
Executed when the startup state change is done, it does the 
same thing as the method InitialiseL() does
@internalComponent
*/
void CSchStartupStateMgr::RunL()
	{
	if(iStatus != KErrNone) // something wrong with the RequestTransitionNotification().
		{
		AcknowledgeLastState(iStatus.Int()); //Acknowledge the domainmanager in case of leaving, this to avoid the acknowledgement in RunError()
		User::LeaveIfError(iStatus.Int()); //RunError will handle this.
		}

	RequestTransitionNotification();

	TDmDomainState rawstate = GetState();

	// Must first check the case rawstate == undefined and deal with it here.
	// GetKnowStartupState maps 1 to 15 to EStartupStateUndefined
	// which means a SS before critical static. It does not mean go
	// to final state.

	//If the rawstate is EStartupStateUndefined there must be sth wrong.
	if(rawstate == EStartupStateUndefined) 
		{
		Cancel();
		AcknowledgeLastState(KErrBadHandle);
		User::Leave(KErrBadHandle); //RunError will handle this.
		}

	// get the known state			
	TStartupStateIdentifier nextKnownState = GetKnownStartupState(rawstate);
	
	//Tell domain manager that we have processed the last state change before starting any time consuming work.
	AcknowledgeLastState(KErrNone);
	
	//Sleep 2 tickperiods this allows the acknowledgement reach the domain manager.
	TTimeIntervalMicroSeconds32 tickPeriod;
	UserHal::TickPeriod(tickPeriod);	
	User::After(2*tickPeriod.Int()); 

	// Schsvr only want to know transition to non-critical.
	// Ignore all states below that level.
	if (iCurrentStartupState < KSchFinalStartupState && nextKnownState == KSchFinalStartupState)
		{
		UpdateStateAwareObjectsL(nextKnownState);
		}
	
	iCurrentStartupState = nextKnownState;	
	
	//do not request transition notification if we have reached the last state
	if(iCurrentStartupState == KSchFinalStartupState)
		{
		Cancel();
		}
	}
