// 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:
//

#include <vtzrules.h>
#include <tz.h>
#include "agmentry.h"
#include "agmtlsproxy.h"
#include "agmtzrules.h"
#include "agstzruleindex.h"
#include "agmpanic.h"
#include "agmrptdef.h"
#include "agsfilemanager.h"

const TUid KUidAgnSystemTzIndex = {0x10285b96};
const TUid KUidAgnImportedTzIndex = {0x10285b97};
const TUid KUidAgnTimeStamp = {0x102863fb};

const TInt KMinYear = 0;
const TInt KMaxYear = 9999;

/* ------------------- CAgnTzRuleIndexItem -------------------*/
CAgnTzRuleIndexItem::CAgnTzRuleIndexItem(TStreamId aStreamId, CTzRules* aRule) :
	iRefCount(1), iTzRules(aRule)
	{
	iStreamId = aStreamId;
	}

CAgnTzRuleIndexItem::CAgnTzRuleIndexItem() :
	iRefCount(1), iTzRules(NULL)
	{
	}

CAgnTzRuleIndexItem* CAgnTzRuleIndexItem::NewL(RReadStream& aStream)
	{
	CAgnTzRuleIndexItem* self = new (ELeave) CAgnTzRuleIndexItem();
	CleanupStack::PushL(self);
	self->InternalizeL(aStream);
	CleanupStack::Pop(self);
	return self;
	}

CAgnTzRuleIndexItem* CAgnTzRuleIndexItem::NewL(TStreamId aStreamId, CTzRules* aTzRule)
	{
	CAgnTzRuleIndexItem* self = new (ELeave) CAgnTzRuleIndexItem(aStreamId, aTzRule);
	return self;
	}

CAgnTzRuleIndexItem::~CAgnTzRuleIndexItem()
	{
	delete iTzRules;
	}

void CAgnTzRuleIndexItem::IncrementRef()
	{
	++iRefCount;
	}

TInt CAgnTzRuleIndexItem::DecrementRef()
	{
	__ASSERT_DEBUG(iRefCount>0, User::Invariant());
	return --iRefCount;
	}

void CAgnTzRuleIndexItem::ExternalizeL(RWriteStream& aStream) const
	{
	aStream.WriteInt16L(iRefCount);
	aStream << iStreamId;
	}

void CAgnTzRuleIndexItem::InternalizeL(RReadStream& aStream)
	{
	iRefCount = aStream.ReadInt16L();
	aStream >> iStreamId;
	}

void CAgnTzRuleIndexItem::SetRule(CTzRules* aTzRules)
	{
	if (iTzRules)
		{
		delete iTzRules;
		}
	iTzRules = aTzRules;
	}

CTzRules* CAgnTzRuleIndexItem::TzRule() const
	{
	return iTzRules;
	}

const TStreamId& CAgnTzRuleIndexItem::StreamId() const
	{
	return iStreamId;
	}

TBool CAgnTzRuleIndexItem::IsSystemTzRule() const
	{
	return EFalse;
	}

/* ------------------- CAgnSystemTzRuleIndexItem -------------------*/
void CAgnSystemTzRuleIndexItem::ConstructL(const TDesC8& aTzId)
	{
	iTzId = aTzId.AllocL();
	}

CAgnSystemTzRuleIndexItem* CAgnSystemTzRuleIndexItem::NewL(TStreamId aStreamId,	const TDesC8& aTzId, CTzRules* aRule)
	{
	CAgnSystemTzRuleIndexItem* self = new (ELeave) CAgnSystemTzRuleIndexItem(aStreamId, aRule);
	CleanupStack::PushL(self);
	self->ConstructL(aTzId);
	CleanupStack::Pop(self);
	return self;
	}

CAgnSystemTzRuleIndexItem::CAgnSystemTzRuleIndexItem() :
	CAgnTzRuleIndexItem(), iTzId(NULL)
	{
	}

CAgnSystemTzRuleIndexItem::CAgnSystemTzRuleIndexItem(TStreamId aStreamId,
		CTzRules* aRules) :
	CAgnTzRuleIndexItem(aStreamId, aRules), iTzId(NULL)
	{
	}

CAgnSystemTzRuleIndexItem::~CAgnSystemTzRuleIndexItem()
	{
	delete iTzId;
	}

CAgnSystemTzRuleIndexItem* CAgnSystemTzRuleIndexItem::NewL(RReadStream& aStream)
	{
	CAgnSystemTzRuleIndexItem* self = new (ELeave) CAgnSystemTzRuleIndexItem();
	CleanupStack::PushL(self);
	self->InternalizeL(aStream);
	CleanupStack::Pop(self);
	return self;
	}

const TDesC8& CAgnSystemTzRuleIndexItem::TzId() const
	{
	return *iTzId;
	}

void CAgnSystemTzRuleIndexItem::ExternalizeL(RWriteStream& aStream) const
	{
	CAgnTzRuleIndexItem::ExternalizeL(aStream);
	aStream << *iTzId;
	}

void CAgnSystemTzRuleIndexItem::InternalizeL(RReadStream& aStream)
	{
	CAgnTzRuleIndexItem::InternalizeL(aStream);
	iTzId = HBufC8::NewL(aStream, KMaxTInt);
	}

TBool CAgnSystemTzRuleIndexItem::IsSystemTzRule() const
	{
	return ETrue;
	}

/* ------------------- CAgnTzRuleIndex -------------------*/
CAgnTzRuleIndex::CAgnTzRuleIndex(CStreamDictionary& aDictionary, CFileStore& aStore) :
	iDictionary(&aDictionary),
	iStore(&aStore),
	iPosCurSystemTzRule(KErrNotFound),
	iPosFetchedLastTime(0),
	iTzDbChangeTimeStamp(Time::NullTTime()),
	iTzRulesLastModifiedTime(Time::NullTTime())
	{
	}

CAgnTzRuleIndex* CAgnTzRuleIndex::NewL(CStreamDictionary& aDictionary, CFileStore& aStore)
	{
	CAgnTzRuleIndex* self = new(ELeave) CAgnTzRuleIndex(aDictionary, aStore);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return (self);
	}

void CAgnTzRuleIndex::ConstructL()
	{
	iTimeZoneAccessor = CAgnTlsProxy::CreateL(CAgnTlsProxy::TAgnTlsTzRulesType_Server);

	iSystemRuleStreamId = ReadItemsFromStoreL(KUidAgnSystemTzIndex);
	iUserDefinedRuleStreamId = ReadItemsFromStoreL(KUidAgnImportedTzIndex);

	RTz& tzServer = iTimeZoneAccessor->TzServer();
	CTzId* tzid = tzServer.GetTimeZoneIdL();
	CleanupStack::PushL(tzid);
	HandleCurrentTimeZoneChangeL(*tzid);
	CleanupStack::PopAndDestroy(tzid);
	}

/* Read the time stamp of time zone database has been modified last time.

- If the file is newly created, the time stamp will be set to current time
- If the file is re-opened, read the time stamp from the file.
- If the file is re-opened, but there isn't a time stamp, the time stamp will be set to current time
 */
void CAgnTzRuleIndex::ReadTimeStampL()
	{
	TStreamId streamId = iDictionary->At(KUidAgnTimeStamp);

	if (streamId == KNullStreamId)
		{//There is not a time stamp in the file either it is newly created or an existing one.
		TTime currentTime;
		currentTime.UniversalTime();
		iTzDbChangeTimeStamp = currentTime;

		if (RefreshTzRulesL())
			{
			iTzRulesLastModifiedTime = currentTime;
			}
		PersistToStoreL(KUidAgnTimeStamp);
		}
	else
		{//iTzChangTimeStamp is read from file
		ReadFromStoreL(KUidAgnTimeStamp, EFalse);
		}
	}

/** Check if the time zone server has been modified since last time the
 * Calendar server shut down. If so, the time zones of entries cached in the Calendar
 * will be updated. Calendar clients will be also notified with the change if
 * any of time zones associated with Calendar entries have been modified.
 *
 * This function is called when a Calendar file is opened.
 *
 */
void CAgnTzRuleIndex::CheckTzDbModificationL(CAgnServFile& aAgnServFile)
	{
	iAgnFile = &aAgnServFile;
	ReadTimeStampL();
	//This will result in HandleTzRulesChangeL to be called immediatly if a tz change has been published.
	iTimeZoneAccessor->AddSystemTimeObserverL(this);
	}
/*
 Called when the file is closed. It will write the current time to the Calendar file.
 */
void CAgnTzRuleIndex::WriteTimeStampL()
	{
	TStreamId streamId = iDictionary->At(KUidAgnTimeStamp);
	RStoreWriteStream writeStream;
	writeStream.ReplaceLC(*iStore, streamId);
	ExternalizeTimesL(writeStream);
	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);
	iStore->CommitL();

	}
void CAgnTzRuleIndex::ExternalizeTimesL(RStoreWriteStream& aWriteStream) const
	{
	aWriteStream << I64LOW(iTzDbChangeTimeStamp.Int64());
	aWriteStream << I64HIGH(iTzDbChangeTimeStamp.Int64());
	aWriteStream << I64LOW(iTzRulesLastModifiedTime.Int64());
	aWriteStream << I64HIGH(iTzRulesLastModifiedTime.Int64());
	}

void CAgnTzRuleIndex::InternalizeTimesL(RReadStream& aReadStream)
	{
	TUint32 low = aReadStream.ReadUint32L();
	TUint32 high = aReadStream.ReadUint32L();
	iTzDbChangeTimeStamp = TTime(MAKE_TINT64(high, low));

	low = aReadStream.ReadUint32L();
	high = aReadStream.ReadUint32L();
	iTzRulesLastModifiedTime = TTime(MAKE_TINT64(high, low));
	}

/* Externalise an array of tz rules or a time stamp to first time Calendar file.

 @param aUid refer to uid of object to write to the file.
 @return Stream Id refer to the object being written to the file
 */
TStreamId CAgnTzRuleIndex::PersistToStoreL(const TUid& aUid)
	{
	RStoreWriteStream writeStream;
	TStreamId streamId = writeStream.CreateLC(*iStore);
	if (aUid == KUidAgnSystemTzIndex)
		{
		ExternalizeRulesL(writeStream, iSystemTzRules);
		}
	else
		if (aUid == KUidAgnImportedTzIndex)
			{
			ExternalizeRulesL(writeStream, iUserDefinedTzRules);
			}
		else
			{
			__ASSERT_ALWAYS(aUid == KUidAgnTimeStamp,User::Invariant());
			ExternalizeTimesL(writeStream);
			}

	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);

	iDictionary->AssignL(aUid, streamId);

	// Re-write the dictionary stream
	RStoreWriteStream stream;
	TStreamId rootStreamId = iStore->Root();
	stream.ReplaceLC(*iStore, rootStreamId);
	stream<< *iDictionary;
	stream.CommitL();
	CleanupStack::PopAndDestroy(); // dictionary stream
	iStore->CommitL();
	return streamId;
	}

void CAgnTzRuleIndex::ReadFromStoreL(const TUid& aUid, TBool aRollback)
	{
	TStreamId streamId = iDictionary->At(aUid);
	__ASSERT_ALWAYS(streamId != KNullStreamId, User::Leave(KErrNotFound));
	RStoreReadStream readStream;
	readStream.OpenLC(*iStore, streamId);
	if (aUid == KUidAgnSystemTzIndex)
		{
		InternalizeRulesL(readStream, ETrue, aRollback);
		}
	else
		if (aUid == KUidAgnImportedTzIndex)
			{
			InternalizeRulesL(readStream, EFalse, aRollback);
			}
		else
			{
			__ASSERT_ALWAYS(aUid==KUidAgnTimeStamp,User::Invariant());
			InternalizeTimesL(readStream);
			}

	CleanupStack::PopAndDestroy(&readStream);
	}

TStreamId CAgnTzRuleIndex::ReadItemsFromStoreL(const TUid& aUid, TBool aRollback)
	{
	TStreamId streamId = iDictionary->At(aUid);

	if (streamId == KNullStreamId)
		{
		streamId = PersistToStoreL(aUid);
		}
	else
		{
		ReadFromStoreL(aUid, aRollback);
		}

	return streamId;
	}

void CAgnTzRuleIndex::InternalizeRulesL(RReadStream& aStream,
		TBool aIsSystemRule, TBool aRollback)
	{
	TInt count = aStream.ReadInt16L();
	CAgnTzRuleIndexItem* item= NULL;
	for (TInt ii=0; ii<count; ++ii)
		{
		if (aIsSystemRule)
			{
			item = CAgnSystemTzRuleIndexItem::NewL(aStream);
			}
		else
			{
			item = CAgnTzRuleIndexItem::NewL(aStream);
			}

		CleanupStack::PushL(item);
		RStoreReadStream readStream;

		readStream.OpenLC(*iStore, item->StreamId());
		CTzRules* tzRules = CTzRules::NewL(readStream);
		item->SetRule(tzRules);
		CleanupStack::PopAndDestroy(&readStream);

		if (aIsSystemRule)
			{
			if (aRollback)
				{
				CleanupStack::Pop(item);
				RollbackItemL(iSystemTzRules, item);
				}
			else
				{
				iSystemTzRules.AppendL(item);
				CleanupStack::Pop(item);
				}
			}
		else
			{
			if (aRollback)
				{
				CleanupStack::Pop(item);
				RollbackItemL(iUserDefinedTzRules, item);
				}
			else
				{
				iUserDefinedTzRules.AppendL(item);
				CleanupStack::Pop(item);
				}
			}
		}
	}

void CAgnTzRuleIndex::ExternalizeRulesL(RWriteStream& aStream, const RPointerArray<CAgnTzRuleIndexItem>& aArray) const
	{
	TInt count = aArray.Count();
	aStream.WriteInt16L(count);

	for (TInt ii=0; ii<count; ++ii)
		{
		aArray[ii]->ExternalizeL(aStream);
		}
	}

CAgnTzRuleIndex::~CAgnTzRuleIndex()
	{
	iSystemTzRules.ResetAndDestroy();
	iUserDefinedTzRules.ResetAndDestroy();
	iTimeZoneAccessor->RemoveSystemTimeObserver(this);
	CAgnTlsProxy::Release(iTimeZoneAccessor);
	}

/**
 It is called when:

 Creating a light entry during index building.
 Fetch an entry

 @param reference to the entry fetching tz rules
 */
void CAgnTzRuleIndex::FetchTzRuleL(CAgnSimpleEntry& aEntry)
	{
	CAgnTzRules* tzZone = GetTzRulesFromEntry(aEntry);
	if (tzZone == NULL)
		{
		return;
		}

	TInt pos = KErrNotFound;
	if (tzZone->SystemTzRule())
		{
		pos = FindTzRuleByStreamIdL(tzZone->TzZoneStreamId(), iSystemTzRules,
				ETrue);
		if (pos>=0)
			{
			tzZone->SetTzRules(iSystemTzRules[pos]->TzRule());
			tzZone->SetUseCurrentSystemTzRules((pos == iPosCurSystemTzRule));
			}
		}
	else
		{
		__ASSERT_DEBUG(!tzZone->UseCurrentSystemTzRules(), User::Invariant());
		pos = FindTzRuleByStreamIdL(tzZone->TzZoneStreamId(),
				iUserDefinedTzRules, ETrue);
		if (pos>=0)
			{
			tzZone->SetTzRules(iUserDefinedTzRules[pos]->TzRule());
			}
		}

	if (pos == KErrNotFound)
		{
		AddCurrentSystemTzRuleL(*tzZone);
		}
	}

/**
 Remove a tz rule from one of tz rule arraies

 It should be called when an entry is removed or an entry is updated

 @param reference to the entry to be deleted
 */
void CAgnTzRuleIndex::RemoveTzRuleL(CAgnSimpleEntry& aEntry)
	{
	CAgnTzRules* tzZone = GetTzRulesFromEntry(aEntry);
	if (tzZone == NULL || !tzZone->HasValidTzZoneStreamId())
		{
		return;
		}

	TInt pos = KErrNotFound;
	if (tzZone->SystemTzRule())
		{
		pos = FindTzRuleByStreamIdL(tzZone->TzZoneStreamId(), iSystemTzRules,
				EFalse);
		if (pos >=0)
			{
			DeleteARuleL(pos, iSystemTzRules, iSystemRuleStreamId);
			}
		}
	else
		{
		pos = FindTzRuleByStreamIdL(tzZone->TzZoneStreamId(),
				iUserDefinedTzRules, EFalse);
		if (pos >=0)
			{
			DeleteARuleL(pos, iUserDefinedTzRules, iUserDefinedRuleStreamId);
			}
		}

	__ASSERT_DEBUG(pos != KErrNotFound, Panic(EAgmErrTzRuleIsNotFound));

	User::LeaveIfError(pos);
	}

/**
 Add a tz rule to the array if it is not existing in the array otherwise
 increase the refference count for that rule.

 It is called when:

 An entry is added with a user defined tz rule
 An old format file (9.4 prior) is converted to a new format

 @param reference to the entry to be added.
 */
void CAgnTzRuleIndex::AddTzRuleL(CAgnEntry& aEntry)
	{
	CAgnTzRules* tzZone = GetTzRulesFromEntry(aEntry);
	if (tzZone == NULL)
		{
		return;
		}

	if (tzZone->TzRules() == NULL || tzZone->UseCurrentSystemTzRules())
		{
		AddCurrentSystemTzRuleL(*tzZone);
		return;
		}

	TBool isSysRule = EFalse;
	TInt pos = FindRule(*tzZone->TzRules(), isSysRule);
	if (pos >= 0)
		{
		if (isSysRule)
			{
			iSystemTzRules[pos]->IncrementRef();
			}
		else
			{
			iUserDefinedTzRules[pos]->IncrementRef();
			}

		}
	else
		{
		CTzRules* tzRule = tzZone->TzRules()->CloneL();
		CleanupStack::PushL(tzRule);
		TStreamId streamId = PersistTzRuleL(*tzRule);//It will not be commited until model commit the entry into the file
		CAgnTzRuleIndexItem* item = CAgnTzRuleIndexItem::NewL(streamId, tzRule);
		CleanupStack::Pop(tzRule);

		CleanupStack::PushL(item);
		iUserDefinedTzRules.AppendL(item);
		pos = iUserDefinedTzRules.Count()-1;
		CleanupStack::Pop(item);
		}

	if (isSysRule)
		{
		UpdateItemsInStoreL(iSystemTzRules, iSystemRuleStreamId);
		tzZone->SetTzRules(iSystemTzRules[pos]->TzRule());
		tzZone->SetSystemTzRule(ETrue);
		tzZone->SetTzZoneStreamId(iSystemTzRules[pos]->StreamId());
		}
	else
		{
		UpdateItemsInStoreL(iUserDefinedTzRules, iUserDefinedRuleStreamId);
		tzZone->SetTzRules(iUserDefinedTzRules[pos]->TzRule());
		tzZone->SetSystemTzRule(EFalse);
		tzZone->SetTzZoneStreamId(iUserDefinedTzRules[pos]->StreamId());
		}
	}

/**
 Called when entry's tz rule changed

 @param aOldEntry reference to the original entry to be updated.
 @param aNewEntry reference to the entry containing updated content.
 */
void CAgnTzRuleIndex::UpdateTzRuleL(CAgnEntry& aOldEntry, CAgnEntry& aNewEntry)
	{
	CAgnTzRules* newTzZone = GetTzRulesFromEntry(aNewEntry);
	if (newTzZone != NULL && newTzZone->HasValidTzZoneStreamId())
		{
		TInt pos = KErrNotFound;
		if (newTzZone->SystemTzRule())
			{
			pos = FindTzRuleByStreamIdL(newTzZone->TzZoneStreamId(), iSystemTzRules, EFalse);
			iSystemTzRules[pos]->IncrementRef();
			newTzZone->SetTzRules(iSystemTzRules[pos]->TzRule());
			UpdateItemsInStoreL(iSystemTzRules, iSystemRuleStreamId);
			}
		else
			{
			pos = FindTzRuleByStreamIdL(newTzZone->TzZoneStreamId(), iUserDefinedTzRules, EFalse);
			iUserDefinedTzRules[pos]->IncrementRef();
			newTzZone->SetTzRules(iUserDefinedTzRules[pos]->TzRule());
			UpdateItemsInStoreL(iUserDefinedTzRules, iUserDefinedRuleStreamId);
			}

		__ASSERT_DEBUG(pos !=KErrNotFound, Panic(EAgmErrTzRuleIsNotFound));
		}
	else
		{
		AddTzRuleL(aNewEntry);
		}

	RemoveTzRuleL(aOldEntry);
	}

void CAgnTzRuleIndex::UpdateItemsInStoreL(
		RPointerArray<CAgnTzRuleIndexItem>& aArray, TStreamId aStreamId)
	{
	RStoreWriteStream writeStream;
	writeStream.ReplaceLC(*iStore, aStreamId);
	ExternalizeRulesL(writeStream, aArray);
	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);
	}

TInt CAgnTzRuleIndex::FindTzRuleByStreamIdL(TStreamId aStreamId, RPointerArray<CAgnTzRuleIndexItem>& aArray, TBool aTryPosLastTime)
	{
	TInt count = aArray.Count();
	if (aTryPosLastTime && iPosFetchedLastTime >= 0 && count
			> iPosFetchedLastTime)
		{
		if (aStreamId == aArray[iPosFetchedLastTime]->StreamId())
			{
			return iPosFetchedLastTime;
			}
		}

	TInt pos = KErrNotFound;
	TInt ii = 0;
	while (pos == KErrNotFound && ii < count)
		{
		if (aStreamId == aArray[ii]->StreamId())
			{
			pos = ii;
			}
		++ii;
		}

	if (aTryPosLastTime)
		{
		iPosFetchedLastTime = pos;
		}

	return pos;
	}

TInt CAgnTzRuleIndex::FindRule(RPointerArray<CAgnTzRuleIndexItem>& aRuleArray, const CTzRules& aRule)
	{
	TInt pos = KErrNotFound;
	TInt count = aRuleArray.Count();
	TInt ii = 0;
	while (pos < 0 && ii<count)
		{
		if (aRule.IsEqualTo(*(aRuleArray[ii]->TzRule())))
			{
			pos = ii;
			}
		++ii;
		}

	return pos;
	}

TInt CAgnTzRuleIndex::FindRule(const CTzRules& aTzRule, TBool& aIsSystemRule)
	{
	TInt pos = KErrNotFound;
	pos = FindRule(iSystemTzRules, aTzRule);
	if (pos>=0)
		{
		aIsSystemRule = ETrue;
		}
	else
		{
		pos = FindRule(iUserDefinedTzRules, aTzRule);
		if (pos>=0)
			{
			aIsSystemRule = EFalse;
			}
		}
	return pos;
	}

/**
 Add the current system tz rule to the array if it is not there otherwise
 increase the refference count for the rule.


 It is called when:

 An entry is added with the current tz rule
 @param reference to a CAgnTzRules object which is added to the index
 */
void CAgnTzRuleIndex::AddCurrentSystemTzRuleL(CAgnTzRules& aTzZone)
	{
	if (iPosCurSystemTzRule >= 0)
		{
		iSystemTzRules[iPosCurSystemTzRule]->IncrementRef();
		}
	else
		{
		RTz& tzServer = iTimeZoneAccessor->TzServer();
		CTzId* tzId = tzServer.GetTimeZoneIdL();
		CleanupStack::PushL(tzId);
		CTzRules* rule = tzServer.GetTimeZoneRulesL(*tzId, KMinYear, KMaxYear, ETzUtcTimeReference);
		CleanupStack::PushL(rule);
		TStreamId streamId = PersistTzRuleL(*rule);//It will not be commited until model commit the entry into the file
		CAgnSystemTzRuleIndexItem* item = CAgnSystemTzRuleIndexItem::NewL(streamId, tzId->TimeZoneNameID(), rule);
		CleanupStack::Pop(rule);
		CleanupStack::PushL(item);
		iSystemTzRules.AppendL(item);
		iPosCurSystemTzRule = iSystemTzRules.Count()-1;
		CleanupStack::Pop(item);

		CleanupStack::PopAndDestroy(tzId);
		}

	UpdateItemsInStoreL(iSystemTzRules, iSystemRuleStreamId);
	aTzZone.SetTzZoneStreamId(iSystemTzRules[iPosCurSystemTzRule]->StreamId());
	aTzZone.SetTzRules(iSystemTzRules[iPosCurSystemTzRule]->TzRule());
	aTzZone.SetSystemTzRule(ETrue);
	aTzZone.SetUseCurrentSystemTzRules(ETrue);
	}

void CAgnTzRuleIndex::DeleteARuleL(TInt aPos, RPointerArray<CAgnTzRuleIndexItem>& aArray, TStreamId aStreamId)
	{
	TInt ref = aArray[aPos]->DecrementRef();
	if (ref ==0)
		{
		iStore->DeleteL(aArray[aPos]->StreamId());
		delete aArray[aPos];
		aArray.Remove(aPos);

		if (aPos == iPosCurSystemTzRule)
			{
			iPosCurSystemTzRule = KErrNotFound;
			}
		else if (aPos < iPosCurSystemTzRule)
			{
			--iPosCurSystemTzRule;
			}
		}

	UpdateItemsInStoreL(aArray, aStreamId);
	}

TStreamId CAgnTzRuleIndex::PersistTzRuleL(const CTzRules& aTzRule)
	{
	TStreamId streamId = iStore->ExtendL();
	RStoreWriteStream writeStream;
	writeStream.ReplaceLC(*iStore, streamId);
	aTzRule.ExternalizeL(writeStream);
	writeStream.CommitL();//Don't commit to store until the CAgnEntryModel does it
	CleanupStack::PopAndDestroy(&writeStream);
	return streamId;
	}

void CAgnTzRuleIndex::HandleCurrentTimeZoneChangeL(const CTzId& aSystemTzId)
	{
	if (iPosCurSystemTzRule != KErrNotFound && 0 == static_cast<CAgnSystemTzRuleIndexItem*>(iSystemTzRules[iPosCurSystemTzRule])->TzId().Compare(aSystemTzId.TimeZoneNameID()))
		{
		//the rule doesn't change.
		return;
		}

	CTzRules* rule = iTimeZoneAccessor->GetCurrentSystemTzRulesL();
	CleanupStack::PushL(rule);
	iPosCurSystemTzRule = FindRule(iSystemTzRules, *rule);
	CleanupStack::PopAndDestroy(rule);
	}

void CAgnTzRuleIndex::HandleTzRulesChangeL(const TTime& aTime)
	{
	if(!iAgnFile->IsLocked())
	    {
        if (iTzDbChangeTimeStamp < aTime)
            {
            iTzDbChangeTimeStamp = aTime;
            if (RefreshTzRulesL())
                {
                iTzRulesLastModifiedTime = aTime;
                }
            WriteTimeStampL();
            }
	    }
	else // set the flag so that it can be done later when agendar server is unlocked.
	    {
	    iAgnFile->SetRefreshTzRules(ETrue);
	    }
	}

/* Refresh cached system tz rules and broadcast the change if any of rules has been updated
 */
TBool CAgnTzRuleIndex::RefreshTzRulesL()
	{
	TBool rulesChanged = EFalse;
	const TInt KCount = iSystemTzRules.Count();
	for (TInt ii = 0; ii<KCount; ++ii)
		{
		CTzId* id = CTzId::NewL(static_cast<CAgnSystemTzRuleIndexItem*>(iSystemTzRules[ii])->TzId());
		CTzRules* rules= NULL;
		TRAPD(err, rules = iTimeZoneAccessor->TzServer().GetTimeZoneRulesL(*id, KMinYear, KMaxYear , ETzUtcTimeReference));
		delete id;
		if(err == KErrNone)
		    {
            CleanupStack::PushL(rules);
            if (rules && !iSystemTzRules[ii]->TzRule()->IsEqualTo(*rules))
                {//If a tz rule cached is different from the one in tz server for the same TZ ID, it needs to be updated with the new one.
                CAgnTzRuleIndexItem* item = iSystemTzRules[ii];
                item->TzRule()->CopyL(*rules);
                RStoreWriteStream writeStream;
                writeStream.ReplaceLC(*iStore, item->StreamId());
                item->TzRule()->ExternalizeL(writeStream);
                writeStream.CommitL();
                CleanupStack::PopAndDestroy(&writeStream);
                iStore->CommitL();
                rulesChanged = ETrue;
                }
            CleanupStack::PopAndDestroy(rules);
            }
		}
	if (rulesChanged)
		{
		iAgnFile->TzRulesHaveChangedL();
		}
	return rulesChanged;
	}

void CAgnTzRuleIndex::RollBackL()
	{
	ReadItemsFromStoreL(KUidAgnSystemTzIndex, ETrue);
	ReadItemsFromStoreL(KUidAgnImportedTzIndex, ETrue);
	}

void CAgnTzRuleIndex::ReloadStore(CStreamDictionary& aDictionary, CFileStore& aStore)
	{
	iDictionary = &aDictionary;
	iStore = &aStore;
	}

CTzRules* CAgnTzRuleIndex::FindTzRuleByStreamIdL(TStreamId aStreamId, TBool aIsSystemRule)
	{
	CTzRules* tzRules= NULL;
	if (aIsSystemRule)
		{
		TInt pos = FindTzRuleByStreamIdL(aStreamId, iSystemTzRules, EFalse);
		if (pos != KErrNotFound)
			{
			tzRules = iSystemTzRules[pos]->TzRule()->CloneL();
			}
		}
	else
		{
		TInt pos = FindTzRuleByStreamIdL(aStreamId, iUserDefinedTzRules, EFalse);
		if (pos != KErrNotFound)
			{
			tzRules = iUserDefinedTzRules[pos]->TzRule()->CloneL();
			}
		}

	if (tzRules == NULL)
		{
		tzRules = iTimeZoneAccessor->GetCurrentSystemTzRulesL();
		}

	return tzRules;
	}

CAgnTzRules* CAgnTzRuleIndex::GetTzRulesFromEntry(CAgnSimpleEntry& aEntry)
	{
	CAgnTzRules* tzZone= NULL;
	if (aEntry.TimeMode() != MAgnCalendarTimeMode::EFloating)
		{
		CAgnRptDef* rptDef = aEntry.RptDef();
		if (rptDef != NULL)
			{
			tzZone = rptDef->AgnTzRules();
			}
		}

	return tzZone;
	}

void CAgnTzRuleIndex::RollbackItemL(RPointerArray<CAgnTzRuleIndexItem>& aRuleArray,	CAgnTzRuleIndexItem* aItem)
	{
	CleanupStack::PushL(aItem);
	TInt pos = FindTzRuleByStreamIdL(aItem->StreamId(), aRuleArray, EFalse);
	if (pos == KErrNotFound)
		{
		aRuleArray.AppendL(aItem);
		CleanupStack::Pop(aItem);
		}
	else
		{
		CAgnTzRuleIndexItem* item = aRuleArray[pos];
		item->SetReferenceCount(aItem->ReferenceCount());
		CleanupStack::PopAndDestroy(aItem);
		}
	}

/** Get the last modified time for tz rules
 * It is called when the client getting the last modified date for an entry since the
 * last modified time of an entry is not updated when tz database is updated.
*/
TTime CAgnTzRuleIndex::TzRulesLastModifiedDateL()
	{
	return iTzRulesLastModifiedTime;
	}
