// Copyright (c) 2008-2010 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 "tzlocalizationdb.h"
#include "timezoneserver.h"
#include "mtimezonedataaccess.h"

#include <centralrepository.h>				// For CRepository.
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <tzusernames.h>//new file added for CTzUserNames which is publishedPartner
#endif
#include <tzlocalizedcityrecord.h>
#include <tzlocalizedtimezonerecord.h>

// Database location and access policy
_LIT(KTzLocalizationDbName,"c:TzLocalization.db");
_LIT(KTzLocalizationDbSecurityPolicy,"secure[10206A8B]");

//Table and Column names for Cached Time Zone Table
//These text strings are never visible and do not need localizing
_LIT(KCZTableName, 			"FrequentlyUsedZones");
_LIT(KCZTableTzIdCol, 		"TzId");
_LIT(KCZTableStdCol, 		"StandardName");
_LIT(KCZTableDSTCol, 		"DSTName");
_LIT(KCZTableShortStdCol,	"ShortStandardName");
_LIT(KCZTableShortDSTCol, 	"ShortDSTName");
_LIT(KCZTableCityCol, 		"City");
_LIT(KCZTableCityGroupCol, 	"GroupId");
_LIT(KCZTableResourceIdCol,	"ResourceId");
_LIT(KCZTableCityIndexCol,	"CityIndex");

//Table and Column names for User Added Cities Table
//These text strings are never visible and do not need localizing
_LIT(KUCTableName, 			"UserCities");
_LIT(KUCTableTzId, 			"TzId");
_LIT(KUCTableCity, 			"City");
_LIT(KUCTableGroup, 		"GroupId");
_LIT(KUCTableResourceId,	"ResourceId");

enum TTzZoneColumn
	{
	// Enum will used as a DB column index, so it must start at 1
	ETzZoneId = 1,
	ETzZoneStdName,
	ETzZoneDSTName,
	ETzZoneShortStdName,
	ETzZoneShortDSTName,
	ETzZoneCity,
	ETzZoneGroupId,
	ETzZoneResourceId,
	ETzZoneCityIndex
	};

_LIT(KTzMutexName, 	"TZ_GlobalMutex");

void CTzLocalizationDb::CleanupTimeZonePointerArray(TAny* aArray)
	{
	RPointerArray<CTzLocalizedTimeZoneRecord>* array = static_cast<RPointerArray<CTzLocalizedTimeZoneRecord>* >(aArray);
	if (array)
		{
		array->ResetAndDestroy();
		array->Close();
		}
	}

void CTzLocalizationDb::CleanupCityPointerArray(TAny* aArray)
	{
	RPointerArray<CTzLocalizedCityRecord>* array = static_cast<RPointerArray<CTzLocalizedCityRecord>* >(aArray);
	if (array)
		{
		array->ResetAndDestroy();
		array->Close();
		}
	}

CTzLocalizationDb* CTzLocalizationDb::NewL(CTzServer& aServer)
	{
	CTzLocalizationDb* self = new(ELeave) CTzLocalizationDb(aServer);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CTzLocalizationDb::~CTzLocalizationDb()
	{
	iServer.UserTimeZoneDb().RemoveObserver(this);
	CloseDb();
	iDbsSession.Close();
	}

CTzLocalizationDb::CTzLocalizationDb(CTzServer& aServer)
	: iServer(aServer)
	{
	}

void CTzLocalizationDb::ConstructL()
	{
	User::LeaveIfError(iDbsSession.Connect());
	OpenDbL();
	iServer.UserTimeZoneDb().AddObserverL(this);
	}

void CTzLocalizationDb::OpenDbL()
	{
	if (iZoneMutex.OpenGlobal(KTzMutexName) != KErrNone)
		{
		User::LeaveIfError(iZoneMutex.CreateGlobal(KTzMutexName)) ;
		}
	//Attempt to open the database
	TInt error = iLocalizedTimeZoneDb.Open(iDbsSession,KTzLocalizationDbName,KTzLocalizationDbSecurityPolicy);
	if (error == KErrNotFound)
		{
		// Database file doesn't exist.  Attempt to create a new one.
		CreateBlankDatabaseL();
		}
	else if ((error == KErrNone) && IsInvalidL())
		{
		RecreateBlankDatabaseL();
		}
	else if (error == KErrNone && iServer.TimeZoneManager().TzDbHasChanged())
		{	
		// We have to refresh the data in the frequently used zones table
		// and the city table because it may contain the time zone id that
		// is not existing in the Tz database. Or the the details of a tz rule
		// has been changed.
		RestoreDbL();
		}
	else if (error != KErrNone)
		{
		User::Leave(error);
		}
	}

void CTzLocalizationDb::CloseDb()
    {
	iLocalizedTimeZoneDb.Close();
	if (iZoneMutex.Handle() && iZoneMutex.IsHeld())    // it is a leave, so release mutex
		{
		iZoneMutex.Signal();
		}       
	iZoneMutex.Close();
	}

void CTzLocalizationDb::CreateBlankDatabaseL()
	{
	User::LeaveIfError(iLocalizedTimeZoneDb.Create(iDbsSession,KTzLocalizationDbName,KTzLocalizationDbSecurityPolicy));
	CreateBlankFrequentlyUsedZoneTableL();
	CreateBlankUserCityTableL();
	}

/**
Creates the Cached Zone Table.
@return KErrNone if succesful, or one of the DBMS Leave codes
@internalTechnology
*/
void CTzLocalizationDb::CreateBlankFrequentlyUsedZoneTableL()
	{
	// Create the columns for the cached zones table
	RArray<TDbCol> cachedTableCols;
	CleanupClosePushL(cachedTableCols);
	cachedTableCols.AppendL(TDbCol(KCZTableTzIdCol,			EDbColUint16));
	cachedTableCols.AppendL(TDbCol(KCZTableStdCol, 			EDbColText));
	cachedTableCols.AppendL(TDbCol(KCZTableDSTCol, 			EDbColText));
	cachedTableCols.AppendL(TDbCol(KCZTableShortStdCol, 	EDbColText));
	cachedTableCols.AppendL(TDbCol(KCZTableShortDSTCol, 	EDbColText));
	cachedTableCols.AppendL(TDbCol(KCZTableCityCol, 		EDbColText));
	cachedTableCols.AppendL(TDbCol(KCZTableCityGroupCol, 	EDbColUint8));
	cachedTableCols.AppendL(TDbCol(KCZTableResourceIdCol, 	EDbColUint32));
    cachedTableCols.AppendL(TDbCol(KCZTableCityIndexCol,	EDbColInt32));

	// Create the columnset - add the columns
	// Columns MUST be added in the same order they appear in TTzZoneColumn
	CDbColSet* frequentlyUsedZoneColSet = CDbColSet::NewLC();
	TInt numCols = cachedTableCols.Count();
	for(TInt i = 0; i < numCols; ++i)
		{
		frequentlyUsedZoneColSet->AddL(cachedTableCols[i]);
		}

	// Create the Cached Time Zone table
	User::LeaveIfError(iLocalizedTimeZoneDb.CreateTable(KCZTableName,*frequentlyUsedZoneColSet));

	//Open the newly created table
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);
	zoneView.Reset();
	//Populate with initial (blank) data.

	_LIT(KEmptyString," ");

	for (TInt x = 0; x < CTzLocalizedTimeZone::ECachedTimeZones; ++x)
		{
		iZoneMutex.Wait();
		CleanupStack::PushL(TCleanupItem(ReleaseMutex,&iZoneMutex));
		// Insert empty row
		zoneView.InsertL();
		// Fill the table with blank data
		zoneView.SetColL(ETzZoneId,			0);
		zoneView.SetColL(ETzZoneStdName,		KEmptyString);
		zoneView.SetColL(ETzZoneDSTName,		KEmptyString);
		zoneView.SetColL(ETzZoneShortStdName,	KEmptyString);
		zoneView.SetColL(ETzZoneShortDSTName,	KEmptyString);
		zoneView.SetColL(ETzZoneCity,			KEmptyString);
		zoneView.SetColL(ETzZoneGroupId,		0);
		zoneView.SetColL(ETzZoneResourceId,	0);
		zoneView.SetColL(ETzZoneCityIndex,		0);
		zoneView.PutL(); // Complete insertion
		CleanupStack::Pop() ;
		iZoneMutex.Signal() ;
		}
	
	CleanupStack::PopAndDestroy(&zoneView);
	CleanupStack::PopAndDestroy(frequentlyUsedZoneColSet);
	CleanupStack::PopAndDestroy(&cachedTableCols); //cachedTableCols
	}

/**
Creates the user added city database table.
@return KErrNone if succesful, or one of the DBMS Leave codes
@internalTechnology
*/
void CTzLocalizationDb::CreateBlankUserCityTableL()
	{
	//Create the columns for the user aded cities table
	RArray<TDbCol> cityTableCols;
	CleanupClosePushL(cityTableCols);
	cityTableCols.AppendL(TDbCol(KUCTableTzId,			EDbColUint16));
	cityTableCols.AppendL(TDbCol(KUCTableCity,			EDbColText));
	cityTableCols.AppendL(TDbCol(KUCTableGroup,			EDbColUint8));
	cityTableCols.AppendL(TDbCol(KUCTableResourceId,	EDbColUint32));

	// Create the columnset - add the columns
	// Columns MUST be added in the same order they appear in TTzCityColumn
	CDbColSet* userCityColSet = CDbColSet::NewLC();
	TInt numCols = cityTableCols.Count();
	for(TInt i = 0; i < numCols; ++i)
		{
		userCityColSet->AddL(cityTableCols[i]);
		}

	// Create the User City table
	User::LeaveIfError(iLocalizedTimeZoneDb.CreateTable(KUCTableName,*userCityColSet));
	
	CleanupStack::PopAndDestroy(userCityColSet);
	CleanupStack::PopAndDestroy(&cityTableCols);
	}

//Check if the database is corrupt or invalid.
TBool CTzLocalizationDb::IsInvalidL()
	{
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);
	TBool isInvalid = EFalse;
	if (!zoneView.FirstL())
		{
		isInvalid = ETrue;
		}
	if (!isInvalid)
		{
		for (TInt i = 1; i < CTzLocalizedTimeZone::ECachedTimeZones; i++)
			{
		    if (!zoneView.NextL())
			    {
			    isInvalid = ETrue;
			    break;
		        }
		    }
		}
	CleanupStack::PopAndDestroy(&zoneView);
	return isInvalid;
	}

void CTzLocalizationDb::RecreateBlankDatabaseL()
	{
	iLocalizedTimeZoneDb.Close();
	const TUid KTzLocalizationDbSecurityPolicyUid = {0x10206A8B};
    User::LeaveIfError(iDbsSession.DeleteDatabase(KTzLocalizationDbName(),KTzLocalizationDbSecurityPolicyUid));
    CreateBlankDatabaseL();
	}

TBool CTzLocalizationDb::NeedsUpdatingL()
	{
	// Check the version of the database
    // There are 9 columns as of Oct 2006
	TBool result = ETrue;
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);
    if (zoneView.ColCount() == ETzZoneCityIndex)
        {
        result = EFalse;
        }
    CleanupStack::PopAndDestroy(&zoneView);
    return result;
	}

void CTzLocalizationDb::NotifyUserTzRulesChange(TTzUserDataChange /*aChange*/)
	{
	// Do nothing as we don't use any rules information
	}

void CTzLocalizationDb::NotifyUserTzNamesChange(TTzUserDataChange aChange)
	{
	if (iLocked)
		{
		return;
		}
	
	if (aChange.iOperation == ETzUserDataCreated)
		{
		// Creation of new zones has no impact on existing zones
		return;
		}
	
	// Iterate through the tables and refresh contents if needed
	TRAPD(err, NotifyUserTzNamesChangeL(aChange));
	if (err != KErrNone)
		{
		// If there has been an error we can't guarantee the contents of the
		// database. Safest option is to lock it. The only way to unlock it
		// is to reboot the server.
		iLocked = ETrue;
		}
	}

void CTzLocalizationDb::NotifyTZDataStatusChangeL(RTz::TTzChanges aChange)
	{
	if (iLocked)
		{
		return;
		}
	
	if(aChange == RTz::ETZLocalizationDataChanged)
		{
		CTzSystemDataDb* systemDataDb = CTzSystemDataDb::NewLC();
		
		RPointerArray<CTzLocalizedTimeZoneRecord> frequentlyUsedZones;
		CleanupStack::PushL(TCleanupItem(CleanupTimeZonePointerArray,&frequentlyUsedZones));
		
		RPointerArray<CTzLocalizedCityRecord> cachedCities;
		CleanupStack::PushL(TCleanupItem(CleanupTimeZonePointerArray,&cachedCities));
			
		for (TInt i = CTzLocalizedTimeZone::ECurrentZone; i < CTzLocalizedTimeZone::ECachedTimeZones; ++i)
			{
			CTzLocalizedTimeZone::TTzFrequentlyUsedZone freqUsedZone = (CTzLocalizedTimeZone::TTzFrequentlyUsedZone)i;
			CTzLocalizedTimeZoneRecord* timeZoneRecord = ReadFrequentlyUsedZoneL(freqUsedZone);
			CleanupStack::PushL(timeZoneRecord);
			CTzLocalizedCityRecord* cityRecord = ReadCachedTimeZoneCityL(freqUsedZone);
			CleanupStack::PushL(cityRecord);
		
			// If a change occurred, check if the timezone exists in the resource file and refresh it
			// If not, delete it
			if(!ExistsInSystemDbL(timeZoneRecord->Id(), *systemDataDb) && !CTzId::IsUserTzId(timeZoneRecord->Id()))
				{	
				CleanupStack::PopAndDestroy(cityRecord);
				cityRecord = NULL;
				CleanupStack::PopAndDestroy(timeZoneRecord);
				timeZoneRecord = NULL;
				if(freqUsedZone != CTzLocalizedTimeZone::ECurrentZone)
					{
					// Put the default cached zone and its default city in the array instead
					timeZoneRecord = systemDataDb->ReadFrequentlyUsedZoneL(freqUsedZone);
					}
				else
					{
					// get the current zone from the time zone server
					TInt id = GetTimeZoneIdFromTzServerL(*systemDataDb);
					timeZoneRecord = systemDataDb->ReadTimeZoneL(id);
					}
				CleanupStack::PushL(timeZoneRecord);
				cityRecord = systemDataDb->ReadDefaultCityL(timeZoneRecord->Id());
				CleanupStack::PushL(cityRecord);
				
				cachedCities.AppendL(cityRecord);
				// ownership is transferred into the array
				CleanupStack::Pop(cityRecord);
				frequentlyUsedZones.AppendL(timeZoneRecord);
				// ownership is transferred into the array
				CleanupStack::Pop(timeZoneRecord);
				}	
			else
				{
				TUint tzid = timeZoneRecord->Id();
				CleanupStack::PopAndDestroy(cityRecord);
				cityRecord = NULL;
				CleanupStack::PopAndDestroy(timeZoneRecord);
				timeZoneRecord = NULL;
				UpdateTimeZoneAndCityRecordForSystemDataL(frequentlyUsedZones, cachedCities, tzid, *systemDataDb);	
				}
			}
		WriteAllFrequentlyUsedZonesL(frequentlyUsedZones, cachedCities);
	
		// Get the cities with the deleted time zone id
		RDbView cityView;
		CleanupClosePushL(cityView);
		_LIT(KSQLQueryLit,"SELECT * FROM UserCities");
		// Iterate through all the cities and delete them when
		// the corresponding tz id doesn't exist anymore
		PrepareCityViewL(cityView, KSQLQueryLit);
		// Loop through the rowset and delete the cities
		for (cityView.FirstL(); cityView.AtRow(); cityView.NextL())
			{
			cityView.GetL();
			if (!ExistsInSystemDbL(cityView.ColUint16(ETzCityId), *systemDataDb))
				{
				cityView.DeleteL();
				}
			}
		CleanupStack::PopAndDestroy(&cityView);
		CleanupStack::PopAndDestroy(&cachedCities); 
		CleanupStack::PopAndDestroy(&frequentlyUsedZones); 
		CleanupStack::PopAndDestroy(systemDataDb);
		}
	}

void CTzLocalizationDb::UpdateTimeZoneAndCityRecordForSystemDataL(RPointerArray<CTzLocalizedTimeZoneRecord>& aFrequentlyUsedZones, 
		RPointerArray<CTzLocalizedCityRecord>& aCachedCities, TInt aTzId, CTzSystemDataDb& aSystemDataDb)
	{
	CTzLocalizedTimeZoneRecord* timeZone = aSystemDataDb.ReadTimeZoneL(aTzId);
	CleanupStack::PushL(timeZone);

	CTzLocalizedCityRecord* city = aSystemDataDb.ReadDefaultCityL(aTzId);
	CleanupStack::PushL(city);
	
	aCachedCities.AppendL(city);
	// ownership is transferred into the array
	CleanupStack::Pop(city);
	aFrequentlyUsedZones.AppendL(timeZone);
	// ownership is transferred into the array
	CleanupStack::Pop(timeZone);
	}

TBool CTzLocalizationDb::ExistsInSystemDbL(TUint aTzId, CTzSystemDataDb& aSystemDataDb)
	{
	CTzLocalizedTimeZoneRecord* timeZoneRecord = NULL;
	TRAPD(err, timeZoneRecord = aSystemDataDb.ReadTimeZoneL(aTzId));
	delete timeZoneRecord;
	if(err == KErrNone)
		{
		return ETrue;
		}
	return EFalse;	
	}

/**
Reads all the user defined cities stored in the
database. If the database is empty the
returned array will contain no elements.
@param aCities Array of cities to add the user defined to.
@internalTechnology
*/
void CTzLocalizationDb::ReadCitiesL(RPointerArray<CTzLocalizedCityRecord>& aCities)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	_LIT(KSQLQueryLit,"SELECT * FROM UserCities");
	DoReadCitiesL(aCities,KSQLQueryLit());
	}

void CTzLocalizationDb::ReadCitiesL(RPointerArray<CTzLocalizedCityRecord>& aCities, TInt aTimeZoneId)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	// Assign basic SQL query literal
	_LIT(KSQLQueryLit,"SELECT * FROM UserCities WHERE TzId = ");
	// Assign a buffer with enough room to hold the query including the time zone id
	HBufC* sqlStr = HBufC::NewLC(KSQLQueryLit().Length() + EMaxTimeZoneIdStringLength);
	// Set the buffer to the start of the query
	*sqlStr = KSQLQueryLit;
	// Append the time zone ID
	sqlStr->Des().AppendNum(aTimeZoneId);

	DoReadCitiesL(aCities,*sqlStr);
	CleanupStack::PopAndDestroy(sqlStr);
	}

void CTzLocalizationDb::ReadCitiesInGroupL(RPointerArray<CTzLocalizedCityRecord>& aCities, TUint8 aGroupId)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	// Assign basic SQL query literal
	_LIT(KSQLQueryLit,"SELECT * FROM UserCities WHERE GroupId = ");
	HBufC* sqlStr = HBufC::NewLC(KSQLQueryLit().Length() + EMaxGroupIdStringLength);
	// Set the buffer to the start of the query
	*sqlStr = KSQLQueryLit;
	// Append the time zone ID
	sqlStr->Des().AppendNum(aGroupId);

	DoReadCitiesL(aCities,*sqlStr);
	CleanupStack::PopAndDestroy(sqlStr);
	}

/**
Retrieves a cached zone from the database.
The calling function takes ownership of the returned zone
@param aFrequentlyUsedZone - The cached zone to return
@return a cached zone from the database
@internalTechnology
*/
CTzLocalizedTimeZoneRecord* CTzLocalizationDb::ReadFrequentlyUsedZoneL(TInt aFrequentlyUsedZone)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	return DoReadFrequentlyUsedZoneL(aFrequentlyUsedZone);
	}

CTzLocalizedTimeZoneRecord* CTzLocalizationDb::DoReadFrequentlyUsedZoneL(TInt aFrequentlyUsedZone)
	{
	// You cannot pass ECachedTimeZones in as the argument, because it is only
	// used to keep count of how many cached zones there are.
	__ASSERT_ALWAYS(aFrequentlyUsedZone != CTzLocalizedTimeZone::ECachedTimeZones, User::Leave(KErrArgument));
	
	//Prepare a view of the zone table
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);
	
	//Ensure the cursor is at the first row in the table.
	zoneView.FirstL();
	
	//Skip through the rows until we reach the row for the required zone
	for (TInt x = 0; x < aFrequentlyUsedZone; ++x)
		{
		zoneView.NextL();
		}
	
	//Get the row
	zoneView.GetL();
	
	//Create the new localized time zone from the row data
	CTzLocalizedTimeZoneRecord* localizedTimeZone = CTzLocalizedTimeZoneRecord::NewL(zoneView.ColUint16(ETzZoneId),
		zoneView.ColDes(ETzZoneStdName), zoneView.ColDes(ETzZoneDSTName), zoneView.ColDes(ETzZoneShortStdName),
		zoneView.ColDes(ETzZoneShortDSTName), zoneView.ColUint32(ETzZoneResourceId));
	
	CleanupStack::PopAndDestroy(&zoneView);
	return localizedTimeZone;
	}

/**
Retrieves the city used to select this time zone if set.  If the time zone was not originally
set using a city then the default city for the time zone will be returned instead.
@param aFrequentlyUsedZone - The cached zone to find the city for
@return	 selected city for the cached zone
@internalTechnology
*/
CTzLocalizedCityRecord* CTzLocalizationDb::ReadCachedTimeZoneCityL(TInt aFrequentlyUsedZone)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	
	// You cannot pass ECachedTimeZones in as the argument, because it is only
	// used to keep count of how many cached zones there are.
	__ASSERT_ALWAYS(aFrequentlyUsedZone != CTzLocalizedTimeZone::ECachedTimeZones, User::Leave(KErrArgument));

	//Prepare a view of the zone table
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);

	//Ensure the cursor is at the first row in the table.
	zoneView.FirstL();

	//Skip through the rows until we reach the row for the required zone
	for (TInt x = 0; x < aFrequentlyUsedZone; ++x)
		{
		zoneView.NextL();
		}

	//Get the row
	zoneView.GetL();

	//Create the new localized time zone from the row data
	TInt index = 0;
	// An extra column was added but we still have to cope with the old format as we
	// need to read the database contents when upgrading to the new format
	if (zoneView.ColCount() == ETzZoneCityIndex)
        {
        index = zoneView.ColInt32(ETzZoneCityIndex);
        }
	CTzLocalizedCityRecord* city = CTzLocalizedCityRecord::NewL(zoneView.ColDes(ETzZoneCity),
		zoneView.ColUint8(ETzZoneGroupId), index, zoneView.ColUint16(ETzZoneId),
		zoneView.ColUint32(ETzZoneResourceId));
    
	CleanupStack::PopAndDestroy(&zoneView);
	return city;
	}

void CTzLocalizationDb::WriteCityL(const TDesC& aCityName, TUint16 aCityTzId, TUint8 aCityGroupId, TUint aCityTzResourceId)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	
	// Build a SQL query to check if the city name already exists in the database
	// with a matching timezoneID
	RDbView cityView;
	CleanupClosePushL(cityView);
	HBufC* sqlStr = GetCityQueryLC(aCityName, aCityTzId);
	PrepareCityViewL(cityView, *sqlStr);
	CleanupStack::PopAndDestroy(sqlStr);
	sqlStr = NULL;

	TInt cityCount = cityView.CountL();

	if (cityCount == 0)
		{
		//City was not found.  Add the new city to the database
		//Retrieve the CdbColSet from the view
		CDbColSet* cityColSet = cityView.ColSetL();
		CleanupStack::PushL(cityColSet);

		//Set the rowset cursor to the beginning of the rowset,
		//insert an empty row to write the city to
		cityView.Reset();
		cityView.InsertL();

		//Set the city information for the new row
		cityView.SetColL(ETzCityId,aCityTzId);
		cityView.SetColL(ETzCityCity,aCityName);
		cityView.SetColL(ETzCityGroup,aCityGroupId);
		cityView.SetColL(ETzCityResourceId,aCityTzResourceId);

		CleanupStack::PopAndDestroy(cityColSet);
		cityView.PutL(); // Complete insertion
		}
	else
		{
		//City already exists.
		User::Leave(KErrAlreadyExists);
		}
	CleanupStack::PopAndDestroy(&cityView);
	iZoneMutex.Wait();
	iLocalizedTimeZoneDb.Compact();
	iZoneMutex.Signal();
	}

void CTzLocalizationDb::DeleteCityL(const TDesC& aCityName, TUint16 aCityTzId)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	
	//Build a SQL query to check if the city name and time zone ID exist in the database
	RDbView cityView;
	CleanupClosePushL(cityView);
	HBufC* sqlStr = GetCityQueryLC(aCityName, aCityTzId);
	PrepareCityViewL(cityView, *sqlStr);
	CleanupStack::PopAndDestroy(sqlStr);
	sqlStr = NULL;
	
	TInt cityCount = cityView.CountL();

	if (cityCount > 0)
		{
		//A city with this name exists in the database.  Delete it.
		if (cityView.FirstL())
			{
			cityView.DeleteL();
			//Reset the cursor, as it now points to an invalid row
			cityView.Reset();
			}
		}
	else
		{
		//City was not found
		User::Leave(KErrNotFound);
		}
	CleanupStack::PopAndDestroy(&cityView);
	iZoneMutex.Wait();
	iLocalizedTimeZoneDb.Compact();
	iZoneMutex.Signal();
	}

/**
Stores the localized time zone in the database for easy retrieval.
The database keeps track of the two most recently used zones, these are updated every time
a new zone is set.  The zone to be replaced becomes recently used zone 1, and recently used zone 1 becomes
recently used zone 2.  The old recently used zone 2 is discarded.
@param Any city in the time zone to store
@param aFrequentlyUsedZone - specifies which time zone to overwrite in the database
@internalTechnology
*/
void CTzLocalizationDb::WriteFrequentlyUsedZoneL(const CTzLocalizedTimeZoneRecord& aTimeZone, 
		const CTzLocalizedCityRecord& aCity, TInt aFrequentlyUsedZone)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	
	// Only the Current, Home and Zone of Interest can be changed explicitly
	// using this function.  Recent Zones are automatically updated.
	__ASSERT_ALWAYS(aFrequentlyUsedZone < CTzLocalizedTimeZone::ERecentZone1, User::Leave(KErrArgument));
	
	iZoneMutex.Wait();
	
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);
	//Ensure the cursor is at the first row in the table.
	zoneView.FirstL();
	TInt x;
	//Skip through the rows until we reach the row for the zone to update
	for (x = 0; x < aFrequentlyUsedZone; ++x)
		{
		zoneView.NextL();
		}

	//Get the row
	zoneView.GetL();
	
	//Bookmark the row
	TDbBookmark rowToUpdate(zoneView.Bookmark());

	//Insert a copy of the row - this is now the last row in the table, and will become
	//recently used zone 1 after the original recently used zones are deleted

	CleanupStack::PushL(TCleanupItem(ReleaseMutex,&iZoneMutex));
	zoneView.InsertCopyL();
	zoneView.PutL();
	CleanupStack::Pop();
	
	//Position the cursor back at the row to update
	zoneView.GotoL(rowToUpdate);

	//Update the row with the new data
	DoWriteFrequentlyUsedZoneL(zoneView, aTimeZone, aCity);

	//Reset the cursor back to the first row in the table.
	zoneView.FirstL();

	//Skip through the rows until we reach the row for recent zone 1
	for (x = 0; x < CTzLocalizedTimeZone::ERecentZone1; ++x)
		{
		zoneView.NextL();
		}

	//Get the row
	zoneView.GetL();

	//Bookmark the row
	TDbBookmark recentZone1(zoneView.Bookmark());

	//Insert a copy of the row - this is now the last row in the table, and will become
	//recently used zone 2 after the original recently used zones are deleted
	CleanupStack::PushL(TCleanupItem(ReleaseMutex,&iZoneMutex));
	zoneView.InsertCopyL();
	zoneView.PutL();
	CleanupStack::Pop();

	//Reset the cursor to the orignal recent zone 1 position
	zoneView.GotoL(recentZone1);

	//Delete the original recent zone 1 and recent zone 2.
	//The new recent zone 1 and recent zone 2 are moved back into the correct
	//position by the removal of the preceding rows
	CleanupStack::PushL(TCleanupItem(ReleaseMutex,&iZoneMutex));
	zoneView.DeleteL();
	CleanupStack::Pop();

	zoneView.NextL();

	CleanupStack::PushL(TCleanupItem(ReleaseMutex,&iZoneMutex));
	zoneView.DeleteL();
	CleanupStack::Pop();
	
	//Cursor is now invalid (pointing to an empty row)
	//and shuold be reset to a known state before reusing
	//Cleanup
	CleanupStack::PopAndDestroy(&zoneView);
	iLocalizedTimeZoneDb.Compact();
	iZoneMutex.Signal();
	}

/**
Sets all the cached zones and their associated cities to be those that are
supplied.
@param aTimeZones Array of time zones to be written.
@param aCities Array of cities to be written.
@internalTechnology
*/
void CTzLocalizationDb::WriteAllFrequentlyUsedZonesL(const RPointerArray<CTzLocalizedTimeZoneRecord>& aTimeZones, 
		const RPointerArray<CTzLocalizedCityRecord>& aCities)
	{
	if (iLocked)
		{
		User::Leave(KErrLocked);
		}
	DoWriteAllFrequentlyUsedZonesL(aTimeZones, aCities);
	}

void CTzLocalizationDb::DoWriteAllFrequentlyUsedZonesL(const RPointerArray<CTzLocalizedTimeZoneRecord>& aTimeZones, 
		const RPointerArray<CTzLocalizedCityRecord>& aCities)
	{
	// Ensure that the numbers of items in the arrays are the expected amount
	__ASSERT_ALWAYS(aTimeZones.Count() == CTzLocalizedTimeZone::ECachedTimeZones &&
					aCities.Count() == CTzLocalizedTimeZone::ECachedTimeZones, User::Leave(KErrArgument));
	
	iZoneMutex.Wait();	
		
	RDbView zoneView;
	CleanupClosePushL(zoneView);
	PrepareZoneViewL(zoneView);
	//Ensure the cursor is at the first row in the table.
	zoneView.FirstL();

	//Skip through the rows until we reach the row for the zone to update
	for (TInt i = 0; i < CTzLocalizedTimeZone::ECachedTimeZones; ++i)
		{
		DoWriteFrequentlyUsedZoneL(zoneView, *aTimeZones[i], *aCities[i]);
		zoneView.NextL();
		}

	CleanupStack::PopAndDestroy(&zoneView);
	
	iLocalizedTimeZoneDb.Compact();
	iZoneMutex.Signal();
	}

/**
Opens the a view on the city table and returns a reference to it.
The view should be released after use with a call to CloseCityView()
@param aSql A sql string to prepare the view with
@return reference to opened iCityTable
@internalTechnology
*/
void CTzLocalizationDb::PrepareCityViewL(RDbView& aCityView, const TDesC& aSqlQuery)
	{
	User::LeaveIfError(aCityView.Prepare(iLocalizedTimeZoneDb, TDbQuery(aSqlQuery),	aCityView.EUpdatable));
	User::LeaveIfError(aCityView.EvaluateAll());
	}

/**
Opens a view to the zone table
@internalTechnology
*/
void CTzLocalizationDb::PrepareZoneViewL(RDbView& aZoneView)
	{
	_LIT(KReadZoneView,"SELECT * FROM FrequentlyUsedZones");
	User::LeaveIfError(aZoneView.Prepare(iLocalizedTimeZoneDb, TDbQuery(KReadZoneView),	aZoneView.EUpdatable));
	User::LeaveIfError(aZoneView.EvaluateAll());
	}

/**
Returns the SQL string that will pick the specified city out of the user-added
cities database. Leaves the returned HBufC pointer on the Cleanup Stack.
The query is of the form:
	"SELECT * FROM UserCities WHERE City = 'London' AND TZID = 2592"
@param aCity The city to make the query for.
@return The SQL Query that will select the aCity form the DB
@internalTechnology
*/
HBufC* CTzLocalizationDb::GetCityQueryLC(const TDesC& aCityName, TInt aCityTimeZoneId)
	{
	// Assign the start and middle of the basic SQL query literal 
	_LIT(KSQLQueryLitStart,"SELECT * FROM UserCities WHERE City = '");
	_LIT(KSQLQueryLitMiddle,"' AND TZID = ");
	
	/* Assign a buffer with enough room to hold the query including the city 
	   name and the time zone id */
	TInt maxBufferLength = KSQLQueryLitStart().Length() + KSQLQueryLitMiddle().Length() 
							+ aCityName.Length() + EMaxTimeZoneIdStringLength;
	HBufC* sqlStr = HBufC::NewLC(maxBufferLength);
	
	// Append the beginning of the query
	sqlStr->Des().Append(KSQLQueryLitStart);
	// Append the city name into the query
	sqlStr->Des().Append(aCityName);
	// Append the middle of the query
	sqlStr->Des().Append(KSQLQueryLitMiddle);
	// Append the time zone ID into the query
	sqlStr->Des().AppendNum(aCityTimeZoneId);
	
	return sqlStr;
	}

HBufC* CTzLocalizationDb::GetCityQueryLC(TInt aCityTimeZoneId)
	{
	// Assign the start and middle of the basic SQL query literal 
	_LIT(KSQLQueryLitStart,"SELECT * FROM UserCities WHERE TZID = ");
	
	/* Assign a buffer with enough room to hold the query including the city 
	   name and the time zone id */
	TInt maxBufferLength = KSQLQueryLitStart().Length() + EMaxTimeZoneIdStringLength;
	HBufC* sqlStr = HBufC::NewLC(maxBufferLength);
	
	// Append the beginning of the query
	sqlStr->Des().Append(KSQLQueryLitStart);
	// Append the time zone ID into the query
	sqlStr->Des().AppendNum(aCityTimeZoneId);
	
	return sqlStr;
	}

/**
Reads the user defined cities in the database
matched by the SQL query into the supplied array. If the
database is empty the returned array will contain no elements.
@param aCities array of cities to add the user defined to.
@param aSqlString - SQL search string determining the cities to retrieve
@internalTechnology
*/
void CTzLocalizationDb::DoReadCitiesL(RPointerArray<CTzLocalizedCityRecord>& aCities, const TDesC& aSqlString)
	{
	RDbView cityView;
	CleanupClosePushL(cityView);
	PrepareCityViewL(cityView, aSqlString);
	
	//Loop through the rowset
	for (cityView.FirstL(); cityView.AtRow(); cityView.NextL())
		{
		// Fetch a cached copy of current row
		cityView.GetL();

		//Create the new city from the row data
		CTzLocalizedCityRecord* newCity = CTzLocalizedCityRecord::NewLC(cityView.ColDes(ETzCityCity), 
			cityView.ColUint8(ETzCityGroup), aCities.Count(), cityView.ColUint16(ETzCityId),
			cityView.ColUint32(ETzCityResourceId));
     
		//Append the new city to the city array - aCities takes ownership of newCity
		aCities.AppendL(newCity);
		CleanupStack::Pop(newCity);
		}
	CleanupStack::PopAndDestroy(&cityView);
	}

/**
Does the actual writing of a zone and city into the database. iZoneView must
have already been prepared and moved to the correct offset into the row set
before this function is called. e.g. at least the following should have been
done:
	PrepareZoneViewL();
	iZoneView.FirstL();
Otherwise the cursor position may be invalid and a DBMS panic will occur.
@param aTimeZone The time zone to write.
@param aCity The city to write.
@param aFrequentlyUsedZone The enum of the cached zone to write to.
@internalTechnology
*/
void CTzLocalizationDb::DoWriteFrequentlyUsedZoneL(RDbView& aZoneView, const CTzLocalizedTimeZoneRecord& aTimeZone,
		const CTzLocalizedCityRecord& aCity)
	{
	CleanupStack::PushL(TCleanupItem(ReleaseMutex,&iZoneMutex));
	aZoneView.UpdateL();  
	aZoneView.SetColL(ETzZoneId, (TInt)aTimeZone.Id());
	aZoneView.SetColL(ETzZoneStdName, aTimeZone.StandardName());
	aZoneView.SetColL(ETzZoneDSTName, aTimeZone.DaylightName());
	aZoneView.SetColL(ETzZoneShortStdName, aTimeZone.ShortStandardName());
	aZoneView.SetColL(ETzZoneShortDSTName, aTimeZone.ShortDaylightName());
	aZoneView.SetColL(ETzZoneCity, aCity.Name());
	aZoneView.SetColL(ETzZoneGroupId, aCity.GroupId());
	aZoneView.SetColL(ETzZoneResourceId, aTimeZone.ResourceId());
    if (aZoneView.ColCount() == ETzZoneCityIndex)
        {
        aZoneView.SetColL(ETzZoneCityIndex, aCity.Index());
        }
    aZoneView.PutL();
	CleanupStack::Pop();
	}

TInt CTzLocalizationDb::GetTimeZoneIdFromTzServerL(CTzSystemDataDb& aSystemDataDb)
	{
	// Get current time zone using the current CTzId from the time zone server
	TUint timeZoneIdInt = iServer.TimeZoneManager().GetTimeZoneIdL().TimeZoneNumericID();
	
	// Is the time zone ID the unknown/invalid ID?
	// This is temporary measure that is required until PREQ 234' TzServer
	// changes are introduced
	const TUint32 KUnknownTZId = 0x0ffffff0;
	if((TUint32)timeZoneIdInt == KUnknownTZId)
		{
		// Return the ID of the DEFAULT home zone instead
		CTzLocalizedTimeZoneRecord* homeZone = aSystemDataDb.ReadFrequentlyUsedZoneL(CTzLocalizedTimeZone::EHomeZone);
		CleanupStack::PushL(homeZone);
		timeZoneIdInt = homeZone->Id();
		CleanupStack::PopAndDestroy(homeZone);
		}
		
	return timeZoneIdInt;
	}

void CTzLocalizationDb::NotifyUserTzNamesChangeL(TTzUserDataChange aChange)
	{
	// If an existing user-defined time zone is updated it means we may have
	// to refresh the data in the frequently used zones table but the city table
	// only contains the time zone id so there is nothing to update in there.
	
	// If an existing user-defined time zone is deleted it means we may have
	// to refresh the data in the frequently used zones table and the city table
	// because it may contain the time zone id that has been deleted.
	CTzSystemDataDb* systemDataDb = CTzSystemDataDb::NewLC();
	TBool frequentlyUsedZonesNeedUpdating = EFalse;
	RPointerArray<CTzLocalizedTimeZoneRecord> frequentlyUsedZones;
	CleanupStack::PushL(TCleanupItem(CleanupTimeZonePointerArray,&frequentlyUsedZones));
	RPointerArray<CTzLocalizedCityRecord> cachedCities;
	CleanupStack::PushL(TCleanupItem(CleanupCityPointerArray,&cachedCities));
	for (TInt i = CTzLocalizedTimeZone::ECurrentZone; i < CTzLocalizedTimeZone::ECachedTimeZones; ++i)
		{
		CTzLocalizedTimeZone::TTzFrequentlyUsedZone freqUsedZone = (CTzLocalizedTimeZone::TTzFrequentlyUsedZone)i;
		CTzLocalizedTimeZoneRecord* timeZoneRecord = ReadFrequentlyUsedZoneL(freqUsedZone);
		CleanupStack::PushL(timeZoneRecord);
		CTzLocalizedCityRecord* cityRecord = ReadCachedTimeZoneCityL(freqUsedZone);
		CleanupStack::PushL(cityRecord);
		
	 if (timeZoneRecord->Id() == aChange.iTzId || cityRecord->TzId() == aChange.iTzId)
		{
		if (aChange.iOperation == ETzUserDataUpdated)
			{
			CleanupStack::PopAndDestroy(cityRecord);
			cityRecord = NULL;
			CleanupStack::PopAndDestroy(timeZoneRecord);
			timeZoneRecord = NULL;
			UpdateTimeZoneAndCityRecordL(frequentlyUsedZones, cachedCities, aChange.iTzId);
			}
		else if (aChange.iOperation == ETzUserDataDeleted)
			{
			CleanupStack::PopAndDestroy(cityRecord);
			cityRecord = NULL;
			CleanupStack::PopAndDestroy(timeZoneRecord);
			timeZoneRecord = NULL;
			
			if(freqUsedZone != CTzLocalizedTimeZone::ECurrentZone)
				{
				// Put the default cached zone and its default city in the array instead
				timeZoneRecord = systemDataDb->ReadFrequentlyUsedZoneL(freqUsedZone);
				}
			else
				{
				// get the current zone from the time zone server
				TInt id = GetTimeZoneIdFromTzServerL(*systemDataDb);
				timeZoneRecord = systemDataDb->ReadTimeZoneL(id);
				}
			CleanupStack::PushL(timeZoneRecord);
			cityRecord = systemDataDb->ReadDefaultCityL(timeZoneRecord->Id());
			CleanupStack::PushL(cityRecord);
			
			cachedCities.AppendL(cityRecord);
			// ownership is transferred into the array
			CleanupStack::Pop(cityRecord);
			frequentlyUsedZones.AppendL(timeZoneRecord);
			// ownership is transferred into the array
			CleanupStack::Pop(timeZoneRecord);
			}
		frequentlyUsedZonesNeedUpdating = ETrue;
		}
	else
		{
		cachedCities.AppendL(cityRecord);
		// ownership is transferred into the array
		CleanupStack::Pop(cityRecord);
		frequentlyUsedZones.AppendL(timeZoneRecord);
		// ownership is transferred into the array
		CleanupStack::Pop(timeZoneRecord);
		}
	}
			
	if (frequentlyUsedZonesNeedUpdating)
		{
		WriteAllFrequentlyUsedZonesL(frequentlyUsedZones, cachedCities);
		}
		
	if (aChange.iOperation == ETzUserDataDeleted)
		{
		DeleteCitiesL(aChange);
		iZoneMutex.Wait();
		iLocalizedTimeZoneDb.Compact();
		iZoneMutex.Signal();
		}
	CleanupStack::PopAndDestroy(&cachedCities); 
	CleanupStack::PopAndDestroy(&frequentlyUsedZones);
	CleanupStack::PopAndDestroy(systemDataDb);
	}

void CTzLocalizationDb::UpdateTimeZoneAndCityRecordL(RPointerArray<CTzLocalizedTimeZoneRecord>& aFrequentlyUsedZones, RPointerArray<CTzLocalizedCityRecord>& aCachedCities, TInt aTzId)
	{
	CTzUserNames* names = iServer.UserTimeZoneDb().ReadTzNamesL(aTzId);
	CleanupStack::PushL(names);

	CTzLocalizedTimeZoneRecord* timeZoneRecord = CTzLocalizedTimeZoneRecord::NewL(aTzId, names->StandardName(),
		names->DaylightSaveName(), names->ShortStandardName(), names->ShortDaylightSaveName(), 0);
	CleanupStack::PushL(timeZoneRecord);
	
	CTzLocalizedCityRecord* cityRecord = CTzLocalizedCityRecord::NewL(names->CityName(), 0, 0, aTzId, 0);
	CleanupStack::PushL(cityRecord);

	aCachedCities.AppendL(cityRecord);
	// ownership is transferred into the array
	CleanupStack::Pop(cityRecord);
	aFrequentlyUsedZones.AppendL(timeZoneRecord);
	// ownership is transferred into the array
	CleanupStack::Pop(timeZoneRecord);
	CleanupStack::PopAndDestroy(names);
	}

void CTzLocalizationDb::DeleteCitiesL(TTzUserDataChange aChange)
	{
	// Get the cities with the deleted time zone id
	RDbView cityView;
	CleanupClosePushL(cityView);
	if(aChange.iOperation == ETzUserDataDeleted)
		{
		HBufC* sqlStr = GetCityQueryLC(aChange.iTzId);
		PrepareCityViewL(cityView, *sqlStr);
		CleanupStack::PopAndDestroy(sqlStr);
		sqlStr = NULL;
		}
	else if(aChange.iOperation == ETzUserDataRestored)
		{
		_LIT(KSQLQueryLit,"SELECT * FROM UserCities");
		// Iterate through all the cities and delete them when
		// the corresponding tz id doesn't exist anymore
		PrepareCityViewL(cityView, KSQLQueryLit);
		}
	// Loop through the rowset and delete the cities
	for (cityView.FirstL(); cityView.AtRow(); cityView.NextL())
		{
		if(aChange.iOperation == ETzUserDataDeleted)
			{
			cityView.DeleteL();		
			}
		else if(aChange.iOperation == ETzUserDataRestored)
			{
			cityView.GetL();
			if (!iServer.TimeZoneManager().TzDataProvider().IsIdInDbL(cityView.ColUint16(ETzCityId)))
				{
				cityView.DeleteL();
				}
			}	
		}
	CleanupStack::PopAndDestroy(&cityView);
	}

/**
Releases the mutex if leave occurs between wait and signal.
@internalTechnology
*/
void CTzLocalizationDb::ReleaseMutex(TAny* iTarget)
	{
	RMutex* mutex = static_cast<RMutex*>(iTarget);	
	mutex->Signal();	
	}

// Backup and restore operations
void CTzLocalizationDb::BackupBeginningL()
	{
	CloseDb();
	iLocked = ETrue;
	}

void CTzLocalizationDb::BackupCompletedL()
	{
	OpenDbL();
	iLocked = EFalse;
	}

void CTzLocalizationDb::RestoreBeginningL()
	{
	CloseDb();
	iLocked = ETrue;
	}

void CTzLocalizationDb::RestoreDbL()
	{
	// When the Tz server is connected or a restore completes, we have to
	// refresh the data in the frequently used zones table and the city table
	// because it may contain the time zone id that has been deleted.  Or if the
	// time zone id is still there but the details of the rule has been changed.

	CTzSystemDataDb* systemDataDb = CTzSystemDataDb::NewLC();

	TBool frequentlyUsedZonesNeedUpdating = EFalse;

	RPointerArray<CTzLocalizedTimeZoneRecord> frequentlyUsedZones;
	CleanupStack::PushL(TCleanupItem(CleanupTimeZonePointerArray,&frequentlyUsedZones));

	RPointerArray<CTzLocalizedCityRecord> cachedCities;
	CleanupStack::PushL(TCleanupItem(CleanupCityPointerArray,&cachedCities));

	for (TInt i = CTzLocalizedTimeZone::ECurrentZone; i < CTzLocalizedTimeZone::ECachedTimeZones; ++i)
		{//Loop through each record in frequently used table
		CTzLocalizedTimeZone::TTzFrequentlyUsedZone freqUsedZone = (CTzLocalizedTimeZone::TTzFrequentlyUsedZone)i;
		CTzLocalizedTimeZoneRecord* timeZoneRecord = DoReadFrequentlyUsedZoneL(freqUsedZone);
		TUint tzid = timeZoneRecord->Id();
		delete timeZoneRecord;
		TBool tzIdExistInDb = iServer.TimeZoneManager().TzDataProvider().IsIdInDbL(tzid);
		
		if(!tzIdExistInDb)
			{
			if(freqUsedZone == CTzLocalizedTimeZone::ECurrentZone)
				{
				tzid = GetTimeZoneIdFromTzServerL(*systemDataDb);
				}
			else
				{
				tzid = GetFrequentlyUsedDefaultZoneIdL(freqUsedZone, *systemDataDb);
				}
			}
		
		if (CTzId::IsUserTzId(tzid))
			{
			UpdateTimeZoneAndCityRecordL(frequentlyUsedZones, cachedCities, tzid);	
			}
		else
			{
			UpdateTimeZoneAndCityRecordForSystemDataL(frequentlyUsedZones, cachedCities, tzid, *systemDataDb);
			}

		frequentlyUsedZonesNeedUpdating = ETrue;
		}
			
	if (frequentlyUsedZonesNeedUpdating)
		{
		//Update the frequently used zone table
		DoWriteAllFrequentlyUsedZonesL(frequentlyUsedZones, cachedCities);
		}
	
	TTzUserDataChange change;
	change.iOperation = ETzUserDataRestored;
	change.iTzId = KInvalidTzId;
	//Delete the city infomation if the associated time zone no longer exist.
	DeleteCitiesL(change);
	
	CleanupStack::PopAndDestroy(&cachedCities); 
	CleanupStack::PopAndDestroy(&frequentlyUsedZones);
	CleanupStack::PopAndDestroy(systemDataDb);
	}

/**
Get the localized default time zone Id for the given frequently used time zone.
The default time zone for each of the possible frequently used time zones may be
specified in the TZ Server repository or in the TZ Localization resource file.
Check the possible sources in this order.

@param aFreqUsedZone The frequently used time zone for which the localized
default time zone is required.
@param aSystemDataDb For TZ Localization database access.

@return The localized default time zone Id for the given frequently used time zone.
*/
TInt CTzLocalizationDb::GetFrequentlyUsedDefaultZoneIdL(
	CTzLocalizedTimeZone::TTzFrequentlyUsedZone aFreqUsedZone,
	CTzSystemDataDb& aSystemDataDb)
	{
	// Assume that we will not find the key in the repository or that we do find
	// the key but it has no value.  If either of these scenarios is true then
	// we will use the time zone identifier recorded in the resource file for
	// the default time zone.
	
	TUint32 defaultTimeZoneKey = GetFrequentlyUsedDefaultZoneCenRepKeyL(aFreqUsedZone);
	CRepository* tzRepository = CRepository::NewL(NTzUpdate::KPropertyCategory);
	CleanupStack::PushL(tzRepository);
	TInt defaultTimeZoneId = 0;
	TInt err = tzRepository->Get(defaultTimeZoneKey, defaultTimeZoneId);
	CleanupStack::PopAndDestroy(tzRepository);
	
	if(err != KErrNotFound)
		{
		User::LeaveIfError(err);
		}
	
	if(defaultTimeZoneId == 0)
		{
		defaultTimeZoneId = aSystemDataDb.ReadFrequentlyUsedZoneIdL(aFreqUsedZone);
		}
	
	return defaultTimeZoneId;
	}

/**
Get the TZ Server's CenRep repository key for the given frequently used zone.

@param aFreqUsedZone The frequently used time zone for which the TZ Server's
CenRep repository key is required.

@return The TZ Server's CenRep repository key for the given frequently used
zone.
*/
TUint32 CTzLocalizationDb::GetFrequentlyUsedDefaultZoneCenRepKeyL(CTzLocalizedTimeZone::TTzFrequentlyUsedZone aFreqUsedZone)
	{
	// These key values are copied from those defined in tzconfigagent.h.  We
	// want to keep the key values private - this is one way to do it.
	const TUint32 KDefaultHomeTimeZoneKey = 0x3UL;
	const TUint32 KDefaultInterestTimeZoneKey = 0x4UL;
	const TUint32 KDefaultRecent1TimeZoneKey = 0x5UL;
	const TUint32 KDefaultRecent2TimeZoneKey = 0x6UL;

	TUint32 key = 0;

	switch(aFreqUsedZone)
	    {
	    case CTzLocalizedTimeZone::EHomeZone:
	    	{
	    	key = KDefaultHomeTimeZoneKey;
	        }
	    break;

	    case CTzLocalizedTimeZone::EInterestZone:
	    	{
	    	key = KDefaultInterestTimeZoneKey;
	        }
	    break;

	    case CTzLocalizedTimeZone::ERecentZone1:
	    	{
	    	key = KDefaultRecent1TimeZoneKey;
	        }
	    break;

	    case CTzLocalizedTimeZone::ERecentZone2:
	    	{
	    	key = KDefaultRecent2TimeZoneKey;
	        }
	    break;
	    
	    default:
	    	{
	    	User::Leave(KErrArgument);
	    	}
	    }

	return key;
    }

void CTzLocalizationDb::RestoreCompletedL()
	{
	OpenDbL();
	iLocked = EFalse;
	RestoreDbL();
	}

