/*
**********************************************************************
* Copyright (c) 2002-2005, International Business Machines
* Corporation and others.  All Rights Reserved.
**********************************************************************
**********************************************************************
*/
#ifndef _STRINGPERF_H
#define _STRINGPERF_H

#include <string>

#include "unicode/utypes.h"
#include "unicode/unistr.h"

#include "unicode/uperf.h"

typedef std::wstring stlstring;	

/* Define all constants for test case operations */
#define MAXNUMLINES	40000	//Max number of lines in a test data file
#define MAXSRCLEN 20		//Max length of one line. maybe a larger number, but it need more mem
#define LOOPS 100			//Iterations 
//#define LOOPS 10
#define catenate_STRLEN 2

const UChar uTESTCHAR1 =  'a';
const wchar_t wTESTCHAR1 = 'a';
const UnicodeString uEMPTY;
const stlstring sEMPTY;
UnicodeString unistr;
stlstring stlstr;
// Simulate construction with a single-char string for basic_string
wchar_t simulate[2]={wTESTCHAR1, 0};

/* Constants for scan operation */
U_STRING_DECL(scan_STRING, "Dot. 123. Some more data.", 25);
const UnicodeString uScan_STRING=UnicodeString(scan_STRING);
const stlstring sScan_STRING=stlstring(L"Dot. 123. Some more data.");

/* global variables or constants for concatenation operation */
U_STRING_DECL(uCatenate_STR, "!!", 2);
const stlstring sCatenate_STR=stlstring(L"!!");
static UnicodeString* catICU;
static stlstring* catStd;
UBool bCatenatePrealloc;

/* type defines */
typedef struct WLine WLine;
struct  WLine {
    wchar_t   name[100];
    int32_t   len;
}; //struct to store one line of wchar_t string

enum FnType { Fn_ICU, Fn_STD };
typedef FnType FnType;
typedef void (*ICUStringPerfFn)(const UChar* src,int32_t srcLen, UnicodeString s0);
typedef void (*StdStringPerfFn)(const wchar_t* src,int32_t srcLen, stlstring s0);


class StringPerfFunction : public UPerfFunction
{
public:

	virtual long getEventsPerIteration(){
		int loops = LOOPS;
		if (catICU) { delete catICU;}
		if (catStd) { delete catStd;}

		if (bCatenatePrealloc) {
	
			int to_alloc = loops * MAXNUMLINES * (MAXSRCLEN + catenate_STRLEN);
			catICU = new UnicodeString(to_alloc,'a',0);
			//catICU = new UnicodeString();

			catStd = new stlstring();
			//catStd -> reserve(loops * MAXNUMLINES * (MAXSRCLEN + catenate_STRLEN));
			catStd -> reserve(110000000);
		} else {
			catICU = new UnicodeString();
			catStd = new stlstring();
		}

		return -1;
    }

	virtual void call(UErrorCode* status)
	{
        if(line_mode_==TRUE){
            if(uselen_){
                for(int32_t i = 0; i< numLines_; i++){
					if (fnType_==Fn_ICU) {
						(*fn1_)(lines_[i].name,lines_[i].len,uS0_[i]);
					} else {
						(*fn2_)(wlines_[i].name,wlines_[i].len,sS0_[i]);
					}
                }
            }else{
                for(int32_t i = 0; i< numLines_; i++){
                    if (fnType_==Fn_ICU) {
						(*fn1_)(lines_[i].name,-1,uS0_[i]);
					} else {
						(*fn2_)(wlines_[i].name,-1,sS0_[i]);
					}
                }
            }
        }else{
            if(uselen_){
				if (fnType_==Fn_ICU) {
					(*fn1_)(src_,srcLen_,*ubulk_);
				} else {
					(*fn2_)(wsrc_,wsrcLen_,*sbulk_);
				}
            }else{
				if (fnType_==Fn_ICU) {
					(*fn1_)(src_,-1,*ubulk_);
				} else {
					(*fn2_)(wsrc_,-1,*sbulk_);
				}
            }
        }
	}

	virtual long getOperationsPerIteration()
	{
        if(line_mode_==TRUE){
            return numLines_;
        }else{
            return 1;
        }
	}

	StringPerfFunction(ICUStringPerfFn func, ULine* srcLines, int32_t srcNumLines, UBool uselen)
	{
		
		fn1_ = func;
		lines_=srcLines;
		wlines_=NULL;
		numLines_=srcNumLines;
		uselen_=uselen;
		line_mode_=TRUE;
        src_ = NULL;
        srcLen_ = 0;
		wsrc_ = NULL;
		wsrcLen_ = 0;
		fnType_ = Fn_ICU;
		
		uS0_=new UnicodeString[numLines_];
		for(int32_t i=0; i<numLines_; i++) {
			uS0_[i]=UnicodeString(lines_[i].name, lines_[i].len);
		} 
		sS0_=NULL;
		ubulk_=NULL;
		sbulk_=NULL;
	} 

	StringPerfFunction(StdStringPerfFn func, ULine* srcLines, int32_t srcNumLines, UBool uselen)
	{
		
		fn2_ = func;
		lines_=srcLines;
		wlines_=NULL;
		numLines_=srcNumLines;
		uselen_=uselen;
		line_mode_=TRUE;
        src_ = NULL;
        srcLen_ = 0;
		wsrc_ = NULL;
		wsrcLen_ = 0;
		fnType_ = Fn_STD;

		uS0_=NULL;
		ubulk_=NULL;
		sbulk_=NULL;
		
		//fillin wlines_[], sS0_[]
		prepareLinesForStd();
	} 

	StringPerfFunction(ICUStringPerfFn func, UChar* source, int32_t sourceLen, UBool uselen)
	{
		
		fn1_ = func;
		lines_=NULL;
		wlines_=NULL;
		numLines_=0;
		uselen_=uselen;
		line_mode_=FALSE;
		src_ = new UChar[sourceLen];
		memcpy(src_, source, sourceLen * U_SIZEOF_UCHAR);
        srcLen_ = sourceLen;
		wsrc_ = NULL;
		wsrcLen_ = 0;
		fnType_ = Fn_ICU;

		uS0_=NULL;
		sS0_=NULL;	
		ubulk_=new UnicodeString(src_,srcLen_);
		sbulk_=NULL;
	}

	StringPerfFunction(StdStringPerfFn func, UChar* source, int32_t sourceLen, UBool uselen)
	{
		
		fn2_ = func;
		lines_=NULL;
		wlines_=NULL;
		numLines_=0;
		uselen_=uselen;
		line_mode_=FALSE;
		src_ = new UChar[sourceLen];
		memcpy(src_, source, sourceLen * U_SIZEOF_UCHAR);
        srcLen_ = sourceLen;
		fnType_ = Fn_STD;

		uS0_=NULL;
		sS0_=NULL;
		ubulk_=NULL;

		//fillin wsrc_, sbulk_
		prepareBulkForStd();
					 
	}

	~StringPerfFunction()
	{
		//free(src_);
		free(wsrc_);
		delete[] src_;
		delete ubulk_;
		delete sbulk_;
		delete[] uS0_;
		delete[] sS0_;
		delete[] wlines_;
	}

private:
	void prepareLinesForStd(void)
	{
		UErrorCode err=U_ZERO_ERROR;
		
		wlines_=new WLine[numLines_];
        wchar_t ws[100];
        int32_t wcap = sizeof(ws) / sizeof(*ws);
        int32_t wl;
		wchar_t* wcs;
        
		sS0_=new stlstring[numLines_];
		for(int32_t i=0; i<numLines_; i++) {		
			if(uselen_) {
				wcs = u_strToWCS(ws, wcap, &wl, lines_[i].name, lines_[i].len, &err);
				memcpy(wlines_[i].name, wcs, wl * sizeof(wchar_t));
				wlines_[i].len = wl;
				sS0_[i]=stlstring(wlines_[i].name, wlines_[i].len);
			} else {
				wcs = u_strToWCS(ws, wcap, &wl, lines_[i].name, lines_[i].len-1, &err);
				memcpy(wlines_[i].name, wcs, wl*sizeof(wchar_t));
				wlines_[i].len = wl;
				sS0_[i]=stlstring(wlines_[i].name, wlines_[i].len+1);
			}

			if (U_FAILURE(err)) {
				return;
			}
		}

	}

	void prepareBulkForStd(void)
	{
		UErrorCode err=U_ZERO_ERROR;

		const UChar* uSrc = src_;
		int32_t uSrcLen = srcLen_;
		wchar_t* wDest = NULL;
		int32_t wDestLen = 0;
		int32_t reqLen= 0 ;

		if(uselen_) {
			/* pre-flight*/
			u_strToWCS(wDest,wDestLen,&reqLen,uSrc,uSrcLen,&err);

			if(err == U_BUFFER_OVERFLOW_ERROR){
				err=U_ZERO_ERROR;
				wDest =(wchar_t*) malloc(sizeof(wchar_t) * (reqLen));
				wDestLen = reqLen;
				u_strToWCS(wDest,wDestLen,&reqLen,uSrc,uSrcLen,&err);
			}

			if (U_SUCCESS(err)) { 
				wsrc_ = wDest;
				wsrcLen_ = wDestLen;
				sbulk_=new stlstring(wsrc_,wsrcLen_); 
			}

		} else {
			/* pre-flight*/
			u_strToWCS(wDest,wDestLen,&reqLen,uSrc,uSrcLen-1,&err);

			if(err == U_BUFFER_OVERFLOW_ERROR){
				err=U_ZERO_ERROR;
				wDest =(wchar_t*) malloc(sizeof(wchar_t) * (reqLen+1));
				wDestLen = reqLen+1;
				u_strToWCS(wDest,wDestLen,&reqLen,uSrc,uSrcLen-1,&err);
			}

			if (U_SUCCESS(err)) { 
				wsrc_ = wDest;
				wsrcLen_ = wDestLen;
				sbulk_=new stlstring(wsrc_); 
			}
		}

		//free(wDest);
	}


private:
	ICUStringPerfFn fn1_;
	StdStringPerfFn fn2_;

	ULine* lines_;
	WLine* wlines_;
	int32_t numLines_;

	UBool uselen_;
	UChar* src_;
	int32_t srcLen_;
	wchar_t* wsrc_;
	int32_t wsrcLen_;
    UBool line_mode_;
	
	//added for preparing testing data
	UnicodeString* uS0_;
	stlstring* sS0_;
	UnicodeString* ubulk_;
	stlstring* sbulk_;
	FnType fnType_;
}; 


class StringPerformanceTest : public UPerfTest
{
public:
	StringPerformanceTest(int32_t argc, const char *argv[], UErrorCode &status);
	~StringPerformanceTest();
	virtual UPerfFunction* runIndexedTest(int32_t index, UBool exec,
		                                  const char *&name, 
										  char *par = NULL);     
	UPerfFunction* TestCtor();
	UPerfFunction* TestCtor1();
	UPerfFunction* TestCtor2();
	UPerfFunction* TestCtor3();
	UPerfFunction* TestAssign();
	UPerfFunction* TestAssign1();
	UPerfFunction* TestAssign2();
	UPerfFunction* TestGetch();
	UPerfFunction* TestCatenate();
	UPerfFunction* TestScan();
	UPerfFunction* TestScan1();
	UPerfFunction* TestScan2();

	UPerfFunction* TestStdLibCtor();
	UPerfFunction* TestStdLibCtor1();
	UPerfFunction* TestStdLibCtor2();
	UPerfFunction* TestStdLibCtor3();
	UPerfFunction* TestStdLibAssign();
	UPerfFunction* TestStdLibAssign1();
	UPerfFunction* TestStdLibAssign2();
	UPerfFunction* TestStdLibGetch();
	UPerfFunction* TestStdLibCatenate();
	UPerfFunction* TestStdLibScan();
	UPerfFunction* TestStdLibScan1();
	UPerfFunction* TestStdLibScan2();

private:
	long COUNT_;
	ULine* filelines_;
	UChar* StrBuffer;
	int32_t StrBufferLen;

};


inline void ctor(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	UnicodeString a;		
}

inline void ctor1(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	UnicodeString b(uTESTCHAR1);
}

inline void ctor2(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	UnicodeString c(uEMPTY);
}

inline void ctor3(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	UnicodeString d(src,srcLen);
}

inline UnicodeString icu_assign_helper(const UChar* src,int32_t srcLen)
{
	if (srcLen==-1) { return src;} 
	else { return UnicodeString(src, srcLen);}
}

inline void assign(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	unistr = icu_assign_helper(src,srcLen);
}

inline void assign1(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	unistr.setTo(src, srcLen);
}

inline void assign2(const UChar* src,int32_t srcLen, UnicodeString s0) 
{
	unistr = s0;
}

inline void getch(const UChar* src,int32_t srcLen, UnicodeString s0)
{
	s0.charAt(0);
}


inline void catenate(const UChar* src,int32_t srcLen, UnicodeString s0)
{
	UTimer mystart, mystop;
	utimer_getTime(&mystart); 
	
	*catICU += s0;
	
	utimer_getTime(&mystop);
    double mytime = utimer_getDeltaSeconds(&mystart,&mystop);
	printf("\nmytime=%f \n", mytime);

	*catICU += uCatenate_STR;
}

volatile int scan_idx;
U_STRING_DECL(SCAN1, "123", 3);

inline void scan(const UChar* src,int32_t srcLen, UnicodeString s0)
{
	UChar c='.';
	scan_idx = uScan_STRING.indexOf(c);
}

inline void scan1(const UChar* src,int32_t srcLen, UnicodeString s0)
{
	scan_idx = uScan_STRING.indexOf(SCAN1,3);
}

inline void scan2(const UChar* src,int32_t srcLen, UnicodeString s0)
{
	UChar c1='s';
	UChar c2='m';
	scan_idx = uScan_STRING.indexOf(c1);
	scan_idx = uScan_STRING.indexOf(c2);
}


inline void StdLibCtor(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	stlstring a;
}

inline void StdLibCtor1(const wchar_t* src,int32_t srcLen, stlstring s0)
{

	stlstring b(simulate);
}

inline void StdLibCtor2(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	stlstring c(sEMPTY);
}

inline void StdLibCtor3(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	if (srcLen==-1) {	
		stlstring d(src); 
	}else {
		stlstring d(src, srcLen); 
	}
}

inline stlstring stl_assign_helper(const wchar_t* src,int32_t srcLen)
{
	if (srcLen==-1) { return src;}
	else { return stlstring(src, srcLen);}
}

inline void StdLibAssign(const wchar_t* src,int32_t srcLen, stlstring s0) 
{
	stlstr = stl_assign_helper(src,srcLen);
}

inline void StdLibAssign1(const wchar_t* src,int32_t srcLen, stlstring s0) 
{
	if (srcLen==-1) { stlstr=src;}
	else { stlstr.assign(src, srcLen);}
}

inline void StdLibAssign2(const wchar_t* src,int32_t srcLen, stlstring s0) 
{
	stlstr=s0;
}

inline void StdLibGetch(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	s0.at(0);
}

inline void StdLibCatenate(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	UTimer mystart, mystop;
	utimer_getTime(&mystart); 

    *catStd += s0;
	*catStd += sCatenate_STR;

	utimer_getTime(&mystop);
    double mytime = utimer_getDeltaSeconds(&mystart,&mystop);
	printf("\nmytime=%f \n", mytime);
	
}

inline void StdLibScan(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	scan_idx = (int) sScan_STRING.find('.');	
}

inline void StdLibScan1(const wchar_t* src,int32_t srcLen, stlstring s0)
{
	scan_idx = (int) sScan_STRING.find(L"123");
}

inline void StdLibScan2(const wchar_t* src,int32_t srcLen, stlstring s0)
{	
	scan_idx = (int) sScan_STRING.find_first_of(L"sm");
}

#endif // STRINGPERF_H