/*
* Copyright (c) 2008-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: 
* Summary:
* A test to exercise the contacts model subview object and associated 
* functionality. The test includes creating, resorting and searching a view. 
* Actions:
* -- Create a new database;
* -- Populate database with randomly generated data;
* -- Create a local named view sorted by lname, fname, company and telephone;
* -- Create a subview based on the local view;
* -- Verify the subview has been created properly;
* -- Resort the local view;
* -- Verify the subview has been updated properly after resort;
* -- Clean up.
* Associated defect:
* DEF125393 PIM Sphinx QP: Improve test coverage of Cntmodel Subviews
* Written by:
* James Clarke
*
*/


#include "t_subview.h"


//////////////////////////////////////////////////////////////////////////////////////////////////
// CSubViewTest methods
//////////////////////////////////////////////////////////////////////////////////////////////////

CSubViewTest* CSubViewTest::NewLC()
	{
	CSubViewTest* self = new (ELeave) CSubViewTest();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}


CSubViewTest::CSubViewTest() :
	CActive(CActive::EPriorityStandard)
	{
	}
	
	
void CSubViewTest::ConstructL()
	{
	CActiveScheduler::Add(this);

	// create a database populated with randomly generated contacts 
	iContactsDb = DbCreator::CreateDbL(KDbName, KNumContacts);
	}


CSubViewTest::~CSubViewTest()
	{
	Cancel();
	Cleanup();
	}


void CSubViewTest::Cleanup()
	{
	if (iNamedLocalView)
		{
		iNamedLocalView->Close(*this);
		iNamedLocalView = NULL;
		}
	if (iSubView)
		{
		iSubView->Close(*this);
		iSubView = NULL;
		}
	delete iContactsDb;
	TRAP_IGNORE(CContactDatabase::DeleteDatabaseL(KDbName));
	}


void CSubViewTest::RunL()
	{
	switch (iState)
		{
		case ETestStarted:
			CreateNamedLocalViewL();
			iState = ENamedLocalViewCreated;
			break;
		case ENamedLocalViewCreated: 
			test.Printf(_L("Local view created"));
			CreateSubViewL();
			iState = ESubViewCreated;
			break;
		case ESubViewCreated:
			test.Printf(_L("Sub view created"));
			PreResortTestL();
			ResortNamedLocalViewL();
			iState = EResorting;
			break;
		case ENamedLocalViewResorted: 
		case ESubViewUpdated: 
			break;
		case ESortingComplete:
			test.Printf(_L("Local view resorted"));
			test.Printf(_L("Sub view updated after local view resort."));
			PostResortTestL();
			CActiveScheduler::Stop();
			break;
		default:
			test.Printf(_L("Invalid state."));
			test(EFalse);
		}
	}



void CSubViewTest::CreateNamedLocalViewL()
	{
	test.Next(_L("Creating local view."));

	
	RContactViewSortOrder sortOrder;
	CleanupClosePushL(sortOrder);
	sortOrder.AppendL(KUidContactFieldFamilyName);
	sortOrder.AppendL(KUidContactFieldGivenName);
	sortOrder.AppendL(KUidContactFieldCompanyName);
	sortOrder.AppendL(KUidContactFieldPhoneNumber);

	// set the field indexes to reflect the order above
	iLnameIdx = 0;
	iFnameIdx = 1;
	iCnameIdx = 2;
	iNumIdx   = 3;
	
	iNamedLocalView = CContactNamedLocalView::NewL(*this, KTestName(), *iContactsDb, sortOrder, EContactsOnly);
	
	CleanupStack::PopAndDestroy(&sortOrder);
	}


void CSubViewTest::DoCancel()
	{
	}


void CSubViewTest::CreateSubViewL()
	{
	test.Next(_L("Creating subview."));


	iSubView = CContactSubView::NewL(*iNamedLocalView, *iContactsDb, *this, KLowBoundary(), KHighBoundary());
	}


void CSubViewTest::HandleContactViewEvent(const CContactViewBase& aView, const TContactViewEvent& aEvent)
	{

	if (aEvent.iEventType == TContactViewEvent::ESortOrderChanged)
		{
			if (iState == EResorting)
				{
				if (&aView == iNamedLocalView)
					{
					iState = ENamedLocalViewResorted;
					}
				else
					{
					iState = ESubViewUpdated; 
					}
				}
			else if (iState == ENamedLocalViewResorted || iState == ESubViewUpdated)
				{
				iState = ESortingComplete;
				}
			else
				{
				test.Printf(_L("Ooops! Something's gone wrong with the view-resorting states..."));
				test(EFalse);
				}
		}

	if (aEvent.iEventType == TContactViewEvent::EReady || iState == ESortingComplete)
		{
		TRequestStatus* pStat = &iStatus;
	  	User::RequestComplete(pStat, KErrNone);
	   	SetActive();
		}
	}


void CSubViewTest::PrintSubViewL() const
	{
	const TInt KLocalCount(iNamedLocalView->CountL());
	const TInt KSubCount(iSubView->CountL());
	test.Printf(_L("Subview contains %d of the %d contacts in the NamedLocalView."), KSubCount, KLocalCount);
	
	for(TInt i = 0; i < KSubCount; ++i)
		{
		const TPtrC fname = iSubView->ContactAtL(i).Field(iFnameIdx);
		const TPtrC lname = iSubView->ContactAtL(i).Field(iLnameIdx);
		const TPtrC cname = iSubView->ContactAtL(i).Field(iCnameIdx);
		const TPtrC num = iSubView->ContactAtL(i).Field(iNumIdx);
		test.Printf(_L("Contact #%d -- Person: %S %S; Company: %S; Phone number: %S."), 
			i + 1, &fname, &lname, &cname, &num);
		}
	}


void CSubViewTest::PreResortTestL() const
	{
	test.Next(_L("Verifying subview is correct after creation."));

	SubAndLocalViewCompareTestL();
	CheckSortOrderL();
	}

void CSubViewTest::PostResortTestL() const
	{
	test.Next(_L("Verifying subview is correct after resorting underlying view."));

	SubAndLocalViewCompareTestL();
	CheckSortOrderL();
	}


void CSubViewTest::SubAndLocalViewCompareTestL() const
	{
	test.Printf(_L("Testing subview is accurate compared to the local view."));

	const TInt KSubViewCount = iSubView->CountL();
	const TInt KLocalViewCount = iNamedLocalView->CountL();
	const TInt KFirstFieldIdx(0);
	
	TInt subViewIdx(0);
	TBool foundFirstInSubView(EFalse);
	TBool foundLastInSubView(EFalse);
	
	TInt subCntItemId(iSubView->AtL(subViewIdx));
	TInt localCntItemId(-1); // initialise with a value that can't be a real Id 
	for(TInt localViewIdx = 0; localViewIdx < KLocalViewCount && !foundLastInSubView; ++localViewIdx)
		{
		localCntItemId = iNamedLocalView->AtL(localViewIdx);

		// already found the first item in the sub view within
		// the underlying local view.
		if (foundFirstInSubView)
			{
			++subViewIdx;
			subCntItemId = iSubView->AtL(subViewIdx);
			// check that the contact ids in the sub view and local view match
			if (subCntItemId != localCntItemId)
				{
				test.Printf(_L("Mismatch of contacts between subview and local view"));
				test(EFalse);
				}

			// if we've found the last sub view item, do boundary checking to 
			// ensure we have the right things from the local view in the sub view. 
			if (subViewIdx == KSubViewCount - 1)
				{
				foundLastInSubView = ETrue;

				// Haven't checked localViewIdx < KLocalViewCount but it's not
				// likely that the last contact in the sub view will legitimately 
				// be the same as the last one in the local view.    
				const TPtrC nextLocalName = iNamedLocalView->ContactAtL(localViewIdx + 1).Field(KFirstFieldIdx);
				const TPtrC subName = iSubView->ContactAtL(subViewIdx).Field(KFirstFieldIdx);

				// check that the first character of the last contact in the subview
				// is within the proper range and that the next contact in the
				// local view is outside the range.
				test.Printf(_L("Found last contact from subview in local view. Testing for errors."));
				test(subName.Left(1).CompareF(KHighChar) <= 0 ||
						nextLocalName.Left(1).CompareF(KHighChar) > 0);
				test.Printf(_L("-- check okay."));
				}
			}

		// we've found the first contact item from the sub view in the local
		// view. check 
		else if (localCntItemId == subCntItemId && !foundFirstInSubView)
			{
			foundFirstInSubView = ETrue;

			// Haven't checked localViewIdx != 0 but it is not likely that the 
			// first contact in the local view will legitimately be the same as 
			// the first one in the sub view.
			const TPtrC prevLocalName = iNamedLocalView->ContactAtL(localViewIdx - 1).Field(KFirstFieldIdx);
			const TPtrC subName = iSubView->ContactAtL(subViewIdx).Field(KFirstFieldIdx);
						
			// check that the first character of the first contact in the subview
			// is within the proper range and that the previous contact in the
			// local view is outside the range.
			test.Printf(_L("Found first contact from subview in local view. Testing for errors."));
			test(subName.Left(1).CompareF(KLowChar) >= 0 &&
					prevLocalName.Left(1).CompareF(KLowChar) < 0);
			test.Printf(_L("-- check okay."));
			
			// check that we can find an expected contact item in the subview
			// and cannot find something that should not be there.
			test.Printf(_L("Testing FindL() functionality."));
			test(iSubView->FindL(subCntItemId) == subViewIdx &&
					iSubView->FindL(iNamedLocalView->AtL(localViewIdx - 1)) == KErrNotFound);
			test.Printf(_L("-- check okay."));
			
			// check the AllFieldsLC functionality
			test.Printf(_L("Testing AllFieldsLC() functionality."));
			
			_LIT(KDelimiter, ";");
			HBufC* allFields = iSubView->AllFieldsLC(subViewIdx, KDelimiter());
			TPtrC allFieldsPtr = allFields->Des();

			TBufC<16> field0 = iSubView->ContactAtL(subViewIdx).Field(0);
			TBufC<16> field1 = iSubView->ContactAtL(subViewIdx).Field(1);
			TBufC<16> field2 = iSubView->ContactAtL(subViewIdx).Field(2);
			TBufC<16> field3 = iSubView->ContactAtL(subViewIdx).Field(3);
			
			TBuf<48> fieldsBuf;
			_LIT(KFormat, "%S;%S;%S;%S");
			fieldsBuf.AppendFormat(KFormat(), &field0, &field1, &field2, &field3);
			
			test(fieldsBuf.Compare(allFieldsPtr) == 0); // they should be the same
			test.Printf(_L("-- check okay."));
			CleanupStack::PopAndDestroy(allFields);
			}
		}
	}


void CSubViewTest::CheckSortOrderL() const
	{
	test.Printf(_L("Checking sort order"));
	RContactViewSortOrder sortOrder = iSubView->SortOrderL(); // don't take ownership
	test(
			sortOrder[iLnameIdx] == KUidContactFieldFamilyName &&
			sortOrder[iFnameIdx] == KUidContactFieldGivenName &&
			sortOrder[iCnameIdx] == KUidContactFieldCompanyName &&
			sortOrder[iNumIdx]   == KUidContactFieldPhoneNumber &&
			iSubView->ContactViewPreferences() == EContactsOnly
		);
	}


void CSubViewTest::ResortNamedLocalViewL()
	{
	test.Next(_L("Resorting local view."));


	RContactViewSortOrder sortOrder;
	CleanupClosePushL(sortOrder);
	sortOrder.AppendL(KUidContactFieldCompanyName);
	sortOrder.AppendL(KUidContactFieldGivenName);
	sortOrder.AppendL(KUidContactFieldFamilyName);
	sortOrder.AppendL(KUidContactFieldPhoneNumber);

	// set the field indexes to reflect the order above
	iCnameIdx = 0;
	iFnameIdx = 1;
	iLnameIdx = 2;
	iNumIdx   = 3;
	
	iNamedLocalView->ChangeSortOrderL(sortOrder);

	CleanupStack::PopAndDestroy(&sortOrder);
	}


void CSubViewTest::DoTestL()
	{
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
	SetActive();
	iState = ETestStarted;
	CActiveScheduler::Start();
	}


//////////////////////////////////////////////////////////////////////////////////////////////////
// main test functions
//////////////////////////////////////////////////////////////////////////////////////////////////

/**

@SYMTestCaseID     PIM-T-SUBVIEW-0001

*/

void DoTestsL()
    {
	test.Start(_L("@SYMTESTCaseID:PIM-T-SUBVIEW-0001 "));

	CSubViewTest* myTest = CSubViewTest::NewLC();
	myTest->DoTestL();

	// do some stuff here...

	CleanupStack::PopAndDestroy(myTest);
    test.End();
    test.Close();
    }

GLDEF_C TInt E32Main()
	{
    // Init
    __UHEAP_MARK; 
    CTrapCleanup* cleanupStack = CTrapCleanup::New();
    if (!cleanupStack)
        {
        return KErrNoMemory;
        }

    CActiveScheduler* activeScheduler = new CActiveScheduler;
    if (!activeScheduler)
        {
        return KErrNoMemory;
        }
    CActiveScheduler::Install(activeScheduler);

    // Run the tests
    TRAPD(err, DoTestsL());
    if (err)
    	{
    	TRAP_IGNORE(CContactDatabase::DeleteDatabaseL(KDbName) );
    	test(EFalse);
    	}
    
    // Cleanup
    delete activeScheduler;
    delete cleanupStack;
    __UHEAP_MARKEND;
	return err;
    }
