// 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:
// UTrace Performance Tests CSV output generator.
//



/**
 @file te_perfcsvgenerator.cpp
 @internalTechnology
 @prototype
*/

#include "te_perfcsvgenerator.h"
#include "te_ostv2integsuite_defs.h"
#include "te_perfapinames.h"


//Punctuation symbols required for generating csv files.


CUptCsvGenerator::CUptCsvGenerator()
	{
	/*Constructor*/
	}

CUptCsvGenerator::~CUptCsvGenerator()
	{
	/*Destructor*/
	}

/** This user-side method uses the RFs and RFile Session methods to open a file specified by the user, 
if the file already exists, it will be appended or overwritten as per the user's specification.
The file information is stored in the private data member iCSVFile.
@param aFileName the constant descriptor containing the filepath as specified by the user.
@param aAppend is the TBool controlling whether files should be appended to or overwritten
				ETrue=Append, EFalse=Overwrite
@return KErrNone if command was prepared correctly and a system wide error code otherwise.
 */
TInt CUptCsvGenerator::Open(const TDesC& aFileName, const TBool& aAppend)
	{

	iCsvFileSession.Connect();
	//if the user has set for the file to be overwritten if it already exists
	//then this part with ensure this is carried out with write permissions
	if(aAppend==EFalse)
		{
		User::LeaveIfError(iCsvFile.Replace(iCsvFileSession, aFileName, EFileWrite));	
		}
	//otherwise, the file is opened and appended to if it already exists
	//and if it doesnt exist, the file is created with write perimission with the user defined filename.
	else
		if(iCsvFile.Open(iCsvFileSession, aFileName ,EFileWrite)!=KErrNone)
			{
			User::LeaveIfError(iCsvFile.Create(iCsvFileSession, aFileName, EFileWrite));			
			}
			
	//NB!!!! may need to put in check if file is already open if this is going to be run on emulator 
	//(not that that matters for performance tests). Not an issue on device.
	return KErrNone;

	}

/** This user-side method uses the RFs and RFile Session methods to close the file specified by the user in the OpenL()
method above and described by the private data member iCSVFile.
@return KErrNone if command was prepared correctly and a system wide error code otherwise.
 */
TInt CUptCsvGenerator::Close()
	{
	//close the csv file
	iCsvFile.Close();
	iCsvFileSession.Close();	
	
	return KErrNone;
	}
	
/** This user-side method uses the RFile Session methods to find the end of the file described by iCSVFile and 
appends a newline character.
@return KErrNone if command was prepared correctly and a system wide error code otherwise.
 */
TInt CUptCsvGenerator::WriteNewLine() 
	{
	TInt filesize;
	
	// find end of this file, and append the newline data field from here.
	iCsvFile.Size(filesize);
	iCsvFile.Seek(ESeekStart, filesize);
	User::LeaveIfError(iCsvFile.Write(KCsvNewLine));
	
	return KErrNone;				
	}

/** This user-side method uses the RFile Session methods to find the end of the file described by iCSVFile.
It then appends performance data metrics by element from the passed array, with each element separated by a comma character.
@param aPerformanceData is the constant array of performance data stored as TInt32
@return KErrNone if command was prepared correctly and a system wide error code otherwise.
 */
void CUptCsvGenerator::WriteL(const RArray<TInt64>& aPerformanceData)
	{
	
	// find end of this file, and append the passed data from here.
	TInt filesize;
	iCsvFile.Size(filesize);
	iCsvFile.Seek(ESeekStart, filesize);
	
	RBuf8 buf; 
	CleanupClosePushL(buf);
	//create a buf large enough to contain the passed data and comma separators
	TInt numbytes = 2*aPerformanceData.Count()*sizeof(TInt64);
	buf.CreateL(numbytes);
	
	//for the number of elements in the passed array:- append each element, separated by a comma, to the buffer
	for(TInt i=0; i!=aPerformanceData.Count(); i++) //could replace aPerformance.Count() with structure paramter Parameter.Count
		{
		buf.AppendNum(aPerformanceData[i]);
		//may reimplement this
		if(i!=aPerformanceData.Count()) 
			buf.Append(KCsvComma);  

		}
	
	//write the buffer to the given file	
	User::LeaveIfError(iCsvFile.Write(buf));
	
	//close and cleanup the heap objects
	buf.Close();
	CleanupStack::PopAndDestroy(&buf);

	}

/** This user-side method uses the RFile Session methods to find the end of the file described by iCSVFile.
It then appends performance data metrics by element from the passed array, with each element separated by a comma character.
@param aPerformanceData is the constant array of performance data stored as TDesC
@return KErrNone if command was prepared correctly and a system wide error code otherwise.
 */
void CUptCsvGenerator::WriteL(const RArray<TPtrC8>& aPerformanceConfig)
	{
	// find end of this file, and append the passed data from here.
	TInt filesize;
	iCsvFile.Size(filesize);
	iCsvFile.Seek(ESeekStart, filesize);
	
	RBuf8 buf; 
	CleanupClosePushL(buf);
	//create a buf large enough to contain the passed data and comma separators
	TInt numbytes = 5*aPerformanceConfig.Count()*sizeof(TPtrC8);
	buf.CreateL(numbytes);
		
	//for the number of elements in the passed array:- append each element, separated by a comma, to the buffer
	for(TInt i=0; i!=aPerformanceConfig.Count(); i++) //could replace aPerformance.Count() with structure paramter Parameter.Count
		{
		buf.Append(aPerformanceConfig[i]);
		//may reimplement this
		if(i!=aPerformanceConfig.Count()) 	
			buf.Append(KCsvComma);  

		}
	//write the buffer to the given file	
	User::LeaveIfError(iCsvFile.Write(buf));
	
	
	//close and cleanup the heap objects
	buf.Close();
	CleanupStack::PopAndDestroy(&buf);

	}


void CUptCsvGenerator::WriteApiNameL(const TInt aApiEnum)
	{
	// find end of this file, and append the passed data from here.
	TInt filesize;
	iCsvFile.Size(filesize);
	iCsvFile.Seek(ESeekStart, filesize);
		
	RBuf8 buf; 
	CleanupClosePushL(buf);
	//create a buf large enough to contain the passed data and comma separators
	TInt numbytes = 1024;//2*sizeof(TPtrC8);
	buf.CreateL(numbytes);
		
	//read the APIenum (which is the same as the first element of aPerformanceData)
	//use this enum value to write the name of the API being tested
	TApiNames getApiName;
	buf.Append(getApiName.GetApiIdString(aApiEnum));
	buf.Append(KCsvComma); 
	
	//write the buffer to the given file	
	User::LeaveIfError(iCsvFile.Write(buf));
		
		
	//close and cleanup the heap objects
	buf.Close();
	CleanupStack::PopAndDestroy(&buf);
		
	}

/** This user-side method writes the column titles (from the KLit string defined in the relevent test.cpp
    to the user-specified csv file, according to the parameters for aTestType.
@param aTestType is the enum identifier for the test in question
@return KErrNone if command was prepared correctly and a system wide error code otherwise.
 */	
TInt CUptCsvGenerator::WriteHeader(const TInt& aTestType)
	{
	RBuf8 buf; 
	CleanupClosePushL(buf);
	
	TInt numbytes = 0;

	//test which testtype is to be written and
	//append the header string to the buffer and write it to the csv file
	//create general KGeneralHeader? 
	//first identify which test
	switch(aTestType)
	{
	case 0:
		//KGeneralHeader=KApiCallHeader;
		//now create a buf large enough to contain the passed data and comma separators
		numbytes = sizeof(KApiCallHeader); 
		buf.CreateL(numbytes);
	
		//append the relevant header to the file
		buf.Append(KApiCallHeader);
	break;
	case 1:
	//now create a buf large enough to contain the passed data and comma separators
		numbytes = sizeof(KOutputPluginHeader); 
		buf.CreateL(numbytes);

		//append the relevant header to the file
		buf.Append(KOutputPluginHeader);
	break;
	case 2:
	//now create a buf large enough to contain the passed data and comma separators
		numbytes = sizeof(KOutputPluginHeader); 
		buf.CreateL(numbytes);

		//append the relevant header to the file
		buf.Append(KOutputPluginHeader);
	break;
	//case 3://to be implemented on next iteration
	//break;
	//case 4://to be implemented on next iteration
	//break;
	default:
		// NB: change this to an empty string
		//now create a buf large enough to contain the passed data and comma separators
		numbytes = sizeof(KApiCallHeader); 
		buf.CreateL(numbytes);

		//append the relevant header to the file
		buf.Append(KApiCallHeader);
	break;
	
	}
	
	//now create a buf large enough to contain the passed data and comma separators
	//numbytes = sizeof(KGeneralHeader); 
	//buf.CreateL(numbytes);
	
	//append the relevant header to the file
	//buf.Append(KGeneralHeader);		

	
	//append a newline to the buffer in preparation for the data
	buf.Append(KCsvNewLine);
	TInt startpos = 0;
	//go to the start of the file and write the buffer 
	iCsvFile.Seek(ESeekStart, startpos); //nb: put in check incase this overwrites data when appending to a file??
	User::LeaveIfError(iCsvFile.Write(buf));

	//close and cleanup the heap objects
	buf.Close();
	CleanupStack::PopAndDestroy(&buf);
	
	return KErrNone;
	}

/** This sanity test method is executed at the start of the test run to verify that UPT methods in
this class are stable before any of the performance tests are carried out

// what tests does it do?
 
@return KErrNone if command was prepared correctly and system wide error code otherwise.
 */
TInt CUptCsvGenerator::Test()
	{
	//define filepaths for the test csv files according to the test platform.
#ifdef __WINSCW__
	_LIT(KTestFileAppend, "c:\\te_CSVoutputfileAppend.csv");
	_LIT(KTestFileOverwrite, "c:\\te_CSVoutputfileOverwrite.csv");
#else
	_LIT(KTestFileAppend, "e:\\te_CSVoutputfileAppend.csv");
	_LIT(KTestFileOverwrite, "e:\\te_CSVoutputfileOverwrite.csv");
#endif	

	//initialise some generic data to write to csv
	RArray<TInt64> atestdata1;
	RArray<TInt64> atestdata2;
	CleanupClosePushL(atestdata1); 
	CleanupClosePushL(atestdata2); 
		
	//data of the form:
	//0	1	2	3	4	5	6	7	8	9
	TInt data1element=10;
	for(TInt i=0; i!=data1element;i++)
		{
		atestdata1.Append((TInt64) i);
		}		
	//data of the form:
	//0	1000	2000	3000	4000	5000	6000	7000	8000	9000	10000	11000
	TInt data2element=12;
	for(TInt i=0; i!=data2element;i++)
		{
		atestdata2.Append((TInt64) i*1000);
		}
	
	
	//now test the CSV Generator functions
	//test the append data option - outputfile should contain an extra 6 lines of data of the form:
	// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	// 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000
	// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	// 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000
	// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	// 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000
	TInt appendcount=3;
	for(TInt i=0; i!=appendcount;i++)
		{ 
		Open(KTestFileAppend, ETrue);
		WriteL(atestdata1);
		WriteNewLine();
		WriteL(atestdata2);
		WriteNewLine();
		Close();	
		}

		
	//test the overwrite data option - outputfile should contain only 2 lines of data of the form:
	// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	// 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000
	for(TInt i=0; i!=appendcount;i++)
		{
		Open(KTestFileOverwrite, EFalse);
		WriteL(atestdata1);
		WriteNewLine();
		WriteL(atestdata2);
		WriteNewLine();
		Close();	
		}

	// read the files back to check they are valid, as an automated check
	
	RFs fsSession;
	RFile appendfile;
	RFile overwritefile;
	TInt filesize;
	User::LeaveIfError(fsSession.Connect());
	
	//sizes in bytes of generated output csv data
	TInt appendsize=appendcount*(sizeof(atestdata1)+sizeof(atestdata2)+2*sizeof(KCsvNewLine)+(data1element+data2element)*sizeof(KCsvComma));
	TInt overwritesize=(sizeof(atestdata1)+sizeof(atestdata2)+2*sizeof(KCsvNewLine)+(data1element+data2element)*sizeof(KCsvComma));
	
	// fill buffers used for data read from the outputted file
	RBuf8 readappendfile;
	readappendfile.CreateL(appendsize);
	RBuf8 readoverwritefile;
	readoverwritefile.CreateL(overwritesize);
	CleanupClosePushL(readappendfile); 
	CleanupClosePushL(readoverwritefile); 

	// comparison data buffers used to contain the expected data
	RBuf8 acompareappend;
	acompareappend.CreateL(appendsize);	
	RBuf8 acompareoverwrite;
	acompareoverwrite.CreateL(overwritesize);
	CleanupClosePushL(acompareappend); 
	CleanupClosePushL(acompareoverwrite); 
		
	//fill comparison array for appended data
	TInt err=FillReferenceBuffer(acompareappend, appendcount, atestdata1, data1element, atestdata2, data2element);

	//first check the appended file by reading back the last 6 lines in the file and comparing with acompareappend
	if(err==KErrNone)
		{
		if(appendfile.Open(fsSession, KTestFileAppend, EFileRead))
			{
			if(appendfile.Size(filesize))	
				{
				if(appendfile.Read((filesize-sizeof(acompareappend)),readappendfile))
					{
					if(!readappendfile.Compare(acompareappend))
						err=KErrGeneral;
					}
				}
			}
		}
	// close test output csv file
	appendfile.Close();
	
	
	//given the above has passed,
	//fill comparison array for overwritten data			
	if(err==KErrNone)
		err=FillReferenceBuffer(acompareoverwrite, (TInt) 0, atestdata1, data1element, atestdata2, data2element);
	
	//check the overwritten file by reading back the only 2 lines in the file and comparing with acompareoverwrite
	//Note: as a thorough check- read from a zero offset
	if(err==KErrNone)
		{
		if(overwritefile.Open(fsSession, KTestFileOverwrite, EFileRead))
			{
			if(overwritefile.Size(filesize))	
				{
				if(overwritefile.Read(0,readoverwritefile))
					{
					if(!readoverwritefile.Compare(acompareoverwrite))
						err=KErrGeneral;
					}
				}
			}
		}
	// close test output csv file
	overwritefile.Close();
	CleanupStack::PopAndDestroy(&atestdata1); 
	CleanupStack::PopAndDestroy(&atestdata2); 
	CleanupStack::PopAndDestroy(&readappendfile); 
	CleanupStack::PopAndDestroy(&readoverwritefile); 
	CleanupStack::PopAndDestroy(&acompareappend); 
	CleanupStack::PopAndDestroy(&acompareoverwrite); 
	return err;
	}
	
	
TInt CUptCsvGenerator::FillReferenceBuffer(RBuf8& aBufferName, const TInt& aAppendCount, const RArray<TInt64>& aData1Name, const TInt& aData1Count, const RArray<TInt64>& aData2Name, const TInt& aData2Count)
	{
	for(TInt i=0; i!=aAppendCount;i++)
		{
		// first line of data
		for(TInt j=0;j!=aData1Count;j++)
			{
			aBufferName.AppendNum(aData1Name[j]);
			//if(j!=aData1Count-1)
			if(j!=aData1Count)
				aBufferName.Append(KCsvComma);
			}	
			
		// newline
		aBufferName.Append(KCsvNewLine);
		
		// second line of data with a potentially different number of elements
		for(TInt j=0;j!=aData2Count;j++)
			{
			aBufferName.AppendNum(aData2Name[j]);
			//if(j!=aData2Count-1)
			if(j!=aData2Count)
				aBufferName.Append(KCsvComma);
			}	
		aBufferName.Append(KCsvNewLine);	
		}
	return 0;
	}

// eof


