// Copyright (c) 2003-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:
//

#include <cflog.h>
#include <cfshared.h>
using namespace CommsFW;
#include <cfmsgs.h>
#include <rsshared.h>
#include "../RootServerTest.h"

__CFLOG_STMT(_LIT8(KLogTestCPM, "TestCPM");) // subsystem name
_LIT( KTestCpmPanic, "TestCpm" );
const TText8* const KSubModules[] = { _S8("Test Protocol1"), _S8("Test Protocol2"), _S8("Test Protocol3") };


class CDeathTimer : public CTimer
	{
public:
	static CDeathTimer* NewL(TInt aAfter)
		{
		CDeathTimer* self = new(ELeave) CDeathTimer;
		CleanupStack::PushL(self);
		self->ConstructL();
		CleanupStack::Pop(self);
		CActiveScheduler::Add(self);
		self->After(aAfter);
		return self;
		}
protected:
	CDeathTimer() : CTimer(EPriorityStandard)
		{
		}
	void RunL()
		{
		// Go not gracefully into that good night
		User::Invariant();
		}
	};

class CChannelHandler;
class CTestModule : public CBase
	{
public:
	enum {EPriority=1000}; //< priority of this Active Object
public:
	~CTestModule();
	void CFMessageReceived(TCFMessage& aMsg);
	static CTestModule* NewL(TCFModuleInfo* aStartupArgs);
	void ConstructL(TCFModuleInfo* aStartupArgs);
	void LoggedAfter(TInt aAfterMS, const TDesC8& aDescription);
private:
	CChannelHandler* iChannelHandler;
	TTestModuleIniDataContainer iDelayTimes;
	TInt iDiscoverPos;
	CDeathTimer* iDeathTimer;
	};

class CChannelHandler:public CActive
	{
public:
	static CChannelHandler* NewL(RCFChannel::TMsgQueues aRxQueues, 
									RCFChannel::TMsgQueues aTxQueues, CTestModule* aModule);
	void RunL();
	TInt Send(TCFMessage);
	~CChannelHandler();
protected:
	virtual void DoCancel();
private:
	void ConstructL(RCFChannel::TMsgQueues aRxQueues, 
					RCFChannel::TMsgQueues aTxQueues, CTestModule* aModule);
	CChannelHandler();
private:
	CTestModule* iModule;
	RCFChannelPair iChannelPair;
	};


//
// CChannelHandler class definitions
//

CChannelHandler::CChannelHandler():CActive(CActive::EPriorityStandard)
	{
	CActiveScheduler::Add(this);
	}

CChannelHandler* CChannelHandler::NewL(RCFChannel::TMsgQueues aRxQueues, 
									RCFChannel::TMsgQueues aTxQueues, CTestModule* aModule)
	{
	CChannelHandler* pHandler = new (ELeave) CChannelHandler;
	pHandler->ConstructL(aRxQueues, aTxQueues, aModule);
	return pHandler;
	}

void CChannelHandler::ConstructL(RCFChannel::TMsgQueues aRxQueues, 
									RCFChannel::TMsgQueues aTxQueues, CTestModule* aModule)
	{
	iModule = aModule;
	iChannelPair.CreateSend(aTxQueues);
	iChannelPair.CreateRecv(aRxQueues);
	SetActive();
	iChannelPair.NotifyDataAvailable(*this);
	}

void CChannelHandler::DoCancel(void)
	{
	iChannelPair.CancelDataAvailable();
	}

TInt CChannelHandler::Send(TCFMessage aMsg)
	{
	TRequestStatus status;
	iChannelPair.NotifySpaceAvailable(*this);
	User::WaitForRequest(status);
	return iChannelPair.Send(aMsg);
	}

void CChannelHandler::RunL()
	{

	if (iStatus != KErrNone)
		{
		User::Invariant();
		}

	// No error. Take the message from the channel.
	TCFMessage msg;
	TInt err = iChannelPair.Receive(msg);
	if (KErrNone == err)
		{
		//Pass the message
		iModule->CFMessageReceived(msg);
		//Pend again
		SetActive();
		iChannelPair.NotifyDataAvailable(*this);
		}
	else
		{
		User::Invariant();
		}
	}

CChannelHandler::~CChannelHandler()
	{
	if(IsActive())
		Cancel();
	iChannelPair.CloseNonGracefully();
	}	


//
// implementation of CTestServer
//

CTestModule* CTestModule::NewL(TCFModuleInfo* aStartupArgs)
/**
 * Create a new CCommServer.
 *
 * This is not a normal 2 phase contruction. If the allocation of the object
 * fails we will panic with -4 which means that something is very wrong.
 */
	{
	CTestModule* pS;
	CleanupStack::PushL(pS = new CTestModule);
	__ASSERT_ALWAYS(pS!=NULL, User::Panic( KTestCpmPanic, KErrNoMemory));
#ifdef SYMBIAN_C32ROOT_API_V3
	pS->ConstructL(aStartupArgs);
#else
	TRAPD(r, pS->ConstructL(aStartupArgs));
	__ASSERT_ALWAYS(r==KErrNone, User::Panic( KTestCpmPanic, KErrNoMemory));
#endif
	CleanupStack::Pop(pS);
	return pS;
	}


void CTestModule::ConstructL(TCFModuleInfo* aStartupArgs)
	{
	if (aStartupArgs->iIniData != NULL)
		{
		if (aStartupArgs->iIniData->Length() == sizeof(iDelayTimes))
			{
			TTestModuleIniData pkg;
			pkg.Copy(*(aStartupArgs->iIniData));
#if defined(SYMBIAN_C32ROOT_API_V3)
			delete aStartupArgs->iIniData;
#endif

			iDelayTimes = pkg.iParams;

			if(iDelayTimes.iInitDelay == EXIT_BEFORE_RENDEZVOUS)
				User::Leave(KErrGeneral);
			else if(iDelayTimes.iInitDelay > 0)
				LoggedAfter(iDelayTimes.iInitDelay, _L8("Init"));
			else if(iDelayTimes.iInitDelay == PANIC_BEFORE_RENDEZVOUS)
				User::Panic(_L("TESTCPM"), 1);

			__CFLOG_5( KLogTestCPM, KLogCode, 
				_L8("TestCpm[%LU] - iIniData delays (msec): discoveryDelay %d bindDelay %d unbindDelay %d shutdownDelay %d"), 
						 RThread().Id().Id(), iDelayTimes.iDiscoveryDelay, iDelayTimes.iBindDelay,
						 iDelayTimes.iUnbindDelay, iDelayTimes.iShutdownDelay);

			iChannelHandler = CChannelHandler::NewL(aStartupArgs->iRxQueues, aStartupArgs->iTxQueues, this);

			RThread::Rendezvous(KErrNone);

			if(iDelayTimes.iInitDelay == PANIC_AFTER_RENDEZVOUS)
				User::Panic(_L("TESTCPM"), 1);

			}
		else
			{
			__CFLOG_1( KLogTestCPM, KLogCode, _L("TestCpm[%LU] - iIniData mismatched"), RThread().Id().Id() );
#if defined(SYMBIAN_C32ROOT_API_V3)
			delete aStartupArgs->iIniData;
#endif
			ASSERT(0);
			}
		}
	else
		{
		__CFLOG_1( KLogTestCPM, KLogCode, _L("TestCpm[%LU] - No iIniData"), RThread().Id().Id() );
		ASSERT(0);
		}
	}

void CTestModule::CFMessageReceived(TCFMessage& aMsg)
	{
	
	//It is called when a new message arrives from incomming channel
	switch (aMsg.Code())
		{
		case TCFCommsMessage::ECodeDiscover:
			{
			// now wait for delay
			__CFLOG(KLogTestCPM, KLogCode, _L("TestCpm Discover message received"));

			LoggedAfter(iDelayTimes.iDiscoveryDelay, _L8("Discovery"));
			const TCFDiscoverMsg& msg = reinterpret_cast<const TCFDiscoverMsg&>(aMsg);
			// we return our sub-modules one at a time to make sure the RootServer copes with iterative discovery
			const TInt numSubModules = sizeof(KSubModules) / sizeof(KSubModules[0]);
			ASSERT(msg.Size() > 0);
			ASSERT(iDiscoverPos < numSubModules);
			if(msg.Reset())
				iDiscoverPos = 0;
			TCFSubModuleName name;
			const TText8* subMod = KSubModules[iDiscoverPos++];
			name.Copy(subMod);
			msg.SubModuleNames()[0] = name;
			TBool discoveryEnded = (iDiscoverPos == numSubModules);
			TCFDiscoverRespMsg diskresp(msg.Identifier(), 1, !discoveryEnded);
			TInt err = iChannelHandler->Send(diskresp);
			if(KErrNone != err)
				{
       		    User::Invariant();
				}
			if(discoveryEnded && iDelayTimes.iDeathDelay != NO_PREMATURE_DEATH)
				{
				TRAP(err, iDeathTimer = CDeathTimer::NewL(iDelayTimes.iDeathDelay * 1000));
				ASSERT(iDeathTimer);
				}
				
			}
			break;

		case TCFCommsMessage::ECodeBind:
			{
			__CFLOG(KLogTestCPM, KLogCode, _L("TestCpm Bind message received"));
			// now wait for delay
			LoggedAfter(iDelayTimes.iBindDelay, _L8("Bind"));
			const TCFBindMsg& msg = reinterpret_cast<const TCFBindMsg&>(aMsg);
			TCFBindCompleteMsg resp(msg.Identifier(), KErrNone);
			TInt err = iChannelHandler->Send(resp);
			if(KErrNone != err)
				{
				User::Invariant();
				}
			}
			break;

		case TCFCommsMessage::ECodeUnbind: 
			{
			__CFLOG(KLogTestCPM, KLogCode, _L("TestCpm UnBind message received"));
			// now wait for delay
			LoggedAfter(iDelayTimes.iUnbindDelay, _L8("Unbind"));
			const TCFUnbindMsg& msg = reinterpret_cast<const TCFUnbindMsg&>(aMsg);
			TCFUnbindCompleteMsg resp(msg.Identifier(), KErrNone);
			TInt err = iChannelHandler->Send(resp);
			if(KErrNone != err)
				{
				User::Invariant();
				}
			}
			break;

		case TCFCommsMessage::ECodeShutdown:
			// now wait for delay
			__CFLOG(KLogTestCPM, KLogCode, _L("TestCpm Shutdown message received"));
			LoggedAfter(iDelayTimes.iShutdownDelay, _L8("Shutdown"));

			CActiveScheduler::Stop();
			break;
		default:;
			User::Invariant();
		}
	}

	
CTestModule::~CTestModule()
	{
	delete iChannelHandler;
	delete iDeathTimer;
	}

void CTestModule::LoggedAfter(TInt aAfterMS, const TDesC8& aDescription)
	{ 
	(void) aDescription;
	__CFLOG_2(KLogTestCPM, KLogCode, _L8("TestCpm: %d ms %S pause"), aAfterMS, &aDescription);
	User::After(aAfterMS * 1000);
	__CFLOG_1(KLogTestCPM, KLogCode, _L8("TestCpm: %S pause complete"), &aDescription);
	}


//---------------

void DoModuleThreadL(TAny * aArg)
	{
	TCFModuleInfo* args = reinterpret_cast<TCFModuleInfo*>(aArg);
	
	CActiveScheduler*  scheduler = new (ELeave) CActiveScheduler;
	CleanupStack::PushL(scheduler);
	CActiveScheduler::Install(scheduler); 

	CTestModule *pTm = CTestModule::NewL(args);

    __CFLOG_1( KLogTestCPM, KLogCode, _L("TestCpm[%LU] - Completed rendezvous."), RThread().Id().Id() );

	CActiveScheduler::Start();

	__CFLOG_1( KLogTestCPM, KLogCode, _L("TestCpm[%LU] - Exiting thread function"), RThread().Id().Id() );

	delete pTm;
	CActiveScheduler::Install(NULL);
	CleanupStack::PopAndDestroy(scheduler);
	}

EXPORT_C TInt ModuleThread(TAny * aArg)
	{
	CTrapCleanup* cleanup = CTrapCleanup::New();
	if(!cleanup)
		return KErrNoMemory;	

	TRAPD(ret, DoModuleThreadL(aArg))
	if (ret!=KErrNone)
		{
		__CFLOG(KLogTestCPM, KLogCode, _L("ModuleThread failed to create TestCpm"));
		}

	delete cleanup;
	return ret;
	}



