// 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 "agsinstanceiterator.h"
#include "agsinstancesorter.h"
#include "agmfilter.h"
#include "agmsortcriteria.h"
#include "agsentrymodel.h"
#include "agssortinstance.h"
#include "agmdate.h"
#include "agmpanic.h"
#include "agmsimpleentry.h"
#include "agsfilemanager.h"

const TInt KMaxCachedNumber = 1000; //The maxium number of instance IDs cahed in the server side iterator
const TInt KInitialInstanceCount = -1;
const TInt KInstanceArrayGranularity = 16;

// CAgsInstanceIterator //

CAgsInstanceIterator::CAgsInstanceIterator(TInt aIteratorId,  CAgnServFileMgr&	aFileManager)
	:iIteratorId(aIteratorId),
	iInstanceCount(KInitialInstanceCount),
	iCachedOffset(0),
	iFileManager(aFileManager)
	{
	}

CAgsInstanceIterator::~CAgsInstanceIterator()
	{
	iCachedInstances.Close();
	delete iFindParams;
	delete iSorter;
	iModels.Close();//Doesn't own the elements of the array.
	}

CAgsInstanceIterator* CAgsInstanceIterator::NewLC(TInt aIteratorId, TFindInstanceParams* aFindParams, CAgnSortCriteria* aSortCriteria, 
								const RArray<CCalEntry::TType>& aDefaultTypeOrder, RArray<TInt64>& aFileIds, CAgnServFileMgr&	aFileManager)
	{
	CAgsInstanceIterator* self = new (ELeave) CAgsInstanceIterator(aIteratorId, aFileManager);
	CleanupStack::PushL(self);
	self->ConstructL(aSortCriteria, aDefaultTypeOrder, aFindParams, aFileIds);
	return self;
	}

void CAgsInstanceIterator::ConstructL(CAgnSortCriteria* aSortCriteria, const RArray<CCalEntry::TType>& aDefaultTypeOrder, TFindInstanceParams* aFindParams, RArray<TInt64>& aFileIds)
	{
	iFindParams = NULL;
	iSorter = NULL;
	const TInt count = aFileIds.Count();
	for(TInt ii=0;ii<count;++ii)
		{
		CAgnServFile* file = iFileManager.GetFileL(aFileIds[ii]);
		iModels.AppendL(file->Model());
		}
	iSorter = CAgsInstanceSorter::NewL(aSortCriteria, aDefaultTypeOrder);
	iFindParams = aFindParams;
	}

void CAgsInstanceIterator::AddInstanceL(TAgnSortInstance& aSortInstance, RArray<TAgnInstance>& aInstances)
	{
	TAgnInstance instance;
	instance.iId = aSortInstance.InstanceIdL();
	instance.iCollectionId = aSortInstance.SimpleEntry().CollectionId();
	aInstances.AppendL(instance);
	}

/**
 * Get the instances started after specified index
@param aInstances reference to instance array that will store matched instances
@param aIndex The start index of instance to look for
*/
void CAgsInstanceIterator::NextInstancesL(RArray<TAgnInstance>& aInstances, TInt aIndex)
	{
	if(IsCached(aIndex, EFalse))
		{
		//The required instances are in cache, so suck out the instances
		const TInt KNumber(iFindParams->iNumInstances);
		while(0 < iCachedInstances.Count() && aInstances.Count() < KNumber)
			{
			if(iCachedOffset >= aIndex)
				{
				aInstances.AppendL(iCachedInstances[0]);
				}
			iCachedInstances.Remove(0);
			++iCachedOffset;
			}
		}
	else
		{
		//Either the cache is empty or out of range to aIndex so reloading all instance is needed
		
		//reset cache
		iCachedInstances.Reset();
		iCachedOffset = 0; 
		
		//Fetching and sorting instances
		CArrayFixSeg<TAgnSortInstance>* instances = FindSortedInstanceLC();
		
		const TInt KInstanceCount(instances->Count());
		if(iInstanceCount != KInstanceCount)
			{
			iInstanceCount = KInstanceCount;
			}
		
		if(aIndex < KInstanceCount)
			{
			//get rid of the instances that are before aIndex
			instances->Delete(0, aIndex);
			
			//appending the instances
			const TInt KMaxCount = (instances->Count() > iFindParams->iNumInstances) ? iFindParams->iNumInstances : instances->Count();
			for(TInt index = 0; index < KMaxCount; index++)
				{
				AddInstanceL((*instances)[index], aInstances);
				}
			
			if(KMaxCount == iFindParams->iNumInstances)
				{
				//cache the rest instances which are fetched and indexed after aIndex 
				instances->Delete(0, KMaxCount);
				const TInt KCachedCount = (instances->Count() > KMaxCachedNumber) ? KMaxCachedNumber : instances->Count();  
				
				for(TInt index = 0; index < KCachedCount; index++)
					{
					AddInstanceL((*instances)[index], iCachedInstances);
					}
				
				iCachedOffset = aIndex + KMaxCount;
				}
			}
		
		CleanupStack::PopAndDestroy(instances);
		}
	}

/**
 * Get the instances indexed before aIndex
@param aInstances reference to instance array that will store matched instances
@param aIndex The end index of instances to look for
*/
void CAgsInstanceIterator::PreviousInstancesL(RArray<TAgnInstance>& aInstances, TInt aIndex)
	{
	if(IsCached(aIndex, ETrue))
		{
		//The required instances are in cache, so suck out the instances
		const TInt KNumber(iFindParams->iNumInstances);
		TInt startIndex = (aIndex > KNumber) ? (aIndex - KNumber) : 0;   
		startIndex -= iCachedOffset;   
		
		while(iCachedInstances.Count() > startIndex)
			{
			if(aInstances.Count() < KNumber)
				{
				aInstances.AppendL(iCachedInstances[startIndex]);
				}
			iCachedInstances.Remove(startIndex);
			}
		}
	else
		{
		//Either the cache is empty or out of range to aIndex so reloading all instance is needed
		
		//reset cache
		iCachedInstances.Reset();
		iCachedOffset = 0;
		
		//reloading instances
		CArrayFixSeg<TAgnSortInstance>* instances = FindSortedInstanceLC();

		const TInt KInstanceCount(instances->Count());
		if(iInstanceCount != KInstanceCount)
			{
			iInstanceCount = KInstanceCount;
			}
		
		//Adding the instances indexed before aIndex to aInstances
		TInt startIndex = (aIndex > iFindParams->iNumInstances) ? aIndex - iFindParams->iNumInstances : 0;   	
		TInt endIndex = (aIndex < instances->Count()) ? aIndex : instances->Count() - 1;  
		
		__ASSERT_DEBUG(endIndex >= startIndex, Panic(EAgmErrOutsideArrayRange));
		
		for(TInt index = startIndex; index <= endIndex; index++)
			{
			AddInstanceL((*instances)[index], aInstances);
			}
		
		//Cache in the rest instances 
		iCachedOffset = (startIndex > KMaxCachedNumber) ? startIndex - KMaxCachedNumber : 0;
		
		for(TInt index = iCachedOffset; index < startIndex; index++)
			{
			AddInstanceL((*instances)[index], iCachedInstances);
			}
		
		CleanupStack::PopAndDestroy(instances);
		}
	}

/**
 * Get the instances started after specified index
@return The number of overall instances which shall be contained in the iterator  
*/
TInt CAgsInstanceIterator::CountL()
	{
	if(iInstanceCount == KInitialInstanceCount)
		{
		CArrayFixSeg<TAgnSortInstance>* instances = NULL;
		if(iCachedInstances.Count() == 0 && iCachedOffset == 0)
			{
			instances = FindSortedInstanceLC();
			//cache is empty so just cached in the first part of instances
			const TInt KCachedCount = (iInstanceCount > KMaxCachedNumber) ? KMaxCachedNumber : iInstanceCount;  
			
			for(TInt index = 0; index < KCachedCount; index++)
				{
				AddInstanceL((*instances)[index], iCachedInstances);
				}
			}
		else
			{
			const TInt count = iModels.Count();
			instances = new (ELeave) CArrayFixSeg<TAgnSortInstance>(KInstanceArrayGranularity);
			CleanupStack::PushL(instances);
			for(TInt ii =0; ii<count; ++ii )
				{
				iModels[ii]->FindInstancesL(*instances, *iFindParams);
				}
			}
		iInstanceCount = instances->Count();
		CleanupStack::PopAndDestroy(instances);
		}
	
	return iInstanceCount; 
	}

CArrayFixSeg<TAgnSortInstance>* CAgsInstanceIterator::FindSortedInstanceLC()
	{
	const TInt count = iModels.Count();
	CArrayFixSeg<TAgnSortInstance>* instances = new (ELeave) CArrayFixSeg<TAgnSortInstance>(KInstanceArrayGranularity);
	CleanupStack::PushL(instances);

	for(TInt ii =0; ii<count; ++ii )
		{
		if(iModels[ii]->AgnServFile().IsLocked())
			{
			User::Leave(KErrLocked);
			}
		iModels[ii]->FindInstancesL(*instances, *iFindParams);
		}
	iSorter->SortL(*instances);
	return instances;
	}


TInt CAgsInstanceIterator::LocateIndexL(TAgnInstance& aInstanceId)
	{
	__ASSERT_DEBUG(!aInstanceId.iId.IsNullId(), Panic(EAgmErrInstanceIteratorLocatedIndexFault));
	
	//We are assuming the function is called just after the iterator is created
	__ASSERT_DEBUG(iInstanceCount == KInitialInstanceCount && 
			       iCachedInstances.Count() == 0 && 
			       iCachedOffset == 0, Panic(EAgmErrInstanceIteratorLocatedIndexFault));
	
	//Find all instances in the iterator and sort them
	CArrayFixSeg<TAgnSortInstance>* instances = FindSortedInstanceLC();
	iInstanceCount = instances->Count();
	
	//Find the index of the given instance id.
	CAgnEntryModel* model = iFileManager.GetFileL(aInstanceId.iCollectionId)->Model();

	const CAgnSimpleEntry* searchFromEntry = model->GetSimpleEntryFromIndexes(aInstanceId.iId);
	const_cast<CAgnSimpleEntry*>(searchFromEntry)->SetCollectionId(aInstanceId.iCollectionId);
	
	TAgnSortInstance searchFromInstance(*searchFromEntry);
	
	searchFromInstance.SetL(aInstanceId.iId.Date().LocalL(), iFindParams->iUndatedTodoTimeLocal);
	
	//the search from instance must match the filter settings
	__ASSERT_ALWAYS(searchFromInstance.CheckStartAndEndDateOverlap(*iFindParams), User::Leave(KErrNotFound));

	TInt foundIndex = iSorter->FindL(*instances, searchFromInstance);
	
	//cache the instances on server side
	TInt endIndex;
	if(iInstanceCount > KMaxCachedNumber)
		{
		//The instances are more than the size of cache so just store those after the given instance
		iCachedOffset = foundIndex;
		endIndex = (iInstanceCount - foundIndex) > KMaxCachedNumber ? (foundIndex + KMaxCachedNumber) : iInstanceCount; 
		}
	else
		{
		//cache all instances
		iCachedOffset = 0;
		endIndex = iInstanceCount; 
		}
	
	for(TInt index = iCachedOffset; index < endIndex; index++)
		{
		AddInstanceL((*instances)[index], iCachedInstances);
		}

	CleanupStack::PopAndDestroy(instances);
	
	return foundIndex;
	}


/**
 * Check if the instance at aIndex is cached and the cached instances are
 * enough for fetch(more than iFindParams->iNumInstances)
*/
TBool CAgsInstanceIterator::IsCached(TInt aIndex, TBool aPrevious) const
	{
	const TInt KNumber(iFindParams->iNumInstances);
	TInt startIndex(aIndex);
	if(aPrevious)
		{
		startIndex = (aIndex > KNumber) ? (aIndex - KNumber) : 0;   
		}
	
	return ((iCachedOffset <= startIndex) && (iCachedInstances.Count() >= KNumber));
	}	

// CAgsInstanceIteratorMgr //

CAgsInstanceIteratorMgr* CAgsInstanceIteratorMgr::NewL()
	{
	CAgsInstanceIteratorMgr* self = new (ELeave) CAgsInstanceIteratorMgr();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CAgsInstanceIteratorMgr::CAgsInstanceIteratorMgr()
	:iNextAvailableId(0) 
	{
	}

void CAgsInstanceIteratorMgr::ConstructL()
	{
	//buid up the default sort order for sorting entry type
	iDefaultTypeOrder.AppendL(CCalEntry::ETodo);
	iDefaultTypeOrder.AppendL(CCalEntry::EAnniv);
	iDefaultTypeOrder.AppendL(CCalEntry::EEvent);
	iDefaultTypeOrder.AppendL(CCalEntry::EAppt);
	iDefaultTypeOrder.AppendL(CCalEntry::EReminder);
	}

CAgsInstanceIteratorMgr::~CAgsInstanceIteratorMgr()
	{
	//release all iterators
	TInt KCount(iIteratorList.Count());
	for(TInt count = 0; count < KCount; count++)
		{
		delete iIteratorList[count]; 
		}	
	iIteratorList.Close();
	iDefaultTypeOrder.Close();
	}

/**
 * Create an iterator
@param aFindParams Finding instance settings/search criteria
@param aSortCriteria Sort criteria for the iterator
@return Iterator id assigned to the new iterator
*/
TInt CAgsInstanceIteratorMgr::CreateIteratorL(TFindInstanceParams* aFindParams, CAgnSortCriteria* aSortCriteria, RArray<TInt64>& aFileIds, CAgnServFileMgr& aFileManager)
	{
	CAgsInstanceIterator* iterator = CAgsInstanceIterator::NewLC(iNextAvailableId, aFindParams, aSortCriteria, iDefaultTypeOrder, aFileIds, aFileManager);
	iIteratorList.AppendL(iterator);
	CleanupStack::Pop(iterator);
	return iNextAvailableId++; 
	}

TInt CAgsInstanceIteratorMgr::FindIteratorById(TInt aIteratorId) const
	{
	TInt ret(KErrNotFound);
	const TInt KCount(iIteratorList.Count());
	for(TInt index = 0; index < KCount; index++)
		{
		if(iIteratorList[index]->IteratorId() == aIteratorId)
			{
			ret = index;
			break;
			}
		}
	return ret;
	}


/**
 * Remove and destroy and remove an iterator
@param Iterator id of the iterator to be destroyed
*/
void CAgsInstanceIteratorMgr::DestroyIteratorL(TInt aIteratorId)
	{
	TInt index = FindIteratorById(aIteratorId);
	User::LeaveIfError(index);
	
	CAgsInstanceIterator* iterator = iIteratorList[index];
	iIteratorList.Remove(index);
	delete iterator;
	}

void CAgsInstanceIteratorMgr::NextInstancesL(TInt aIteratorId, RArray<TAgnInstance>& aInstances, TInt aStartIndex)
	{
	TInt index = FindIteratorById(aIteratorId);
	User::LeaveIfError(index);
	
	iIteratorList[index]->NextInstancesL(aInstances, aStartIndex);
	}
	
void CAgsInstanceIteratorMgr::PreviousInstancesL(TInt aIteratorId, RArray<TAgnInstance>& aInstances, TInt aLastIndex)
	{
	TInt index = FindIteratorById(aIteratorId);
	User::LeaveIfError(index);
	
	iIteratorList[index]->PreviousInstancesL(aInstances, aLastIndex);
	}

TInt CAgsInstanceIteratorMgr::CountL(TInt aIteratorId)
	{
	TInt index = FindIteratorById(aIteratorId);
	User::LeaveIfError(index);
	
	return iIteratorList[index]->CountL();
	}

TInt CAgsInstanceIteratorMgr::LocateIndexL(TInt aIteratorId, TAgnInstance& aInstanceId)
	{
	TInt index = FindIteratorById(aIteratorId);
	User::LeaveIfError(index);
	
	return iIteratorList[index]->LocateIndexL(aInstanceId);
	}


