/*
* Copyright (c) 1997-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 <e32test.h>
//#include /**/ <s32mem.h>
#include <s32file.h>
#include <flddef.h>
#include <fldbase.h>
#include <fldset.h>
#include "TESTFAC.H"

const TInt KTestCleanupStack=0x20;

LOCAL_D RTest test(_L("T_STREAM - Fields Persistence"));
LOCAL_D CTrapCleanup* TheTrapCleanup;
LOCAL_D CTextFieldSet* TheFieldSetOriginal;
LOCAL_D CTextFieldSet* TheFieldSetCopy;
LOCAL_D TTestFieldFactory* TheFactory;

_LIT(KTFieldOutputFile, "c:\\etext\\tfield.tst");
// Test Picture persistance.
LOCAL_C void testStoreRestore(CTextFieldSet* aCopy,const CTextFieldSet* aOriginal)
    {
	// set up the store
	RFs	theFs;
	theFs.Connect();
	theFs.Delete(KTFieldOutputFile);
	theFs.MkDirAll(KTFieldOutputFile);
	CFileStore* theStore=CDirectFileStore::CreateL(theFs, KTFieldOutputFile, EFileRead | EFileWrite);
	CleanupStack::PushL(theStore);
	theStore->SetTypeL(KDirectFileStoreLayoutUid);
	//
	// store the original
	TStreamId id(0);
	TRAPD(ret,id=aOriginal->StoreL(*theStore));
		test(ret==KErrNone);
	//
	// restore into the copy
	TRAP(ret,aCopy->RestoreL(*theStore,id));
		test(ret==KErrNone);
	//
	// tidy up
	CleanupStack::PopAndDestroy();  // theStore
	theFs.Close();
    }



template <class T>
void testCopyPaste(T* aCopy, T* aOriginal,TInt aCopyPos,TInt aLen,TInt aPastePos,TInt aPasteLen=-1)
// Copy part of anOriginal to aCopy using memory-based streams.
//
	{
	// set up the store
	RFs	theFs;
	theFs.Connect();
	CFileStore* theStore=CDirectFileStore::ReplaceL(theFs,KTFieldOutputFile,EFileRead|EFileWrite);
	CleanupStack::PushL(theStore);
	theStore->SetTypeL(KDirectFileStoreLayoutUid);
	//
	// Copy anOriginal out to the buffer
	TStreamId id(0);
	TRAPD(ret,id=aOriginal->CopyToStoreL(*theStore,aCopyPos,aLen));
		test(ret==KErrNone);
	//
	// paste into the copy
	TRAP(ret,aCopy->PasteFromStoreL(*theStore,id,aPastePos,aPasteLen));
		test(ret==KErrNone);
	//
	// tidy up
	CleanupStack::PopAndDestroy();  // theStore
	theFs.Close();

	}


LOCAL_C TBool IsEqual(const CTextFieldSet* aCopy,const CTextFieldSet* anOriginal)
//
// Returns true if aCopy contents matches anOriginal contents.
//
	{
	// test the num chars and num fields
	TInt charCount = anOriginal->CharCount();
	TInt fieldCount = anOriginal->FieldCount();
	test(charCount==aCopy->CharCount());
	test(fieldCount==aCopy->FieldCount());

	// test each entry in the array
	for (TInt pos=0 ; pos<charCount ;)
		{
		TFindFieldInfo infoOrig,infoCopy;
		anOriginal->FindFields(infoOrig,pos,charCount-pos);
		aCopy->FindFields(infoCopy,pos,charCount-pos);
		test(infoOrig==infoCopy);
		pos += infoOrig.iFirstFieldPos+infoOrig.iFirstFieldLen;
		if (infoOrig.iFieldCountInRange==0)
			break;
		}

	return ETrue;
	}


LOCAL_C TBool UpdateField(TInt aPos,CTextFieldSet* aSet)
	{
	// find out which field aPos is in
	TFindFieldInfo info;
	TBool ret;
	ret=aSet->FindFields(info,aPos);
		test(ret);
	// get the new value
	HBufC* buf = HBufC::NewL(5); 
	CleanupStack::PushL(buf);
 	TInt err=aSet->NewFieldValueL(buf,aPos); 
		test(err==KErrNone);
	// Notify FieldSet of update
	aSet->NotifyFieldUpdate(aPos,buf->Length());
	// tidy up
	CleanupStack::PopAndDestroy();
	return ret;
	}


LOCAL_C void test2()
// Streams an empty field set
//
	{
	test.Next(_L("- streaming of a default FieldSet (no fields)"));
	testStoreRestore(TheFieldSetCopy,TheFieldSetOriginal);
	test(IsEqual(TheFieldSetCopy,TheFieldSetOriginal));
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test3()
// Streams a field set containing 100 chars & 1 field
//
	{
	test.Next(_L("- streaming with a field"));

	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert a field into the original
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	ret=UpdateField(10,TheFieldSetOriginal);
		test(ret);

	// test streaming
	testStoreRestore(TheFieldSetCopy,TheFieldSetOriginal);
	test(IsEqual(TheFieldSetCopy,TheFieldSetOriginal));
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test4()
// Streams a field set into a non-empty target
//
	{
	test.Next(_L("- streaming into a non-empty target"));

	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert a field into the original
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	ret=UpdateField(10,TheFieldSetOriginal);
		test(ret);

	// insert a block of text into the copy
	TheFieldSetCopy->NotifyInsertion(0,40); // pos=0, len=40
	// insert two fields into the copy
	CTextField* field2 = TheFieldSetCopy->NewFieldL(KDummyFieldUid);
	CTextField* field3 = TheFieldSetCopy->NewFieldL(KDummyFieldUid);
	ret=TheFieldSetCopy->InsertFieldL(20,field2,KDummyFieldUid); 
		test(ret==KErrNone);
	ret=TheFieldSetCopy->InsertFieldL(30,field3,KDummyFieldUid); 
		test(ret==KErrNone);
	ret=UpdateField(20,TheFieldSetCopy);
		test(ret);
	ret=UpdateField(33,TheFieldSetCopy);
		test(ret);

	// test streaming
	testStoreRestore(TheFieldSetCopy,TheFieldSetOriginal);
	test(IsEqual(TheFieldSetCopy,TheFieldSetOriginal));
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test5()
// Streams a field set containing an out of date field
//
	{
	test.Next(_L("- streaming an out of date field"));

	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert a field into the original
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	ret=UpdateField(10,TheFieldSetOriginal);
		test(ret);
	// invalidate the field
	TheFieldSetOriginal->NotifyInsertion(11,7); // pos=11, len=7

	// test streaming
	testStoreRestore(TheFieldSetCopy,TheFieldSetOriginal);
	test(IsEqual(TheFieldSetCopy,TheFieldSetOriginal));
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test6()
// Tests that copy/paste methods exist
//
	{
	test.Next(_L("- testing copy/paste methods with empty array"));
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,0,0,0); // copyPos,Len,PastePos
	test(IsEqual(TheFieldSetCopy,TheFieldSetOriginal));
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test7()
// Tests copy/paste methods in detail
//
	{
	test.Next(_L("- testing copy/paste methods in detail"));
	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert a field into the original
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	TBool ok=UpdateField(10,TheFieldSetOriginal);
		test(ok);
		test(TheFieldSetOriginal->CharCount()==103);
	// copy out of that into an empty array
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,15,0); // copyPos,Len,PastePos
	// check that the contents of the array are as expected
	TFindFieldInfo info;
	TheFieldSetCopy->FindFields(info,0,15);
		test(TheFieldSetCopy->CharCount()==15);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==5);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->Reset();
	// test copying part of a field
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,6,0); // copyPos,Len,PastePos
	TheFieldSetCopy->FindFields(info,0,6);
		test(TheFieldSetCopy->CharCount()==6);
		test(TheFieldSetCopy->FieldCount()==0);
		test(info.iFieldCountInRange==0);
	TheFieldSetCopy->Reset();
	// test copying exactly one field
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,10,3,0); // copyPos,Len,PastePos
	TheFieldSetCopy->FindFields(info,0,3);
		test(TheFieldSetCopy->CharCount()==3);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==0);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->Reset();
	// test pasting into text in a non-empty array
	TheFieldSetCopy->NotifyInsertion(0,50); // pos=0, len=50
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,15,10); // copyPos,Len,PastePos
	TheFieldSetCopy->FindFields(info,0,65);
		test(TheFieldSetCopy->CharCount()==65);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==15);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->Reset();
	// test pasting into a field
	TheFieldSetCopy->NotifyInsertion(0,50); // pos=0, len=50
	field=TheFieldSetCopy->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	ret=TheFieldSetCopy->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	ok=UpdateField(10,TheFieldSetCopy);
		test(ok);
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,15,11); // copyPos,Len,PastePos
	TheFieldSetCopy->FindFields(info,0,68);
		test(TheFieldSetCopy->CharCount()==68);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==10);
		test(info.iFirstFieldLen==18);
	TheFieldSetCopy->Reset();
	// test pasting at the start & end of a field
	TheFieldSetCopy->NotifyInsertion(0,10); // pos=0, len=10
	field=TheFieldSetCopy->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	ret=TheFieldSetCopy->InsertFieldL(5,field,KDummyFieldUid); // pos=5
		test(ret==KErrNone);
	ok=UpdateField(5,TheFieldSetCopy);
		test(ok);
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,10,3,8); // copyPos,Len,PastePos
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,10,3,5); // copyPos,Len,PastePos
	TheFieldSetCopy->FindFields(info,0,19);
		test(TheFieldSetCopy->CharCount()==19);
		test(TheFieldSetCopy->FieldCount()==3);
		test(info.iFieldCountInRange==3);
		test(info.iFirstFieldPos==5);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->FindFields(info,8,11);
		test(info.iFieldCountInRange==2);
		test(info.iFirstFieldPos==8);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->FindFields(info,11,8);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==11);
		test(info.iFirstFieldLen==3);
	// tidy up
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test8()
// Tests paste method with restricted target length
//
	{
	test.Next(_L("- testing paste method with restricted length"));
	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert 2 fields into the original
	// 1...
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	TBool ok=UpdateField(10,TheFieldSetOriginal);
		test(ok);
	// 2..
	field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	ret=TheFieldSetOriginal->InsertFieldL(20,field,KDummyFieldUid); // pos=20
		test(ret==KErrNone);
	ok=UpdateField(20,TheFieldSetOriginal);
		test(ok);
	// paste part of original into copy with length greater than required
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,10,5,0,7); // copyPos,Len,PastePos,PasteLen
	TFindFieldInfo info;
	TheFieldSetCopy->FindFields(info,0,5);
		test(TheFieldSetCopy->CharCount()==5);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==0);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->Reset();
	// paste part of original into copy with length equal to that required
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,10,5,0,5); // copyPos,Len,PastePos,PasteLen
	TheFieldSetCopy->FindFields(info,0,5);
		test(TheFieldSetCopy->CharCount()==5);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==0);
		test(info.iFirstFieldLen==3);
	TheFieldSetCopy->Reset();
	// paste part of original into copy with length zero
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,10,5,0,0); // copyPos,Len,PastePos,PasteLen
		test(TheFieldSetCopy->CharCount()==0);
		test(TheFieldSetCopy->FieldCount()==0);
	TheFieldSetCopy->Reset();
	// paste part of original into copy with length st field is chopped
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,10,0,6); // copyPos,Len,PastePos,PasteLen
	TheFieldSetCopy->FindFields(info,0,6);
		test(TheFieldSetCopy->CharCount()==6);
		test(TheFieldSetCopy->FieldCount()==0);
		test(info.iFieldCountInRange==0);
	TheFieldSetCopy->Reset();
	// paste part of original into copy with length st one field is left off, the other pasted in
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,20,0,10); // copyPos,Len,PastePos,PasteLen
	TheFieldSetCopy->FindFields(info,0,10);
		test(TheFieldSetCopy->CharCount()==10);
		test(TheFieldSetCopy->FieldCount()==1);
		test(info.iFieldCountInRange==1);
		test(info.iFirstFieldPos==5);
		test(info.iFirstFieldLen==3);
	// tidy up
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


LOCAL_C void test9()
// Tests paste into set with no field factory
// Should convert all pasted fields to text...
//
	{
	test.Next(_L("- testing paste into set with no field factory"));
	TheFieldSetCopy->SetFieldFactory(NULL);
	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert a field into the original
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDummyFieldUid);
		test(field!=NULL);
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDummyFieldUid); // pos=10
		test(ret==KErrNone);
	TBool ok=UpdateField(10,TheFieldSetOriginal);
		test(ok);
	// paste part of original into copy
	testCopyPaste(TheFieldSetCopy,TheFieldSetOriginal,5,25,0); // copyPos,Len,PastePos
	TFindFieldInfo info;
	TheFieldSetCopy->FindFields(info,0,25);
		test(TheFieldSetCopy->CharCount()==25);
		test(TheFieldSetCopy->FieldCount()==0);
	// tidy up
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	TheFieldSetCopy->SetFieldFactory(TheFactory);
	}


LOCAL_C void test10()
// Streams a field set containing 100 chars & 1 field
//
	{
	test.Next(_L("- streaming CDateTimeField"));

	// insert a block of text into the original
	TheFieldSetOriginal->NotifyInsertion(0,100); // pos=0, len=100
	// insert a field into the original
	CTextField* field=TheFieldSetOriginal->NewFieldL(KDateTimeFieldUid);
		test(field!=NULL);
	TBuf<14> buff = _L("%-A%*I%:1%T%+A");
	((CDateTimeField*)field)->SetFormat(buff); // 01:53pm
	TInt ret=TheFieldSetOriginal->InsertFieldL(10,field,KDateTimeFieldUid); // pos=10
		test(ret==KErrNone);
	ret=UpdateField(10,TheFieldSetOriginal);
		test(ret);
	// value before
	HBufC* miniBuf = HBufC::NewL(5); 
	CleanupStack::PushL(miniBuf);
	ret=TheFieldSetOriginal->NewFieldValueL(miniBuf,10); // pos=10
		test(ret==KErrNone);
	TPtr buf = miniBuf->Des();
	test.Printf(_L("  Value before streaming: "));
		test.Printf(buf);
		test.Printf(_L("\n"));
	// test streaming
	testStoreRestore(TheFieldSetCopy,TheFieldSetOriginal);
	test(IsEqual(TheFieldSetCopy,TheFieldSetOriginal));
	// value after
	ret=TheFieldSetCopy->NewFieldValueL(miniBuf,10); // pos=10
		test(ret==KErrNone);
	buf = miniBuf->Des();
	test.Printf(_L("  Value after streaming:  "));
		test.Printf(buf);
		test.Printf(_L("\n"));
	CleanupStack::PopAndDestroy(); // miniBuf
	TheFieldSetOriginal->Reset();
	TheFieldSetCopy->Reset();
	}


void TestStreamingL()
//
// Test streaming of PrintSetup info.
// Stream from one copy to another, then compare
//
	{
	// create the PrintSetups
	test.Start(_L("Streaming CTextFieldSet"));

	// instantiate the factory and FieldSet
	TheFactory = new(ELeave) TTestFieldFactory();
	TheFieldSetOriginal = CTextFieldSet::NewL();
	TheFieldSetOriginal->SetFieldFactory(TheFactory);
	TheFieldSetCopy = CTextFieldSet::NewL();
	TheFieldSetCopy->SetFieldFactory(TheFactory);

	// Use "original" with default settings & test stream
	test2();

	// change data in original and test stream again
	test3();

	// Stream a field set into a non-empty target
	test4();

	// Stream a field set containing an out of date field
	test5();

	// Test that copy/paste methods exist
	test6();

	// Test copy/paste methods in detail
	test7();

	// Test paste with limited target area
	test8();

	// Test paste into set with no field factory
	test9();

	// Test streaming of built-in types
	test10();

	// end
	delete TheFieldSetOriginal;
	delete TheFieldSetCopy;
	delete TheFactory;
	}


LOCAL_C void setupCleanup()
//
// Initialise the cleanup stack.
//
    {

	TheTrapCleanup=CTrapCleanup::New();
	TRAPD(r,\
		{\
		for (TInt i=KTestCleanupStack;i>0;i--)\
			CleanupStack::PushL((TAny*)1);\
		test(r==KErrNone);\
		CleanupStack::Pop(KTestCleanupStack);\
		});
	}


LOCAL_C void DeleteDataFile(const TDesC& aFullName)
	{
	RFs fsSession;
	TInt err = fsSession.Connect();
	if(err == KErrNone)
		{
		TEntry entry;
		if(fsSession.Entry(aFullName, entry) == KErrNone)
			{
			RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
			err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
				}
			err = fsSession.Delete(aFullName);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
				}
			}
		fsSession.Close();
		}
	else
		{
		RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
		}
	}

GLDEF_C TInt E32Main()
//
// Test streaming conversions.
//
    {
	test.Title();

	__UHEAP_MARK;
	setupCleanup();

	TRAPD(r,TestStreamingL());
	test(r==KErrNone);

	delete TheTrapCleanup;

	__UHEAP_MARKEND;
	
	::DeleteDataFile(KTFieldOutputFile);	//deletion of data files must be before call to End() - DEF047652
	
	test.End();
	test.Close();

	return 0;
    }

