/*
* Copyright (c) 2008 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:  Handles PIM contact list <-> Contacts Model conversions
 *
*/


// CLASS HEADER
#include "cpimcontactlistadapter.h"

// INTERNAL INCLUDES
#include "cpimcontactcategorymanager.h"
#include "cpimcontactitemadapter.h"
#include "mpimcontactitem.h"
#include "mpimitemdata.h"
#include "logger.h"

// EXTERNAL INCLUDES
#include <badesca.h>
#include <cntdb.h>
#include <cntitem.h> // CContactGroup
#include <cntfilt.h> // CCntFilter


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CPIMContactListAdapter* CPIMContactListAdapter::NewL(
    java::util::FunctionServer* aFuncServer)
{
    JELOG2(EPim);
    CPIMContactListAdapter* self =
        new(ELeave) CPIMContactListAdapter(aFuncServer);

    CallMethodL(self, &CPIMContactListAdapter::ConstructL, self->iFuncServer);

    return self;
}

// Destructor
CPIMContactListAdapter::~CPIMContactListAdapter()
{
    JELOG2(EPim);
    Close();
}
// -----------------------------------------------------------------------------
// CPIMContactListAdapter::HandleDatabaseEventL
// Tests the contact database observer event type and handles it. The ID of
// a contact affected by the change event, if relevant, can be retrieved
// via TContactDbObserverEvent::iContactId.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::HandleDatabaseEventL(
    TContactDbObserverEvent aEvent) // information about the change event
{
    JELOG2(EPim);
    switch (aEvent.iType)
    {
    case EContactDbObserverEventGroupAdded:
    {
        ExternalGroupChangeL(aEvent.iContactId, EPIMExternalChangeNew);
        break;
    }
    case EContactDbObserverEventGroupChanged:
    {
        ExternalGroupChangeL(aEvent.iContactId, EPIMExternalChangeModified);
        break;
    }
    case EContactDbObserverEventGroupDeleted:
    {
        ExternalGroupChangeL(aEvent.iContactId, EPIMExternalChangeRemoved);
        break;
    }
    case EContactDbObserverEventContactAdded:
    {
        ExternalItemChangeL(aEvent.iContactId, EPIMExternalChangeNew);
        break;
    }
    case EContactDbObserverEventContactChanged:
    {
        ExternalItemChangeL(aEvent.iContactId, EPIMExternalChangeModified);
        break;
    }
    case EContactDbObserverEventContactDeleted:
    {
        ExternalItemChangeL(aEvent.iContactId, EPIMExternalChangeRemoved);
        break;
    }
    default:
    {
        // we don't care about other changes
        return;
    }
    }
}


// -----------------------------------------------------------------------------
// CPIMContactListAdapter::GetCategoriesL
// Provides all categories currently existing in the native database.
// Returns: Array of categories
// -----------------------------------------------------------------------------
//
const CDesCArray& CPIMContactListAdapter::GetCategoriesL()
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iCategoryManager, User::Leave(KErrNotReady));
    return iCategoryManager->CallGetCategoriesL();
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::AddCategoryL
// Adds a new category to the native database.
// If the category already  exists, nothing is done and the method returns
// successfully.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::AddCategoryL(const TDesC& aNewCategory)
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iCategoryManager, User::Leave(KErrNotReady));
    CallMethodL(iCategoryManager, &CPIMContactCategoryManager::AddCategoryL,
                aNewCategory, iFuncServer);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::DeleteCategoryL
// Deletes an existing category.
// If there is no such category, nothing is done and the method returns
// successfully.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::DeleteCategoryL(const TDesC& aCategory)
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iCategoryManager, User::Leave(KErrNotReady));
    CallMethodL(iCategoryManager, &CPIMContactCategoryManager::DeleteCategoryL,
                aCategory, iFuncServer);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::RenameCategoryL
// Renames an existing category.
// Entries in the old category are moved to the new category.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::RenameCategoryL(const TDesC& aOldCategory, // The old category name
        const TDesC& aNewCategory) // The new category name
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iCategoryManager, User::Leave(KErrNotReady));
    CallMethodL(iCategoryManager, &CPIMContactCategoryManager::RenameCategoryL,
                aOldCategory, aNewCategory, iFuncServer);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::IsCategoriesExternallyModified
// Checks whether there have been external changes to the categories in
// the native database after last call to
// GetExternalCategoryModifications or list adapter creation.
// Returns: ETrue: If there are any external changes
//          EFalse: Otherwise
// -----------------------------------------------------------------------------
//
TBool CPIMContactListAdapter::IsCategoriesExternallyModified()
{
    JELOG2(EPim);
    if (iCategoryChanges)
    {
        return ETrue;
    }
    return EFalse;
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::GetExternalCategoryModificationsL
// Provides the external changes to the categories in the native database.
// Returns: An array of category state change objects. The ownership
//          of the array is transferred to the caller. Note that the
//          array elements contain heap-allocated data.
// -----------------------------------------------------------------------------
//
RPointerArray<CPIMCategoryStateChange>*
CPIMContactListAdapter::GetExternalCategoryModificationsL()
{
    JELOG2(EPim);
    iMutex.Wait();
    CallMethodL(this, &CPIMContactListAdapter::IsDatabaseReadyL, iFuncServer);
    RPointerArray<CPIMCategoryStateChange>* retval = iCategoryChanges;
    iCategoryChanges = NULL;
    iMutex.Signal();
    return retval;
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::IsItemsExternallyModified
// Checks whether there have been external changes to the items in
// the native database after last call to
// GetExternalItemModifications or list adapter creation.
// Returns: EExternalChangesMinor: If there are any external changes
//          EExternalChangesNone: Otherwise
// -----------------------------------------------------------------------------
//
MPIMListAdapter::TExternalItemChangeClass CPIMContactListAdapter::IsItemsExternallyModified()
{
    JELOG2(EPim);
    if ((iItemChanges || iFirstItemChanges))
    {
        return MPIMListAdapter::EExternalChangesMinor;
    }
    return MPIMListAdapter::EExternalChangesNone;
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::GetExternalItemModificationsL
// Provides the external changes to the items in the native database.
// Returns: An array of item state change objects. The ownership
//          of the array is transferred to the caller. Note that the
//          array elements contain heap-allocated data.
// -----------------------------------------------------------------------------
//
RPointerArray<CPIMItemStateChange>*
CPIMContactListAdapter::GetExternalItemModificationsL()
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::IsDatabaseReadyL, iFuncServer);

    // the first time,
    // we check for all cards
    if (iFirstItemChanges)
    {
        //FilterAllContactsL();
        CallMethodL(this, &CPIMContactListAdapter::FilterAllContactsL,
                    iFuncServer);
        iFirstItemChanges = EFalse;
    }

    RPointerArray<CPIMItemStateChange>* retval = iItemChanges;
    iItemChanges = NULL;
    return retval;
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::Close
// Used to inform the list adapter that the list has been closed.
// The list adapter may then release all resources it has reserved.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::Close()
{
    JELOG2(EPim);
    CallMethod(this, &CPIMContactListAdapter::DoClose, iFuncServer);
}

void CPIMContactListAdapter::DoClose()
{
    JELOG2(EPim);
    if (iCategoryChanges)
    {
        iCategoryChanges->ResetAndDestroy();
        delete iCategoryChanges;
        iCategoryChanges = NULL;
    }
    if (iItemChanges)
    {
        iItemChanges->ResetAndDestroy();
        delete iItemChanges;
        iItemChanges = NULL;
    }
    delete iNotifier;
    iNotifier = NULL;
    delete iItemAdapter;
    iItemAdapter = NULL;
    delete iCategoryManager;
    iCategoryManager = NULL;
    delete iDatabase;
    iDatabase = NULL;
    delete iMinimalFieldsViewDef;
    iMinimalFieldsViewDef = NULL;
    iMutex.Close();
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::GetPimListAdapter
// Provides access to the MPIMListAdapter representation of this
// MPIMContactListAdapter object.
// -----------------------------------------------------------------------------
//
MPIMListAdapter* CPIMContactListAdapter::GetPimListAdapter()
{
    JELOG2(EPim);
    return this;
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::CreateContactItemL
// Creates a new contact item (entry) in the native database.
// The adapter creates a new native database entry, sets its data
// according to the data in aContactItem, adds it to the database
// and sets the Item ID of aContactItem. aContactItem must
// contain valid data and have Item ID KPIMNullItemID.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::CreateContactItemL(MPIMContactItem& aContactItem) // The contact item to add
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::DoCreateContactItemL,
                aContactItem, iFuncServer);
    ReadContactItemL(aContactItem);
}

void CPIMContactListAdapter::DoCreateContactItemL(MPIMContactItem& aContactItem) // The contact item to add
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iItemAdapter || iDatabase, User::Leave(KErrNotReady));
    CContactCard* card = iItemAdapter->CardL(aContactItem);
    CleanupStack::PushL(card);
    // Leaves with KErrDiskFull if there is not enough disk
    // space to create a new contact item
    TContactItemId id = iDatabase->AddNewContactL(*card);
    // Set new item id. If this leaves it means that
    // SetContactItemIdL leaved with KErrOutNoMemory
    TRAPD(err, aContactItem.SetContactItemIdL(id));
    if (KErrNone != err)
    {
        TBuf8<KPIMItemIdDesSize> entryId;
        entryId.Num(static_cast<TUint>(id));
        DoRemoveContactItemL(entryId);
        User::Leave(err);
    }
    iItemAdapter->UpdateCategoriesL(aContactItem, *card);
    CleanupStack::PopAndDestroy(card);
    // update possible changes (e.g. dropped attributes) to the item
    aContactItem.PrepareForLoadL();
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ReadContactItemL
// Reads an existing contact item from the native database.
// The adapter maps the Item ID in aContactItem to a native database
// entry identifier, reads the entry and sets the data of
// aContactItem according to the data in the native entry. This operation
// is very performance sensitive, so it must be used with cause and only
// when the full contact item is needed
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::ReadContactItemL(MPIMContactItem& aContactItem) // The contact item to be read
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::DoCallReadContactItemL,
                aContactItem, iFuncServer);
}

void CPIMContactListAdapter::DoCallReadContactItemL(
    MPIMContactItem& aContactItem) // The contact item to be read
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iItemAdapter || iDatabase, User::Leave(KErrNotReady));
    const TContactItemId id = aContactItem.ContactItemIdL();
    __ASSERT_ALWAYS(id != 0, User::Leave(KErrArgument));
    // If the contact item contains data we don't want to overwrite it
    // so we create a mask item view definition if needed. Otherwise
    // we just read full contact item from the database
    CContactItemViewDef* itemViewDef = CContactItemViewDef::NewLC(
                                           CContactItemViewDef::EMaskFields,
                                           CContactItemViewDef::EIncludeHiddenFields);
    CArrayFix<TPIMField>* fields = aContactItem.ItemData().FieldsLC();
    TInt count = fields->Count();

    // Mask fields and do not include. Note that if there is no fields in the
    // item the view should provide all possible fields from the database
    for (TInt i = 0; i < count; i++)
    {
        TPIMContactField field = static_cast<TPIMContactField>(fields->At(i));
        // Get Contacts Model fields mapped to PIM fields
        CArrayFix<TInt>* fieldArray = iItemAdapter->ContactsModelFieldTypeL(
                                          field);
        CleanupStack::PushL(fieldArray);
        // Add retrieved fields to the item view definition
        TInt fieldCount = fieldArray->Count();
        for (TInt j = 0; j < fieldCount; j++)
        {
            const TUid fieldUid =
                { fieldArray->At(j) };
            // Do not add the field to the view if it already contains
            // this type of a field (e.g the field is a name array element)
            if (itemViewDef->Find(fieldUid) == KErrNotFound)
            {
                itemViewDef->AddL(fieldUid);
            }
        }
        CleanupStack::PopAndDestroy(fieldArray);
    }
    DoReadContactItemL(aContactItem, *itemViewDef);
    CleanupStack::PopAndDestroy(2, itemViewDef);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ReadMinimalContactItemL
// This version reads a minimal contact item from the native database by using
// contact database view definition. The view definition is initialized in
// the constructor of this class and needs to be up to date with currently
// required contact fields when the item is read for the first time. This
// function was introduced due to improve poor performance of the PIM API
// ContactList
// -----------------------------------------------------------------------------

void CPIMContactListAdapter::ReadMinimalContactItemL(
    MPIMContactItem& aContactItem)
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::DoCallReadMinimalContactItemL,
                aContactItem, iFuncServer);
}

void CPIMContactListAdapter::DoCallReadMinimalContactItemL(
    MPIMContactItem& aContactItem)
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iItemAdapter || iDatabase, User::Leave(KErrNotReady));
    TContactItemId id = aContactItem.ContactItemIdL();
    __ASSERT_ALWAYS(id != 0, User::Leave(KErrArgument));

    // Reset item for reading
    aContactItem.PrepareForLoadL();

    // Use contact database filtering. Currently only all name elements
    // are retrieved from the database to improve performance of the list
    DoReadContactItemL(aContactItem, *iMinimalFieldsViewDef);

}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ReadMinimalContactItemL
// This version reads a minimal contact item from the native database by using
// contact database view definition. The view definition is initialized in
// the constructor of this class and it can be modified to fetch specified fields
// by the matching item given as an argument to this function
// -----------------------------------------------------------------------------

void CPIMContactListAdapter::ReadMinimalContactItemL(
    MPIMContactItem& aContactItem, const MPIMContactItem& aMatchingContactItem)
{
    JELOG2(EPim);
    // Get fields from the matching item and add those to the view definition
    CArrayFix<TPIMField>* fields = aMatchingContactItem.ItemData().FieldsLC();
    TInt count = fields->Count();
    for (TInt i = 0; i < count; i++)
    {
        TPIMContactField field = static_cast<TPIMContactField>(fields->At(i));
        // Get Contacts Model fields mapped to PIM fields
        CArrayFix<TInt>* fieldArray = iItemAdapter->ContactsModelFieldTypeL(
                                          field);
        CleanupStack::PushL(fieldArray);
        // Add retrieved fields to the item view definition
        TInt fieldCount = fieldArray->Count();
        for (TInt j = 0; j < fieldCount; j++)
        {
            const TUid fieldUid =
                { fieldArray->At(j) };
            // Do not add the field to the view if it already contains
            // this type of a field (e.g the field is a name array element)
            if (iMinimalFieldsViewDef->Find(fieldUid) == KErrNotFound)
            {
                iMinimalFieldsViewDef->AddL(fieldUid);
            }
        }
        CleanupStack::PopAndDestroy(fieldArray);
    }
    CleanupStack::PopAndDestroy(fields);
    // Read contact item using minimal field definition
    ReadMinimalContactItemL(aContactItem);
    // Reset the view definition to its previous state
    InitializeMinimalViewDefinitionL();
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ReadContactFieldL
// Reads one field from the contact database
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::ReadContactFieldL(MPIMContactItem& aContactItem,
        TPIMContactField aContactField)
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::DoReadContactFieldL,
                aContactItem, aContactField, iFuncServer);
}

void CPIMContactListAdapter::DoReadContactFieldL(MPIMContactItem& aContactItem,
        TPIMContactField aContactField)
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iItemAdapter || iDatabase, User::Leave(KErrNotReady));
    TContactItemId id = aContactItem.ContactItemIdL();
    __ASSERT_ALWAYS(id != 0, User::Leave(KErrArgument));

    // Create new view definition
    CContactItemViewDef* itemViewDef = CContactItemViewDef::NewLC(
                                           CContactItemViewDef::EIncludeFields,
                                           CContactItemViewDef::EIncludeHiddenFields);
    CArrayFix<TInt>* fieldArray = iItemAdapter->ContactsModelFieldTypeL(
                                      aContactField);
    CleanupStack::PushL(fieldArray);
    // Add retrieved fields to the item view definition
    TInt fieldCount = fieldArray->Count();
    for (TInt i = 0; i < fieldCount; i++)
    {
        const TUid fieldUid =
            { fieldArray->At(i) };
        itemViewDef->AddL(fieldUid);
    }
    // The view cannot be empty so check the field count in debug builds
    __ASSERT_DEBUG(itemViewDef->Count() > 0, User::Panic(KPIMPanicCategory,
                   EPIMPanicInvalidState));
    CleanupStack::PopAndDestroy(fieldArray);
    DoReadContactItemL(aContactItem, *itemViewDef);
    CleanupStack::PopAndDestroy(itemViewDef);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::WriteContactItemL
// Writes an existing contact item to the native database.
// The adapter maps the Item ID in aContactItem to a native database
// entry identifier, reads the entry and sets the data of the entry
// according to the data in aContactItem.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::WriteContactItemL(MPIMContactItem& aContactItem) // The contact item to write
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::DoWriteContactItemL,
                aContactItem, iFuncServer);
    ReadContactItemL(aContactItem);
}

void CPIMContactListAdapter::DoWriteContactItemL(MPIMContactItem& aContactItem) // The contact item to write
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iItemAdapter || iDatabase, User::Leave(KErrNotReady));
    TContactItemId id = aContactItem.ContactItemIdL();
    __ASSERT_ALWAYS(id != 0, User::Leave(KErrArgument));

    // OpenContactLX leaves the lock item in the cleanup stack
    CContactCard* contactItem =
        static_cast<CContactCard*>(iDatabase->OpenContactLX(id));
    CleanupStack::PushL(contactItem);
    iItemAdapter->UpdateCardL(aContactItem, *contactItem);
    iDatabase->CommitContactL(*contactItem);
    // Note: Even though the above line closed the contact, we can still
    // keep the lock item in the cleanup stack as closing a contact
    // twice is harmless
    iItemAdapter->UpdateCategoriesL(aContactItem, *contactItem);
    CleanupStack::PopAndDestroy(contactItem);
    CleanupStack::Pop(); // contactItem lock item
    // update possible changes (e.g. dropped attributes) to the item
    aContactItem.PrepareForLoadL();
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ReadContactItemL
// Removes an existing contact from the native database.
// The adapter maps aItemID to a native database entry and removes it.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::RemoveContactItemL(TPIMItemID aItemID)
{
    JELOG2(EPim);
    CallMethodL(this, &CPIMContactListAdapter::DoRemoveContactItemL, aItemID,
                iFuncServer);
}

void CPIMContactListAdapter::DoRemoveContactItemL(TPIMItemID aItemID)
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iDatabase, User::Leave(KErrNotReady));
    // Convert string id to integer id
    TUint id;
    TLex8 lex(aItemID);
    TInt status = lex.Val(id);
    User::LeaveIfError(status);
    // Delete contact from the contact database
    iDatabase->DeleteContactL(id);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::CPIMContactListAdapter
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CPIMContactListAdapter::CPIMContactListAdapter(
    java::util::FunctionServer* aFuncServer)
{
    JELOG2(EPim);
    iFirstItemChanges = ETrue;
    iFuncServer = aFuncServer;
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::ConstructL()
{
    JELOG2(EPim);
    // Create default contact database if it does not exist. This may happen
    // if the current Phonebook engine is not running or started during
    // device boot. Usually, the database should be available but this is precaution
    if (!CContactDatabase::DefaultContactDatabaseExistsL())
    {
        iDatabase
        = CContactDatabase::CreateL(/*CContactDatabase::EMultiThread*/);
    }
    else
    {
        iDatabase = CContactDatabase::OpenL(/*CContactDatabase::EMultiThread*/);
    }

    // Create other contact adapter resources and initialize item view
    iCategoryManager
    = CPIMContactCategoryManager::NewL(*iDatabase, iFuncServer);
    iNotifier = CContactChangeNotifier::NewL(*iDatabase, this);
    iItemAdapter = CPIMContactItemAdapter::NewL(*iCategoryManager);
    // Initialize minimal fields view definition
    iMinimalFieldsViewDef = CContactItemViewDef::NewL(
                                CContactItemViewDef::EIncludeFields,
                                CContactItemViewDef::EIncludeHiddenFields);
    InitializeMinimalViewDefinitionL();
    User::LeaveIfError(iMutex.CreateLocal());
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ExternalGroupChangeL
// Adds an entry to external category change list.
// Also causes the category cache to be flushed.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::ExternalGroupChangeL(TContactItemId aId, // Id of the changed category
        TPIMExternalChangeType aType) // type of the change
{
    JELOG2(EPim);
    iMutex.Wait();
    iCategoryManager->FlushCache();

    // This should never happen
    __ASSERT_ALWAYS(iCategoryManager, User::Leave(KErrNotReady));

    if (!iCategoryChanges)
    {
        iCategoryChanges
        = new(ELeave) RPointerArray<CPIMCategoryStateChange> (1);
    }
    CPIMCategoryStateChange* change = NULL;
    switch (aType)
    {
    case EPIMExternalChangeModified:
    {
        HBufC* newCategoryName = iCategoryManager->GroupLabelL(aId);
        CleanupStack::PushL(newCategoryName);
        HBufC* oldCategoryName = iCategoryManager->LabelFromCacheL(aId);
        CleanupStack::PushL(oldCategoryName);
        change
        = new(ELeave) CPIMCategoryStateChange(oldCategoryName, aType, newCategoryName);
        CleanupStack::Pop(2); // newCategoryName and oldCategoryName are
        // now owned by change
        break;
    }
    case EPIMExternalChangeNew:
    {
        HBufC* newCategoryName = iCategoryManager->GroupLabelL(aId);
        CleanupStack::PushL(newCategoryName);
        change = new(ELeave) CPIMCategoryStateChange(newCategoryName, aType);
        CleanupStack::Pop(); // newCategoryName is now owned by change
        break;
    }
    case EPIMExternalChangeRemoved:
    {
        HBufC* oldCategoryName = iCategoryManager->LabelFromCacheL(aId);
        CleanupStack::PushL(oldCategoryName);
        change = new(ELeave) CPIMCategoryStateChange(oldCategoryName, aType);
        CleanupStack::Pop(); // newCategoryName is now owned by change
        break;
    }
    default:
    {
        User::Leave(KErrArgument);
    }
    }

    CleanupStack::PushL(change);
    User::LeaveIfError(iCategoryChanges->Append(change));
    CleanupStack::Pop(change);
    iMutex.Signal();
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::ExternalItemChangeL
// Adds an entry to external item change list.
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::ExternalItemChangeL(TContactItemId aId, // Id of the changed category
        TPIMExternalChangeType aType) // type of the change
{
    JELOG2(EPim);
    if (!iItemChanges)
    {
        iItemChanges = new(ELeave) RPointerArray<CPIMItemStateChange> (10);
    }
    HBufC8* changeId = HBufC8::NewLC(KPIMItemIdDesSize);
    TPtr8 entryId = changeId->Des();
    entryId.Num(static_cast<TUint>(aId));
    CPIMItemStateChange* change =
        new(ELeave) CPIMItemStateChange(changeId, aType);
    // The ownership of changeId is transferred to the item state change object
    CleanupStack::Pop(changeId);

    CleanupStack::PushL(change);
    iItemChanges->AppendL(change);
    CleanupStack::Pop(change);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::FilterAllContacts
// Filters all contact items from the database to the changes list
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::FilterAllContactsL()
{
    JELOG2(EPim);
    CCntFilter* filter = CCntFilter::NewLC();
    filter->SetIncludeNewContacts(EFalse);
    if (!iItemChanges)
    {
        iItemChanges = new(ELeave) RPointerArray<CPIMItemStateChange> (10);
    }
    filter->SetContactFilterTypeALL(EFalse);
    filter->SetContactFilterTypeCard(ETrue);
    iDatabase->FilterDatabaseL(*filter);
    CContactIdArray* idArray = filter->iIds;
    TInt idCount = idArray->Count();
    TInt i = 0;
    for (i = 0; i < idCount; i++)
    {
        TContactItemId id = (*idArray)[i];
        TBool wasInList = EFalse;
        TInt changeCount = iItemChanges->Count();
        for (TInt j = 0; j < changeCount; j++)
        {
            TBuf8<KPIMItemIdDesSize> entryId;
            entryId.Num(static_cast<TUint>(id));
            if ((*iItemChanges)[j]->ItemID().Compare(entryId) == 0)
            {
                wasInList = ETrue;
            }
        }
        if (!wasInList)
        {
            ExternalItemChangeL(id, EPIMExternalChangeNew);
        }
    }
    CleanupStack::PopAndDestroy(); // filter
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::InitializeMinimalViewDefinitionL
// Initializes minimal view definition
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::InitializeMinimalViewDefinitionL()
{
    JELOG2(EPim);
    __ASSERT_DEBUG(iMinimalFieldsViewDef, User::Panic(KPIMPanicCategory,
                   EPIMPanicInvalidState));
    // Reset view definition
    iMinimalFieldsViewDef->Reset();
    // Add fields which are to retrieved from the native database
    // when minimal item is to be created or read
    iMinimalFieldsViewDef->AddL(KUidContactFieldAdditionalName);
    iMinimalFieldsViewDef->AddL(KUidContactFieldGivenName);
    iMinimalFieldsViewDef->AddL(KUidContactFieldFamilyName);
    iMinimalFieldsViewDef->AddL(KUidContactFieldGivenNamePronunciation);
    iMinimalFieldsViewDef->AddL(KUidContactFieldFamilyNamePronunciation);
    iMinimalFieldsViewDef->AddL(KUidContactFieldPrefixName);
    iMinimalFieldsViewDef->AddL(KUidContactFieldSuffixName);
}

// -----------------------------------------------------------------------------
// CPIMContactListAdapter::DoReadContactItemL
// Reads contact from the contacts database using item view definition
// -----------------------------------------------------------------------------
//
void CPIMContactListAdapter::DoReadContactItemL(MPIMContactItem& aContactItem,
        const CContactItemViewDef& aContactItemViewDef)
{
    JELOG2(EPim);
    const TContactItemId id = aContactItem.ContactItemIdL();
    __ASSERT_DEBUG(id != 0, User::Panic(KPIMPanicCategory,
                                        EPIMPanicInvalidItemID));
    // Read contact item using the item view definition
    CContactCard* contactItem =
        static_cast<CContactCard*>(iDatabase->ReadContactLC(id,aContactItemViewDef));
    // Set date and fill the PIM item
    TTime lastModified = contactItem->LastModified();
    aContactItem.SetLastModifiedL(lastModified);
    iItemAdapter->FillItemL(aContactItem, *contactItem);
    CleanupStack::PopAndDestroy(contactItem);
}

void CPIMContactListAdapter::IsDatabaseReadyL()
{
    JELOG2(EPim);
    __ASSERT_ALWAYS(iDatabase, User::Leave(KErrNotReady));
}

// End of file
