// 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 the License "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:
// testcaseb2broot.cpp
// @internalComponent
// 
//

#include <e32std.h>
#include <e32std_private.h>
#include <u32std.h> 	// unicode builds
#include <e32base.h>
#include <e32base_private.h>
#include <e32cons.h>
#include <e32Test.h>	// RTest header
#include <e32ver.h>
#include <e32def.h>
#include <e32def_private.h>
#include <d32otgdi.h>		// OTGDI header
#include <d32usbc.h>		// USBCC header
#include "testcaseroot.h"
#include "b2bwatchers.h"



void CNotifyWatcherBase::RunL()
	{
	DisplayEvent(); 
	iHandler.HandleEvent(iWatchType, GetEventValue()); // report the event upwards

	IssueAgain(); 
	SetActive();
	}


CNotifyCollector::CNotifyCollector(TRequestStatus &aStatus)  : iStatusStep(aStatus) 
	{
	LOG_FUNC
	TTimeIntervalDays oneday(1);
	iTimeStarted.HomeTime();
	iTimeStarted += (oneday); // force all durations to produce a negative (invalid) value
	}


/***************************************************************
 *  ~CNotifyCollector
 */ 
CNotifyCollector::~CNotifyCollector()
	{
	LOG_FUNC
	
	ClearAllEvents(); // free event arrays

	iNotifyObjects.Close();
	iFailureEvents.Close();
	iRequiredEvents.Close();
	iReceivedEvents.Close();
	}


/* The test-case calls this to clear the events stored.
 * Both the expected and already recieved events get cleared, this method 
 * is typically called at the start of each test-step
 */
void CNotifyCollector::ClearAllEvents(TBool aClearRecieved/*=ETrue*/, TBool aClearRequired /*=ETrue*/)
	{
	//LOG_FUNC
	if (aClearRequired)
		{
		iRequiredEvents.Reset();
		iFailureEvents.Reset();
		}
	if (aClearRecieved)
		{
		iReceivedEvents.Reset();
		}
	}


/* Creates and starts all 4 observers
 * Note: The Watchdog does not get started, because it needs an interval
 */
void CNotifyCollector::CreateObserversL(COtgRoot &aOtgDriver)
	{
	LOG_FUNC
	TInt watchType;
	ASSERT(aOtgDriver.LddLoaded());
	
	for (watchType=EWatcherTimeouts; watchType < EWatcherInvalid; watchType++)
		{
		CNotifyWatcherBase *pWatcher=0;
		switch ((TWatcherNotifyType )watchType)
			{
			case EWatcherTimeouts:
				pWatcher = COtgWatchdogWatcher::NewL(*this,  EWatcherTimeouts, aOtgDriver);
				break;
			case EWatcherState:
				pWatcher = COtgStateWatcher::NewL(*this , EWatcherState, aOtgDriver);
				break;
			case EWatcherEvent:
				pWatcher = COtgEventWatcher::NewL(*this, EWatcherEvent, aOtgDriver);
				break;
			case EWatcherMessage:
				pWatcher = COtgMessageWatcher::NewL(*this, EWatcherMessage, aOtgDriver);
				break;
			case EWatcherPeripheralState:
				pWatcher = CPeripheralStateWatcher::NewL(*this, EWatcherPeripheralState, aOtgDriver);
				break;
			case EWatcherAConnectionIdle:
				pWatcher = CAConnectionIdleWatcher::NewL(*this, EWatcherAConnectionIdle, aOtgDriver);
				break;

			}
		// the TRequest object is added to scheduler in it's own constructor
		
		// add it to our list so we can kill them after the test.
		iNotifyObjects.Append(pWatcher);
		//LOG_VERBOSE3(_L("Added watcher type %d, TRequest= %08X.\n"), iType, (TInt)(&pWatcher->iStatus));	
		
		// start all watchers, except for the watchdog
		if (watchType != EWatcherTimeouts)
			{
			pWatcher->StartWatching(-1);
			}
		}
	test.Printf(_L("\n"));
	}


/* NOTE: OTG must still be loaded or else we cannot cancel the outstanding event watches here!
 */
void CNotifyCollector::DestroyObservers()
	{
	LOG_FUNC
	
	// Free the Watchers
	for (TInt idx=0; idx < iNotifyObjects.Count(); idx++)
		{
		LOG_VERBOSE2(_L(".. %d .."), idx);		
		delete iNotifyObjects[idx];	// they will call their own Cancel() methods
		}
	iNotifyObjects.Close();	
	}


void CNotifyCollector::AddRequiredNotification(TWatcherNotifyType aType, TInt aValue)
	{
	AddRequiredOrFailureNotification(aType, aValue, EFalse);
	}

void CNotifyCollector::	AddFailureNotification(const TWatcherNotifyType aType, TInt aValue)
	{
	AddRequiredOrFailureNotification(aType, aValue, ETrue);
	}

/* Checks that a watcher for the event exists, and then adds it to a list of events required for a PASS condition
 * The timout event does not get added to this list.
 * If the parameter is a time, the timer gets started
 * If aEventMeansFailure is set True, then the reception of the event will cause the current test step to fail
 */	
void CNotifyCollector::AddRequiredOrFailureNotification(TWatcherNotifyType aType, TInt aValue, TBool aEventMeansFailure)
	{
	CNotifyWatcherBase *pWatcher=0;
	TInt index=0;
	TBuf<MAX_DSTRLEN> aDescription;	

	// print a usefull debug message
	switch (aType)
		{
		case EWatcherTimeouts:
			break;
		case EWatcherState:
			COtgRoot::OtgStateString(static_cast<RUsbOtgDriver::TOtgState>(aValue), aDescription);
			LOG_VERBOSE3(_L("AddRequiredNotification() State %d '%S' wanted\n"), aValue, &aDescription);
			break;
		case EWatcherEvent:
			COtgRoot::OtgEventString(static_cast<RUsbOtgDriver::TOtgEvent>(aValue), aDescription);
			LOG_VERBOSE3(_L("AddRequiredNotification() Event %d '%S' wanted\n"), aValue, &aDescription);
			break;
		case EWatcherMessage:
			COtgRoot::OtgMessageString(static_cast<RUsbOtgDriver::TOtgMessage>(aValue), aDescription);
			LOG_VERBOSE3(_L("AddRequiredNotification() Message %d '%S' wanted\n"), aValue, &aDescription);
			break;
		case EWatcherPeripheralState:
			COtgRoot::PeripheralStateString(static_cast<TUint>(aValue), aDescription);
			LOG_VERBOSE3(_L("AddRequiredNotification() Peripheral State %d '%S' wanted\n"), aValue, &aDescription);
			break;
		case EWatcherAConnectionIdle:
			COtgRoot::AConnectionIdleString(static_cast<RUsbOtgDriver::TOtgConnection>(aValue), aDescription);
			LOG_VERBOSE3(_L("AddRequiredNotification() AConnectionIdle %d '%S' wanted\n"), aValue, &aDescription);
			break;

		}
	
	// Find the watcher if possible
	while (index < iNotifyObjects.Count())
		{

		TEST_ASSERTION(iNotifyObjects[index]!=NULL, _L("iNotifyObjects element gone!"));

		if (iNotifyObjects[index]->GetType() == aType)
			{
			pWatcher = iNotifyObjects[index];
			break;
			}
		index++;
		}
	
	TEST_ASSERTION(pWatcher!=NULL, _L("pWatcher=0!"));
	if (aType == EWatcherTimeouts)	
		{ // other watchers are already running, but we start the timer now
		pWatcher->StartWatching(aValue);
		}
	else
		{ // timeouts are not added to the Q
		TOtgObservedEvent evt(aType, aValue);	
		if(aEventMeansFailure)
			{
			iFailureEvents.Append(evt);
			}
		else
			{
			iRequiredEvents.Append(evt);
			}
		}
	// flag as pending
	iStatusStep = KRequestPending;
	iTimeStarted.HomeTime();
	}


/* Return the number of milliseconds since the last call to AddRequiredNotification()
 */
TInt CNotifyCollector::DurationElapsed()
	{
	TTime TimeEnd;
	TInt Millisec;

	TimeEnd.HomeTime();
	TTimeIntervalMicroSeconds ivlMicro(TimeEnd.MicroSecondsFrom(iTimeStarted));
	Millisec = (TInt)(ivlMicro.Int64())/1000;	// USB times are in uSec, but in ms for the user layer

	if (Millisec < 0)
		Millisec = -1; // TRUE for when the Notifiers are not yet being used.
	return(Millisec);				
	}


/* Search for an event in the received Q
 * @return :TRUE if the specified event has been received
 */
TBool CNotifyCollector::EventReceivedAlready(const TOtgObservedEvent& aEvent)
	{
	for (TInt idx=0; idx < iReceivedEvents.Count(); idx++)
		if (iReceivedEvents[idx] == aEvent)
			return(ETrue);
	return(EFalse);
	}

/* Search for an event in the failure event queue
 * @return :TRUE if the specified event does denote a failure
 */
TBool CNotifyCollector::IsFailureEvent(TOtgObservedEvent &aEvent)
	{
	for (TInt idx=0; idx < iFailureEvents.Count(); idx++)
		if (iFailureEvents[idx] == aEvent)
			return(ETrue);
	return(EFalse);
	}

/* @return 0 if the watcher has not yet been created. (for instance early in the test)
 */
CNotifyWatcherBase* CNotifyCollector::GetWatcher(TWatcherNotifyType aType)
	{
	CNotifyWatcherBase *pWatcher=0;
	// Find the watcher 
	TInt index=0;
	
	while (index < iNotifyObjects.Count())
		{

		TEST_ASSERTION(iNotifyObjects[index]!=NULL, _L("iNotifyObjects element gone!"));

		if (iNotifyObjects[index]->GetType() == aType)
			{
			pWatcher = iNotifyObjects[index];
			break;
			}
		index++;
		}

	return(pWatcher);
	}


/* Process the event. The OTG watchers are responsible for renewing themselves
 * but the Timer event does not renew
 */
void CNotifyCollector::HandleEvent(TWatcherNotifyType aType, TInt aValue)
	{
	if (aType == EWatcherTimeouts)
		{
		test.Printf(_L("Step timed out..(%dms).\n\n"), GetWatcher(aType)->GetEventValue());
		CompleteStep(KTestCaseWatchdogTO);
		return;
		}
	
	TOtgObservedEvent evt(aType, aValue);
	TInt start=0;
	iReceivedEvents.Append(evt);  // store incomming evt

	//	Check to see whether the event denotes a failure for this event
	if (IsFailureEvent(evt))
		{
		test.Printf(_L("This event denotes failure for this test\n"));
		CompleteStep(KTestCaseFailureEventReceived);
		return;
		}
	
	if (iRequiredEvents.Count())
		{
		// itterate all required events, search for each one in the incomming events list
		while (start< iRequiredEvents.Count())
			{
				//LOG_VERBOSE3(_L("Search for=[%d,%d] :"), 
				//					iRequiredEvents[start].GetType(), iRequiredEvents[start].GetValue());
				
				if (!EventReceivedAlready(iRequiredEvents[start]))
					return;	// missing still, continue
			start++; 
			}
		// found all the required events
		LOG_VERBOSE1(_L("Found all.\n"));
		CompleteStep(KErrNone);
		}
	else
		{
		test.Printf(_L("Warning : No required events!\n"));
		}	
	}

//	Complete the test step's TRequestStatus (checking it is currently KRequestPending
//	to try and avoid multiple completions).
//
void CNotifyCollector::CompleteStep(TInt aCompletionCode)
	{
	if(iStatusStep.Int() != KRequestPending)
		{
		test.Printf(_L("Can't complete step - not KRequestPending!\n"));
		}
	else
		{
		TRequestStatus *StatusStepPtr = &iStatusStep;
		User::RequestComplete(StatusStepPtr, aCompletionCode);
		}
	}

/****************************************************************************
 * COtg Watchdog Watcher
 */
COtgWatchdogWatcher *COtgWatchdogWatcher::NewL(MOtgNotificationHandler &wdHandler, 
												const TWatcherNotifyType aWatchType, 
												COtgRoot &aOtgRoot)
	{
	LOG_FUNC 
	COtgWatchdogWatcher* self = new (ELeave) COtgWatchdogWatcher(wdHandler, aWatchType, aOtgRoot);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}


void COtgWatchdogWatcher::ConstructL()
	{
	LOG_FUNC
	
	iTimer.CreateLocal();
	iIntervalMs = -1;
	}


void COtgWatchdogWatcher::StepExpired(TInt aInterval) 
	{
	LOG_FUNC ; 
	iHandler.HandleEvent(EWatcherTimeouts, aInterval) ; 
	}


void COtgWatchdogWatcher::RunL()
	{ 
	//LOG_FUNC 
	StepExpired(iIntervalMs); 
	}


void COtgWatchdogWatcher::StartTimer(TInt aIntervalMs)
	{
	LOG_FUNC ;

	iIntervalMs = aIntervalMs;	// save value for printing latter 
	if (IsActive()) //cancel the last timer we set, this is easier than cancelling it in each test-step
		{
		iTimer.Cancel();
		User::WaitForRequest(iStatus); // swallow it
		iTimer.After(iStatus, aIntervalMs*1000);
		}
	else
		{
		iTimer.After(iStatus, aIntervalMs*1000);
		SetActive();
		}
	LOG_VERBOSE2(_L("wd Timer %dms\n"), aIntervalMs)
	}	


/****************************************************************************
 * OTG Event/State/Message Watchers
 */
COtgMessageWatcher* COtgMessageWatcher::NewL(MOtgNotificationHandler &wdHandler, 
										const TWatcherNotifyType aWatchType, 
										COtgRoot &aOtgRoot)
	{
	LOG_FUNC 
	COtgMessageWatcher* self = new (ELeave) COtgMessageWatcher(wdHandler, aWatchType, aOtgRoot);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}


void COtgMessageWatcher::DisplayEvent()
	{
	TBuf<MAX_DSTRLEN> aDescription;	
	iOtgRoot.OtgMessageString(iMessage, aDescription);
	test.Printf(_L("Received Message %d '%S'\n"), iMessage, &aDescription);
	}


COtgStateWatcher* COtgStateWatcher::NewL(MOtgNotificationHandler &wdHandler, 
										const TWatcherNotifyType aWatchType, 
										COtgRoot &aOtgRoot)
	{
	LOG_FUNC 
	COtgStateWatcher* self = new (ELeave) COtgStateWatcher(wdHandler, aWatchType, aOtgRoot);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}


void COtgStateWatcher::DisplayEvent() 
	{
	//LOG_FUNC
	TBuf<MAX_DSTRLEN> aDescription;	
	iOtgRoot.OtgStateString(iState, aDescription);
	test.Printf(_L("Received State %d '%S'\n"), iState, &aDescription);
	}


COtgEventWatcher* COtgEventWatcher::NewL(MOtgNotificationHandler &wdHandler, 
										const TWatcherNotifyType aWatchType, 
										COtgRoot &aOtgRoot)
	{
	//LOG_FUNC 
	COtgEventWatcher* self = new (ELeave) COtgEventWatcher(wdHandler, aWatchType, aOtgRoot);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void COtgEventWatcher::DisplayEvent()
	{
	TBuf<MAX_DSTRLEN> aDescription;	
	iOtgRoot.OtgEventString(iEvent, aDescription);
	test.Printf(_L("Received Event %d '%S'\n"), iEvent, &aDescription);
	}

CPeripheralStateWatcher* CPeripheralStateWatcher::NewL(MOtgNotificationHandler &wdHandler, 
														const TWatcherNotifyType aWatchType, 
														COtgRoot &aOtgRoot)
	{
	//LOG_FUNC 
	CPeripheralStateWatcher* self = new (ELeave) CPeripheralStateWatcher(wdHandler, aWatchType, aOtgRoot);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CPeripheralStateWatcher::DisplayEvent()
	{
	TBuf<MAX_DSTRLEN> aDescription;	
	iOtgRoot.PeripheralStateString(iPeripheralState, aDescription);
	test.Printf(_L("Peripheral State %d '%S'\n"), iPeripheralState, &aDescription);
	}

CAConnectionIdleWatcher* CAConnectionIdleWatcher::NewL(MOtgNotificationHandler &wdHandler, 
														const TWatcherNotifyType aWatchType, 
														COtgRoot &aOtgRoot)
	{
	//LOG_FUNC 
	CAConnectionIdleWatcher* self = new (ELeave) CAConnectionIdleWatcher(wdHandler, aWatchType, aOtgRoot);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CAConnectionIdleWatcher::RunL()
	{
	//	We need to override the RunL for this event type, as
	//	the semantics of the asynchronous function are somewhat
	//	different to the rest of the ones being serviced by 
	//	CNotifyWatcherBase.
	//
	//	In the case of QueueOtgConnectionNotification, the value
	//	passed in is updated *immediately* to reflect the current
	//	activity or otherwise of the connection (unlike the other
	//	async functions which update the value on completion).
	//	The completion in the case of QueueOtgConnectionNotification
	//	is used to indicate that the value has changed, and
	//	another request should be queued to pick up this new value.
	//
	//	The practical upshot of this is that the IssueAgain needs
	//	to happen before the event is displayed and handled...
	
	IssueAgain(); 
	DisplayEvent(); 
	iHandler.HandleEvent(iWatchType, GetEventValue()); // report the event upwards
	SetActive();
	}


void CAConnectionIdleWatcher::DisplayEvent()
	{
	TBuf<MAX_DSTRLEN> aDescription;	
	iOtgRoot.AConnectionIdleString(iAConnectionIdle, aDescription);
	test.Printf(_L("AConnectionIdle %d '%S'\n"), iAConnectionIdle, &aDescription);
	}
