/**
* Copyright (c) 2007-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:
*
*/



/**
 @file
 @internalComponent
 @released
*/


#ifndef __PLTABLES_H__
#define __PLTABLES_H__

#include "cntsqlprovider.h"
#include "persistencelayer.h"
#include "clplcontactproperties.h"
#include <cntdef.h>
#include <cntitem.h>
#include <cntfldst.h>

#include <sqldb.h>
#include <e32hashtab.h>


/**
The CPplTableBase class forms the base class for all SQLite tables in the
Persistence Layer. It implements default behaviour for some basic operations.

It holds member variables for the database and SQL statements representing the
4 CRUD operations. The ReadL function is virtual with a default implementation
that does nothing so that it can be overridden in the derived classes, as
necessary. This is to allow the read operation to be performed on all classes
in the same manner, even when in cases where there is nothing to be read. In
this way, all table classes can be treated the same. This is to facilitate the
use of the composite design pattern.
*/
NONSHARABLE_CLASS(CPplTableBase) : public CBase
	{
public:
	virtual void CreateInDbL(CContactItem& aItem) = 0;
	virtual void UpdateL(const CContactItem& aItem) = 0;
	virtual void DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred) = 0;
	virtual void CreateTableL() = 0;
	};


/**
The CPplContactTable class has a dual nature, caused by inheritance from two
interfaces: MPplContactItemPersistor and MLplPersistenceBroker.

MPplContactItemPersistor inheritance makes the CPplContactTable a normal table
capable of perfoming five basic operations (Create, Read, Update, Delete and
ChangeType) when called from the parent table.

MLplPersistenceBroker is a public interface responsible for carrying out the
basic operations.  It is exposed to the users of Persistence Layer making the
RPplContactTable an entry point for these operations.

As a result ContactTable appears twice in the tree structure of tables: once as
a root table (MLplPersistenceBroker inteface) and second time as a child of
Email table (MPplContactItemPersistor inteface).

The Contact table is responsible for saving header information for all the
fields in a contact and the values of most of the fields. Four C/BLOB fields
are used for this purpose.

Header information (including label and content type of the fields) is stored in
in the binary_fields_header and text_fields_header columns.

The binary_fields column accomodates the binary values of the fields (such as
pictures or sounds).

All human readable text values go to the text_fields column. This is particularly
useful for searching through all textual fields of contacts.
*/
NONSHARABLE_CLASS(CPplContactTable) : public CPplTableBase
	{
public:
	class THintField
		{
	friend class CPplContactTable;
	public:
		THintField();
		void UpdateHintL(const CContactItemField& aField, const RArray<TUid>& aCustFiltFields);


		TUint16 ExtHint();
		TInt8 Hint();
	private:
		THintField(TUint16 aExtHint, TUint8 aHint);

	private:
		TInt iValue;
		};


public:
	static CPplContactTable* NewL(RSqlDatabase& aDatabase, CLplContactProperties& aProperties);
	static CPplContactTable* NewLC(RSqlDatabase& aDatabase, CLplContactProperties& aProperties);
	void CreateInDbL(CContactItem& aItem);
	void CreateInDbL(CContactItem& aItem, TUint aSessionId);

	void UpdateL(const CContactItem& aItem);
	void DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred);
	CContactItem* DeleteLC(TContactItemId  aItemId, TBool& aLowDiskErrorOccurred);
	void CreateTableL();

	void ChangeTypeL(TContactItemId aItemId, TUid aNewType);
	TInt NameFieldUid(const CContactItemField& nameField);

	TBool IsTableEmptyL();
	CContactIdArray& CardTemplateIdsL();
	TContactItemId OwnCardIdL();
    void SetOwnCardIdL(TContactItemId aId);	
    
	~CPplContactTable();
	

private:
	CPplContactTable(RSqlDatabase& aDatabase, CLplContactProperties& aProps);
	void ConstructL();
	void WriteContactItemL(const CContactItem& aItem, TCntSqlStatement aType);
	void UpdateTemplateAccessCounterL(TContactItemId aTemplateRefId, TBool aIncrement);
	void GetTypeFlagFields(TInt aTypeFlags, TUid& aType, TUint& aAttributes, TUint& aHintFields);
	TInt GenerateTypeFlags(TUid aType, TUint aAttributes, TUint aHintFields);
	TUint NumDigits(TInt aNum);
	
private:
	CLplContactProperties& iProperties;
	CCntSqlStatement* iInsertStmnt;
	CCntSqlStatement* iDeleteSelectStmnt;
	CCntSqlStatement* iAccessCountUpdateStmnt;
	CCntSqlStatement* iUpdateFlagsStmnt;
	CCntSqlStatement* iUpdateStmnt;
	CCntSqlStatement* iDeleteStmnt;
	RHashMap<TInt, TPtrC> iFieldMap;
	RSqlDatabase&  iDatabase;
	
	CContactIdArray* iCardTemplateIds;
	};

/**
The CPplCommAddrTable class is used to store all communication addresses in a
contact item. This includes telephone/fax numbers, email addresses and SIP
addresses.

Email and SIP addresses are stored as-is but telephone/fax numbers are hashed
in this table. This is because in the text field in the contact table, telephone
numbers are stored as text. This provides great flexibility for the users as they
can use any symbol in their telephone number fields, such as '+','(',')', spaces
or even words like 'ext.' or 'emergency only'. However, this form of information
storage present a big problem in the phone matching use case i.e. when the
Contacts Model should quickly find a contact with the given phone number in it.

This problem is solved with the hashing mechanism.  Each time the user creates a
new phone field or changes the existing one the hash value is calculated and
saved in the phone table.  The hash value is calculated with the help of the
cntphone plugin, which parses the text containing the telephone number and tries
to find all the informative digits and symbols.

Originally the hash value covered only last seven digits of the phone number and
the TInt column was enough to store the hash.  Later the value was extended and
now it is stored in two TInt columns to provide data backward compatibility with
previous versions of the Contacts Model.

The comm_addr table contains a private helper class TMatch that represents the
hash value and an enumeration to specify type of communication address.
*/
NONSHARABLE_CLASS(CPplCommAddrTable) : public CPplTableBase
	{
private:
	class TMatch
		{
	public:
		TMatch();

		static TInt32 CreateHashL(const TDesC& aPhoneNumberString, TInt aMatchLength, TInt& aNumPhoneDigits);
		static void StripOutNonDigitChars(TDes& aText);
		static TInt32 PadOutPhoneMatchNumber(TInt32& aPhoneNumber,TInt aPadOutLength);
		static TBool Equals (const TMatch& aRMatch ,const TMatch& aLMatch);
		inline TBool operator==(const TMatch &aMatch) const;

	public:
		TInt32 iLowerSevenDigits;
		TInt32 iUpperDigits;
		TInt iNumLowerDigits;
		TInt iNumUpperDigits;
		};

public:
	// defines type of communication address.
	// !! the values these represent are persisted in the database  !!
	// !!  do not change the order -- add new ones to the bottom    !!
	// !!   changing these values could break data compatibility    !!
	enum TCommAddrType
		{
		EPhoneNumber,
		EEmailAddress,
		ESipAddress
		};

public:
	static CPplCommAddrTable* NewL(RSqlDatabase& aDatabase, CLplContactProperties& aProperties);
	static CPplCommAddrTable* NewLC(RSqlDatabase& aDatabase, CLplContactProperties& aProperties);
	void CreateInDbL(CContactItem& aItem);
	void UpdateL(const CContactItem& aItem);
	void DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred);
	void CreateTableL();

	CContactIdArray* MatchPhoneNumberL(const TDesC& aNumber, const TInt aMatchLengthFromRight);
	CContactIdArray* MatchEmailAddressL(const TDesC& aEmailAddr);
	CContactIdArray* MatchSipAddressL(const TDesC& aSipAddr);

	~CPplCommAddrTable();

private:
	void ConstructL();
	CPplCommAddrTable(RSqlDatabase& aDatabase, CLplContactProperties& iProperties);
	void RemoveNonUpdatedAddrsL(RArray<TMatch>& aNewPhones, RArray<TPtrC>& aNewEmails, RArray<TPtrC>& aNewSips, 
							RArray<TInt>& aFreeCommAddrIds, const TInt aItemId);
	void DoUpdateCommAddrsL(RArray<TMatch>& aNewPhones, RArray<TPtrC>& aNewEmails, RArray<TPtrC>& aNewSips, 
						    RArray<TInt>& aFreeCommAddrIds, const TInt aItemId);
	void DeleteSingleCommAddrL(TInt aCommAddrId, TBool& aLowDiskErrorOccurred);
	void DoPhoneNumWriteOpL(const CPplCommAddrTable::TMatch& aPhoneNum, TCntSqlStatement aType, TInt aCntId);
	void DoPhoneNumWriteOpL(const CPplCommAddrTable::TMatch& aPhoneNum, TCntSqlStatement aType, TInt aCntId, 
							TInt aCommAddrId);
	void DoNonPhoneWriteOpL(const TDesC& aAddress, TCntSqlStatement aType, TInt aCntId, 
							TCommAddrType aAddrType);
	void DoNonPhoneWriteOpL(const TDesC& aAddress, TCntSqlStatement aType, TInt aCntId, 
							TCommAddrType aAddrType, TInt aCommAddrId);
	CContactIdArray* MatchNonPhoneAddrL(const TDesC& aCommAddr, TCommAddrType aAddrType);
	CPplCommAddrTable::TMatch CreatePaddedPhoneDigitsL(const TDesC& aNumber, const TInt aNumLowerDigits, 
							const TInt aNumUpperDigits);
	CPplCommAddrTable::TMatch CreatePhoneMatchNumberL(const TDesC& aText, TInt aLowerMatchLength, 
							TInt aUpperMatchLength);

private:
	CLplContactProperties& iProperties;
	CCntSqlStatement* iInsertStmnt;
	CCntSqlStatement* iWholeSelectStmnt;
	CCntSqlStatement* iMatchSelectStmnt;
	CCntSqlStatement* iUpdateStmnt;
	CCntSqlStatement* iAllForItemDeleteStmnt;
	CCntSqlStatement* iSingleDeleteStmnt;
	RSqlDatabase&  iDatabase;
	};


/**
The CPplGroupsTable class provides a mapping between contact groups and members
of those groups. The groups table provides two fields: contact_group_id and
contact_group_member_id. Both of these are foreign keys referencing the contact_id
field of the contact table. Using these two fields, lookup can be performed in 
either direction: members of a group; groups of which item is a member.
*/
NONSHARABLE_CLASS(CPplGroupsTable) : public CPplTableBase
	{
public:
	static CPplGroupsTable* NewL(RSqlDatabase& aDatabase);
	static CPplGroupsTable* NewLC(RSqlDatabase& aDatabase);
	void CreateInDbL(CContactItem& aItem);
	void ReadL(CContactItem& aItem);
	void UpdateL(const CContactItem& aItem);
	void DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred);
	void CreateTableL();
	~CPplGroupsTable();

private:
	void ConstructL();
	CContactIdArray* GetListForItemL(TContactItemId aItemId, TBool aIsGroup);
	void DeleteItemL(TContactItemId aItemId, TBool& aLowDiskErrorOccurred);
	CPplGroupsTable(RSqlDatabase& aDatabase);
	void WriteGroupMembersL(const CContactItem& aGroup);

private:
	CCntSqlStatement* iInsertStmnt;
	CCntSqlStatement* iSelectGroupsStmnt;
	CCntSqlStatement* iSelectMembersStmnt;
	CCntSqlStatement* iDeleteStmnt;
	CCntSqlStatement* iCountContactsStmnt;
	
	RSqlDatabase&  iDatabase;
	};


/**
This class holds a set of contact database preferences and is used in conjunction
with the CPplPreferencesPersistor class.
*/
NONSHARABLE_CLASS(RCntPreferences)
	{
public:
	TUint DataSchemaVersion() const;
	TUint DatabaseUid() const;
	TTime CreationDate() const;
	TUint PreferCardTemplateId() const;	
	RContactViewSortOrder& PreferredSortOrder();
	RCntPreferences();
	void Close();
	void SetDataSchemaVersion(TUint aDbSchemaVersion);
	void SetDatabaseUid(TUint aDbUid);
	void SetCreationDate(TTime aCreationDate);
	void SetFieldTypeFixsize(TUint aFieldTypeFixsize);
	void SetPreferredSortOrderL(const RContactViewSortOrder& aSortOrder);
	void SetPreferCardTemplateId(TUint aTemplateId);	

private:
	TUint iDbSchemaVersion;
	TUint iDbUid;
	TTime iCreationDate;
	TUint iPreferCardTemplateId;	
	RContactViewSortOrder iSortOrder;
	};

/**
The CPplPreferencesPersistor class is used to store contact database preferences,
as represented by the RCntPreferences class and, currently, stored in the 
preferences table in the SQLite database. As this information is different from
the rest of the data in that it is not contacts data but metadata about the 
database itself, it is treated differently. Hence, this class is called a persistor
and not a table class to emphasise the differences and to provide the flexibility
to change the way the preferences information is persist in the future if this is
desired.

*/
NONSHARABLE_CLASS(CPplPreferencesPersistor) : public CBase
	{
public:
	static CPplPreferencesPersistor* NewL(RSqlDatabase& aDatabase);
	~CPplPreferencesPersistor();
	
	void CreateTableL();

    // getters
	TDesC DataSchemaVersion() const;
	TInt64 MachineIdL();
	TTime CreationDateL();
	TContactItemId PreferredCardTemplateIdL();	
	const CArrayFix<CContactDatabase::TSortPref>& PreferredSortOrderL();
	TPtrC DatabaseUidL();	
		
	// setters 	
	void SetMachineIdL(TInt64 aMachineId);
	void SetPreferredCardTemplateIdL(TContactItemId aTemplateId);
	void SetPreferredSortOrderL(CArrayFix<CContactDatabase::TSortPref>* aSortOrder);	 
	
	// utility methods
	void SetGuidL(CContactItem& aContact, TBool& aCompressed);
	
private:
	CPplPreferencesPersistor(RSqlDatabase& aDatabase);
	void ConstructL();
	void FirstWriteToTableL();
	void GenerateMachineUniqueID();
	void ReadInStateL();
	void PersistStateL(const TDesC& aParam, TInt aValue);

private:
	RSqlDatabase& iDatabase;
	CCntSqlStatement* iUpdateStmnt;
	TUint iMachineId;        // machine id    
	TTime iCreationDate;     // creation data
	TBuf<40> iUidString;     //unique id  
	TContactItemId iPreferCardTemplateId;   // template	
	CArrayFix<CContactDatabase::TSortPref>* iSortOrderPrefs;		 // sort order
	};

#endif
