/*
* 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:
*
*/

#include <hal.h>
#include <usbman.h>
#include <e32test.h>
#include <e32twin.h>
#include <e32def.h>
#include <c32comm.h>
#include <d32comm.h>
#include <f32file.h>
#include <e32cons.h>
#include <acmserver.h>
#include <AcmInterface.h>
#include <UsbClassUids.h>
#include <CUsbClassControllerPlugIn.h>
#include "CommonFramework.h"
#include <usb/acmconfig.h>

#include <e32std.h>
#include <test/testexecutelog.h>
#include <test/testexecutelogger.h>

#ifndef __WINS__
_LIT(KCommDriverName, "EUSBC");
#endif

RFs gFs;
RUsb usb;
CConsoleBase* tCon;
TPtrC16 KTestResult;

/**
 * Quick cleanup before exit
 */
static void Cleanup()
	{
	if(usb.Handle())
		{
		usb.Close();
		}
	gFs.Close();
	tCon = NULL;
	}

/**
 * Handy function for outputting the buffers length values whilst debugging.
 * @param CConsoleBase* a console for output of information and getting user input
 * @param RBuf& the buffer you want to check
 */
void PrintBuf(CConsoleBase* aCon, RBuf8& aBuf)
	{
	aCon->Printf(_L("aBuf.Length(): %d\t aBuf.MaxLength() %d.\n"), aBuf.Length(), aBuf.MaxLength());
	aCon->Getch();			
	}

/**
 * LoadDrivers
 * Attempts to load drivers for Usb. This only works on the device, the emulator will fail to load
 * correct drivers, so this is skipped for WINS builds
 */
static TInt LoadDrivers(CConsoleBase*  aCon)
	{
	TInt err = KErrNone;
	aCon->Printf(_L("loading LDD\n")); //leave this line outside of preprocessor if to avoid compiler warning.
#ifndef __WINS__
	err = User::LoadLogicalDevice(KCommDriverName);
	if(err != KErrNone && err != KErrAlreadyExists)
		return err;
#endif
	return err == KErrAlreadyExists ? KErrNone : err;
	}

/**
 * Attached a supplied RProperty to the AcmConfig published data
 * @param RProperty& aProp the RProperty to be attached to the published data
 * @return KErrNone on success or standard Symbian error code on failure
 */
void AttachPropertyL(RProperty& aProp)
	{
	User::LeaveIfError(aProp.Attach(KUidSystemCategory, KAcmKey));
	}

/**
 * Returns the number of acm functions currently available
 * @param RProperty aProp, a connected RProperty connected to the P&S data
 * @return TInt the number of functions currently available or a standard
 * Symbian error code.
 */
TInt GetCount(RProperty aProp)
	{
	TPckgBuf<TPublishedAcmConfigs> configBuf;
	TInt ret = aProp.Get(configBuf);
	if(ret == KErrNone)
		 {
		 ret = configBuf().iAcmCount;
		 }	 		 		 		 		 		 		 
	return ret;
	}

/**
 * Checks the function count against the expected value.
 * @param RProperty aProp, a connected RProperty connected to the P&S data
 * @param TInt8 aExpected the number of functions to test against.
 * @return KErrNone if count is expected. KErrGeneral if count differs
 * from the expected value. Otherwise standard Symbian error code  
 */
TInt CheckFunctionCount(RProperty aProp, TInt8 aExpected)
	{
	TInt ret = GetCount(aProp);
  	if(aExpected >=0) //check the expected count
  		{
  		if(ret != aExpected)
  			{
  			KTestResult.Set(_L("Check function count failed. "));
  			ret = KErrGeneral;
  			}
  		else
  			{
  			ret = KErrNone;	
  			}
  		}
  	else //we are checking for an error code 
  		{
  		if(ret == aExpected)
  			ret = KErrNone; //else return the error code
  		}
	return ret;
	}
	
	
//DisplayProperties is not strictly required by this test case but is 
//left in as it shows how to access all the AcmConfig properties in one
//convenient place.
/**
 * Displays a list of available acm fucntions with protocol numbers
 * @param RProperty aProp, a connected RProperty connected to the P&S data
 * @param CConsoleBase* aCon, console where output will be directed.
 */
void DisplayProperties(RProperty aProp, CConsoleBase* aCon)
	{
	TInt err;
	TPckgBuf<TPublishedAcmConfigs> configBuf;
	err = aProp.Get(configBuf);
	if(err != KErrNone)
		{
		aCon->Printf(_L("ERROR: Failed to get the published data as a buffer, code: %d\n\n"), err);
		}
	//purely to prove it can be done, create a TPublishedAcmConfig from the configBuf and use
	//it to display	the properties of the functions
	TPublishedAcmConfigs configs = configBuf();
	
	
	TInt version = configs.iAcmConfigVersion;
	TInt funcCount = configs.iAcmCount;
	
	aCon->Printf(_L("AcmConfig Version: %d\n"), version);
	for(int i=0;i<funcCount;i++)	
		{
		aCon->Printf(_L("Func Num: %d\tProtocol: %d\n"), i, configs.iAcmConfig[i].iProtocol);
		}
	aCon->Printf(_L("Done\n"));	
	}

/**
 * Checks the properties of an Acm function are as expected
 * @param RProperty aProp the attached RProperty to be checked.
 * @param TInt aFuncNum the function to check
 * @param TInt aVersion the version of the config info being used. This is the same for all functions
 * @param TInt aProtocol the expected protocol of this function
 * @return TInt ret. Expected KErrNone or standard Symbian error code.
 */
TInt CheckProperties(RProperty aProp, TInt aFuncNum, TInt aVersion, TUint8 aProtocol)
	{
	TInt ret = KErrNone;
	TPckgBuf<TPublishedAcmConfigs> configBuf;
	TInt err = aProp.Get(configBuf);
	if(err != KErrNone)
		{
		KTestResult.Set(_L("Check properties failed to retrieve the P&S data"));
		ret = KErrGeneral;
		}
	else
		{
		//Check the version number
		if(configBuf().iAcmConfigVersion != aVersion)
			{
			KTestResult.Set(_L("Check properties version check failed."));
			ret = KErrGeneral;
			}
		else
			{
    		if(configBuf().iAcmConfig[aFuncNum].iProtocol != aProtocol)
				{
				KTestResult.Set(_L("Check properties protocol check failed."));
				ret = KErrGeneral;
				}
			}
		}
	return ret;
	}

/**
 * Build a new test ROM configuration where the ACM class controller has 
 * an .ini file which creates multiple ACMs, with different protocols. 
 * Again, check that the P&S key is appropriately set before TryStart(), 
 * while started, and after TryStop()
 */	
TInt TestCase1L()
	{
	RArray<TInt> availablePersonalities;
	TRequestStatus status;
	RProperty prop;
	TInt err = KErrNone;
		
	err = usb.Connect();
	if(err)
		{
		KTestResult.Set(_L("RUsb::Connect failed."));
		return err;
		}
		
	//check to see if the P&S data exists already
	//It should do as usbman should have been started during system startup, 
	//but no functions should exist until RUsb::Start | RUsb::TryStart calls
	//are made.
	AttachPropertyL(prop);
	err = CheckFunctionCount(prop, 0);
	if(err)
		{
		KTestResult.Set(_L("Expected 0 function to exist at this time."));
		return GetCount(prop); //Error return the number of functions found
		}
	
	err = usb.GetPersonalityIds(availablePersonalities);
	if(err) //couldn't retrieve the personalities. 
		{
		KTestResult.Set(_L("Failed to retrieve the supported personalities for this device."));
		return err;
		}
	if(availablePersonalities.Count() == 0) //no personalities 
		{
		KTestResult.Set(_L("GetPersonalities returned 0 supported personalities."));
		return KErrGeneral;
		}
	usb.TryStart(availablePersonalities[0], status); //personality 0 should be the default personality
	User::WaitForRequest(status);
	if(status.Int())
		{
		KTestResult.Set(_L("RUsb::TryStart failed."));
		return status.Int(); //USB failed to start the personality
		}
	
	//now that usb has started we should be able to check the P&S data.
	//Our NumberOfAcmFunctions.ini defines 3 functions so we should 
	//have three funtions now
	DisplayProperties(prop, tCon);
	err = CheckFunctionCount(prop, 3);
	if(err)
		{
		KTestResult.Set(_L("Incorrect number of functions found."));
		return GetCount(prop);//Error return the number of functions found
		}
	
	//We've gotten here so presumably all is going well, so check the version and 
	//protcol numbers on the functions created.
	//NumberOfAcmFunctions.ini defines functions with protocols 1, 255 & 127. 
	TInt version = 1; //simply for clarity
	
	if(err)
		{
		KTestResult.Set(_L("Failed to attach property for CheckProperties."));
		return err;
		}
	
	err = CheckProperties(prop, 0, version, 1);
	if(err)
		{
		KTestResult.Set(_L("Properties check failed for function 1."));
		return err;
		}
	
	err = CheckProperties(prop, 1, version, 255);
	if(err)
		{
		KTestResult.Set(_L("Properties check failed for function 2."));
		return err;
		}
	
	err = CheckProperties(prop, 2, version, 127);
	if(err)
		{
		KTestResult.Set(_L("Properties check failed for function 3."));
		return err;
		}
	
	//Assuming we're still here everything is correct, so shutdown Usbman and check 
	//the P&S data is destroyed 
	usb.TryStop(status);
	User::WaitForRequest(status);
	if(status.Int())
		{
		KTestResult.Set(_L("RUsb::TryStop failed."));
		return status.Int();
		}
	
	//Check to see if the P&S data was destroyed
	err = CheckFunctionCount(prop, 0);
	if(err)
		{
		KTestResult.Set(_L("P&S data was not destroyed by stopping usb."));
		return GetCount(prop); //Error return the number of functions found
		}
		
	//We got here so "All is well!"
	KTestResult.Set(_L("Test Passed."));
	return KErrNone;
	}

void doExampleL(CConsoleBase *aCon)
    {
 
	TInt err;
	tCon = aCon;  
	err = LoadDrivers(aCon);
    if(err != KErrNone)
    {
    	aCon->Printf(_L("Failed to load device drivers\n"));
    	Cleanup();
    }
    
    //Connect to the file server
    gFs.Connect();
    TInt result = KErrNone;
    TRAPD(error, result = TestCase1L());
    if(error)
    	{
    	KTestResult.Set(_L("TestCase1L Leave occurred."));
    	result = error;
    	}
	aCon->Printf(_L("TestCase 1 results: %S Result: %d\n"), &KTestResult, result);
	Cleanup();
	}


