// 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:
// Test code for HCI Proxy class (adapted from TRFCOMM)
// 
//

#include <e32test.h>
#include <es_sock.h>
#include <f32file.h>
#include <bt_sock.h>
#include <c32comm.h>
#include "btmanclient.h"
#include "debug.h"
#include <btsdp.h>
#include <hciproxy.h>

#if defined (__WINS__)
#define PDD_NAME _L("ECDRV")
#define LDD_NAME _L("ECOMM")
#else  // __GCC32__
#define PDD_NAME _L("EUART1")
#define LDD_NAME _L("ECOMM")
// #define ETNA_PDD_NAME _L("EUART2") // for debugging over com2
#endif

// Test Ioctl definitions
//const static TUint KRFCOMMFConIoctl =	0x7FFFFFFF;
//const static TUint KRFCOMMFCoffIoctl =	0x7FFFFFFE;

TBuf8<1024> buf;
TBuf16<2048> display;

GLDEF_D RTest test(_L("HCI Proxy module tests"));

const TUint KEventTimeout = 5000000;


void LoadLDD_PDD()
	{
	TInt r;
	r=StartC32();
	if (r!=KErrNone && r!=KErrAlreadyExists)
		{
		test.Printf(_L("Failed %d!\n\r"),r);
		test(r==KErrNone);
		}
	else
		test.Printf(_L("Started C32\n"));
	test.Printf(_L("Loading PDD\n"));
	r=User::LoadPhysicalDevice(PDD_NAME);
	if (r!=KErrNone && r!=KErrAlreadyExists)
		{
		test.Printf(_L("Failed %d!\n\r"),r);
		test(r==KErrNone);
		}
	else 
		test.Printf(_L("Loaded LDD\n"));
	test.Printf(_L("Loading LDD\n"));
	r=User::LoadLogicalDevice(LDD_NAME);
	if (r!=KErrNone && r!=KErrAlreadyExists)
		{
		test.Printf(_L("Failed %d!\n\r"),r);
		test(r==KErrNone);
		}
	else
		test.Printf(_L("Loaded PDD\n"));
	}



void TestMenuL()
	{
	test.SetLogged(EFalse);
	test.Title();
	test.Start(_L("Loading RFCOMM Protocol Module"));

	// This function is essential!
	LoadLDD_PDD();
	// For some reason, you have to do the following to
	// ensure that the file server behaves properly.


	}

CConsoleBase* AutoSizeNewL(const TDesC& aTitle, TSize aSize)
	{
	// Try to create a console of our preferred size, otherwise we
	// guess the screen is too small and use its entirety.
	CConsoleBase* console = NULL;
	TRAPD(err, console = Console::NewL(aTitle, aSize));
	if (err != KErrNone)
		{
		// If we leave now it is not because of offscreen drawing.
		console = Console::NewL(aTitle, TSize(KConsFullScreen, KConsFullScreen));
		}
	return console;
	}

/******************************************************************/
class CVendorSpecificEventTimout;

class CDebugEventConsole : public CBase, private MVendorSpecificHciConduit
	{
	friend class CVendorSpecificEventTimout;
public:
	static CDebugEventConsole* NewL();

	~CDebugEventConsole();

private:
	void ConstructL();
	CDebugEventConsole();
	virtual void CommandCompleted(TInt aError);
	virtual TBool ReceiveEvent(TDesC8& aEvent, TInt aError);
	
	void KillTest();
	void StartVdTestCase();
	void VerifyVdTestCase(TUint aVdTcNum, const TDesC8& aData);

	static const TUint8 VendorDebugTC1[];
	static const TUint8 VendorDebugTC2[];
	static const TUint8 VendorDebugTC3[];
	static const TUint8 VendorDebugTC4[];
	
	typedef struct
		{
		const TUint8* iData;
		TUint iSize;
		}
	TVendorDebugTC;
	
	static const TVendorDebugTC VendorDebugTestCases[];
	static const TUint KNumVdTestCases;
	
	static TInt NextVendorDebugTestCaseCallbackFun(TAny *aPtr);

	CAsyncCallBack				iNextVdTcCallBack;
	
	//  If true, CommandCompleted will start Vendor Debug test case n+1 when n completes.
	TBool						iVdTestCasesRunChained;
	//  The number of currently executing Vendor Debug test case. 1-based.
	TUint						iVdTcNum;
	CConsoleBase*				iConsole;
	CHciExtensionConduit*		iConduit;
	CVendorSpecificEventTimout* iEventTimeout;
	};

NONSHARABLE_CLASS(CVendorSpecificEventTimout) : public CTimer
	{
public:
	virtual ~CVendorSpecificEventTimout();
	static CVendorSpecificEventTimout* NewL(CDebugEventConsole* aContext);
	virtual void CancelTimer();
	virtual void StartTimer();
private:
	void RunL();
	CVendorSpecificEventTimout(CDebugEventConsole* aContext);
	void ConstructL();
private:
	CDebugEventConsole* iClient;
	};


/******************************************************************/


const TUint KLastTestComplete = KMaxTUint;

const TUint8 CDebugEventConsole::VendorDebugTC1[] =
	{
	1, 2, 3,
	};

const TUint8 CDebugEventConsole::VendorDebugTC2[] =
	{
	0
	};

// 250 bytes. If you add 3 bytes of fixed parameters in the Command Completion Event,
// you get 253 bytes of data. We should be able to handle 255 bytes of data,
// but the conduit's ioctl buffer is too small for 255 bytes in Command Completion Events
// - the buffer was meant for commands and then abused for returned completion events,
// and it's 2 bytes too short for them (because you also get 2 bytes extra of packet headers).
const TUint8 CDebugEventConsole::VendorDebugTC3[] =
	{
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, //0xA, 0xB, 0xC, 0xD, 0xE
	};

const TUint8 CDebugEventConsole::VendorDebugTC4[] =
	{
	'i', 't', ' ', 'w', 'o', 'r', 'k', 's', '!'
	};

const CDebugEventConsole::TVendorDebugTC CDebugEventConsole::VendorDebugTestCases[] =
	{
	{ VendorDebugTC1, sizeof VendorDebugTC1 },
	{ VendorDebugTC2, sizeof VendorDebugTC2 },
	{ VendorDebugTC3, sizeof VendorDebugTC3 },
	{ VendorDebugTC4, sizeof VendorDebugTC4 }
	};

const TUint CDebugEventConsole::KNumVdTestCases =
	sizeof CDebugEventConsole::VendorDebugTestCases / sizeof CDebugEventConsole::VendorDebugTestCases[0];

	
CDebugEventConsole* CDebugEventConsole::NewL()
	{
	CDebugEventConsole* self=new (ELeave)CDebugEventConsole();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CDebugEventConsole::~CDebugEventConsole()
	{
	delete iConsole;
	delete iConduit;
	delete iEventTimeout;
	}

void CDebugEventConsole::ConstructL()
	{
	
	TUint8 Extension[] = //returns response event
			{
			0x99 //...DUMMY VALUE:	Insert extension byte sequence here...
			};
	
/*	TUint8 ActivateInfo[] = //returns response event and couple of extension events
			{
			99 //...DUMMY VALUE:	Insert activation byte sequence here...
			};
	
	TUint8 DeActivateInfo[] = //returns response event and couple of extension events
			{
			99 //...DUMMY VALUE:	Insert deactivation byte sequence here...
			};

	TUint8 ActivateSequence[] = //HCI extension events sent once per second until deactivation
			{
			99 //...DUMMY VALUE:	Insert sequence activation byte sequence here...
			};

	TUint8 DeActivateSequence[] = //deactivate ActivateSequence - no more events!
			{
			99 //...DUMMY VALUE:	Insert sequence deactivation byte sequence here...
			};
*/
	
	TBuf8<100> ExtensionCommand;
	ExtensionCommand.Append(Extension, sizeof(Extension));

//	TPtrC8 ExtensionCommand(Extension, sizeof(Extension));
//	TPtrC8 ExtensionCommand(ActivateInfo, 3);
//	TPtrC8 ExtensionCommand(DeActivateInfo, sizeof(DeActivateInfo));
//	TPtrC8 ExtensionCommand(ActivateSequence, sizeof(ActivateSequence));
//	TPtrC8 ExtensionCommand(DeActivateSequence, sizeof(DeActivateSequence));

	iConsole=AutoSizeNewL(_L("listening for Events..."),TSize(50,40));
	iConduit = CHciExtensionConduit::NewL(*this);
	iEventTimeout = CVendorSpecificEventTimout::NewL(this);

	//  Get the VendorSpecificDebug Command test cases going.
	//  They will run in chain, until each test case from VendorDebugTestCases[] has executed.
	iVdTestCasesRunChained = ETrue;
	iVdTcNum = 0;
	iConsole->Printf(_L("\"No Hardware Response\" Test Case\n"));
	_LIT8(KDummyString, "Dummy");
	iConduit->IssueCommandL(KVendorDebugOGF | 0x00, KDummyString());
	}

CDebugEventConsole::CDebugEventConsole()
	: iNextVdTcCallBack(TCallBack(NextVendorDebugTestCaseCallbackFun, this), CActive::EPriorityStandard)
	{
	}

void CDebugEventConsole::KillTest()
	{
	iConsole->Printf(_L("ALL TESTS HAVE COMPLETED!\nPRESS ANY KEY TO CLOSE TProxy\n"));
	iConsole->Getch();
	CActiveScheduler::Stop();
	return;
	}

void CDebugEventConsole::StartVdTestCase()
	{
	TBuf8<256> CmdDsc;
	const TVendorDebugTC& tc = VendorDebugTestCases[iVdTcNum-1];
	if (tc.iSize > 0)
		CmdDsc.Append(tc.iData, tc.iSize);	
	iConsole->Printf(_L("Starting VendorDebug Test Case number %d\n"), iVdTcNum);
	TRAPD(err, iConduit->IssueCommandExpectingCompleteEventL(KVendorDebugOGF | iVdTcNum, CmdDsc));
	if(err)
		{
		iConsole->Printf(_L("Starting VendorDebug Test Case returned error %d\n"),err);
		}

	}

void CDebugEventConsole::VerifyVdTestCase(TUint aVdTcNum, const TDesC8& aData)
	{
	const TVendorDebugTC& tc = VendorDebugTestCases[aVdTcNum-1];
	const TUint KMaxDataSize		= 255;
	const TUint KCommandHeaderSize	= 2;
	const TUint KFixedParamSize		= 3;
	TUint sizeField     = (tc.iSize + KFixedParamSize < KMaxDataSize) ?
						  (tc.iSize + KFixedParamSize) : KMaxDataSize;

	for (int i = 0; i < aData.Length(); i++)
		{
		iConsole->Printf(_L("Received CCE: %x\n"), aData[i]);
		}
	for (int i = 0; i < tc.iSize; i++)
		{
		test(aData[i] == tc.iData[i]);
		}
	return;
	}

TInt CDebugEventConsole::NextVendorDebugTestCaseCallbackFun(TAny* aPtr)
	{
	CDebugEventConsole* self = static_cast<CDebugEventConsole*>(aPtr);
	self->StartVdTestCase();
	return 0;
	}

void CDebugEventConsole::CommandCompleted(TInt aError)
	{
	iConsole->Printf(_L("Cmd Complete: %d\n"), aError);

	TPtrC8 data;
	iConduit->CommandCompleteEventData(data);

	VerifyVdTestCase(iVdTcNum, data);
	
	if (iVdTestCasesRunChained && iVdTcNum < KNumVdTestCases)
		{
		iVdTcNum++;
		iNextVdTcCallBack.CallBack();
		}
	else
		{
		iVdTcNum = KLastTestComplete;
		iConduit->WaitForEvent(); //now see if we have any events queued
		iEventTimeout->StartTimer();
		}
	}

TBool CDebugEventConsole::ReceiveEvent(TDesC8& aEvent, TInt aError)
	{
	iConsole->Printf(_L("Rcvd Event!\n  err %d Length %d\n"), aError, aEvent.Length());
	for (int i = 0; i < aEvent.Length(); i++)
		{
		iConsole->Printf(_L("Received Dbg Evnt: %x\n"), aEvent[i]);
		}
	iEventTimeout->StartTimer(); // restart evnt timeout
	return ETrue; // Keep receiving events
	}

/******************************************************************/
 
CVendorSpecificEventTimout::CVendorSpecificEventTimout(CDebugEventConsole* aContext) : CTimer(EPriorityStandard),
	iClient(aContext)
	{
	}

CVendorSpecificEventTimout::~CVendorSpecificEventTimout()
	{
	CancelTimer();
	}


CVendorSpecificEventTimout* CVendorSpecificEventTimout::NewL(CDebugEventConsole* aContext)
	{
	CVendorSpecificEventTimout* self = new (ELeave) CVendorSpecificEventTimout(aContext);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
	}

void CVendorSpecificEventTimout::ConstructL()
	{
	CTimer::ConstructL();
	CActiveScheduler::Add(this);
	}

void CVendorSpecificEventTimout::CancelTimer()
	{
	if(IsActive())
		{
		CActive::Cancel();
		}
	}

void CVendorSpecificEventTimout::StartTimer()
	{
	if(IsActive())
		{
		CActive::Cancel();
		}
	After(KEventTimeout);
	}



void CVendorSpecificEventTimout::RunL()
	{
	iClient->KillTest();
	}


/******************************************************************/

void DoEventListenerL()
	{
	CActiveScheduler *exampleScheduler = new (ELeave) CActiveScheduler();
	CleanupStack::PushL(exampleScheduler);
	CActiveScheduler::Install(exampleScheduler); 

	CleanupStack::PushL(CDebugEventConsole::NewL());

/*	TInt count=0;
	while(ETrue)
		{
		User::After(1000000);
		myConsole->Printf(_L("Number %d\n"),++count);
		}
*/
	CActiveScheduler::Start();

	CleanupStack::PopAndDestroy(2); // active shed, myDebug
	}


TInt AnotherThreadFunction(TAny*)
	{
	CTrapCleanup* cleanupStack=CTrapCleanup::New();
	TRAPD(err,DoEventListenerL());
	delete cleanupStack;
	return err;
	}

TInt E32Main()
	{

	CTrapCleanup* cleanupStack=CTrapCleanup::New();

//	RThread thread;
//	thread.Create(_L("Plim"),AnotherThreadFunction,KDefaultStackSize,KMinHeapSize,0x15000,(TAny*)0);
//	thread.Resume();

	TRAP_IGNORE(TestMenuL());	//	Ignore err
	TRAPD(err, DoEventListenerL());

//	thread.Close();

	delete cleanupStack;
   	
	return err;
	}

