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


#include "CVPbkFindView.h"
#include <MVPbkFieldType.h>
#include <CVPbkSortOrder.h>
#include <CVPbkAsyncCallback.h>
#include <MVPbkViewContact.h>
#include <CVPbkAsyncOperation.h>
#include <VPbkError.h>

#include <CVPbkFieldTypeRefsList.h>
#include <CVPbkContactNameConstructionPolicy.h>
#include <CVPbkContactFindPolicy.h>
#include <MVPbkContactBookmarkCollection.h>
#include <CVPbkContactFieldIterator.h>
#include <MVPbkContactFieldData.h>
#include <MVPbkContactFieldTextData.h>

#include <VPbkDebug.h>

namespace {
const TInt KObserverArrayGranularity( 4 );
const TInt KGranularity( 2 );

// ---------------------------------------------------------------------------
// Event sending function for one reference parameter
// ---------------------------------------------------------------------------
// 
template <class NotifyFunc>
void SendEventToObservers(MVPbkContactViewBase& aView,
                          RPointerArray<MVPbkContactViewObserver>& iObservers,
                          NotifyFunc aNotifyFunc)
    {
    const TInt count = iObservers.Count();
    for (TInt i = count - 1; i >= 0; --i)
        {
        MVPbkContactViewObserver* observer = iObservers[i];
        (observer->*aNotifyFunc)(aView);
        }
    }

// ---------------------------------------------------------------------------
// Event sending functions for 3 parameters
// ---------------------------------------------------------------------------
// 
template <class NotifyFunc, class ParamType1, class ParamType2>
void SendEventToObservers(MVPbkContactViewBase& aView,
                          RPointerArray<MVPbkContactViewObserver>& iObservers,
                          NotifyFunc aNotifyFunc,
                          ParamType1 aParam1,
                          const ParamType2& aParam2)
    {
    const TInt count = iObservers.Count();
    for (TInt i = count - 1; i >= 0; --i)
        {
        MVPbkContactViewObserver* observer = iObservers[i];
        (observer->*aNotifyFunc)(aView, aParam1, aParam2);
        }
    }

} // namespace


// ---------------------------------------------------------------------------
// CVPbkFindView::CVPbkFindView
// ---------------------------------------------------------------------------
// 
CVPbkFindView::CVPbkFindView(
        MVPbkContactViewBase& aBaseView,        
        const MVPbkFieldTypeList* aMasterFieldTypeList ) :
    iBaseView(aBaseView),
    iMasterFieldTypeList( aMasterFieldTypeList ),
    iObservers( KObserverArrayGranularity )
    {
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::ConstructL
// ---------------------------------------------------------------------------
// 
inline void CVPbkFindView::ConstructL( 
        const MDesCArray& aFindWords,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ConstructL(0x%x)"), this);
    
    iAsyncOperation = new(ELeave) VPbkEngUtils::CVPbkAsyncOperation;
    
    iFindStrings = new(ELeave)CDesCArrayFlat( KGranularity );
    SetFindStringsL( aFindWords );
    
    SetAlwaysIncludedContactsL( aAlwaysIncludedContacts );

    // Create contact find policy
    iContactFindPolicy = CVPbkContactFindPolicy::NewL();
    
    CVPbkContactNameConstructionPolicy::TParam param( *iMasterFieldTypeList );
    iNameConstructionPolicy =
        CVPbkContactNameConstructionPolicy::NewL( param );
        
    //Create field type list
    iFieldTypeRefsList = CVPbkFieldTypeRefsList::NewL();        
            
    iBaseView.AddObserverL(*this);

    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ConstructL(0x%x) end"), this);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::NewL
// ---------------------------------------------------------------------------
// 
CVPbkFindView* CVPbkFindView::NewLC(
        MVPbkContactViewBase& aBaseView,
        MVPbkContactViewObserver& aObserver,
        const MDesCArray& aFindWords,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts,
        const MVPbkFieldTypeList& aMasterFieldTypeList )
    {
    CVPbkFindView* self = 
        new(ELeave) CVPbkFindView(
            aBaseView, &aMasterFieldTypeList );
    CleanupStack::PushL(self);
    self->ConstructL( aFindWords, aAlwaysIncludedContacts );
    self->AddObserverL(aObserver);
    return self;
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::~CVPbkFindView
// ---------------------------------------------------------------------------
// 
CVPbkFindView::~CVPbkFindView()
    {    
    iContactMapping.Reset();   
    iBaseView.RemoveObserver(*this); 
    iObservers.Close();
    delete iFindStrings;
    delete iFieldTypeRefsList;
    delete iNameConstructionPolicy;
    delete iContactFindPolicy;
    delete iAsyncOperation;        
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::Type
// ---------------------------------------------------------------------------
// 
TVPbkContactViewType CVPbkFindView::Type() const
    {
    return iBaseView.Type();
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::ChangeSortOrderL
// ---------------------------------------------------------------------------
// 
void CVPbkFindView::ChangeSortOrderL(
        const MVPbkFieldTypeList& aSortOrder )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ChangeSortOrderL(0x%x)"), &aSortOrder);

    iBaseView.ChangeSortOrderL(aSortOrder);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::SortOrder
// ---------------------------------------------------------------------------
// 
const MVPbkFieldTypeList& CVPbkFindView::SortOrder() const
    {
    return iBaseView.SortOrder();
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::RefreshL
// ---------------------------------------------------------------------------
// 
void CVPbkFindView::RefreshL()
    {
    iBaseView.RefreshL();
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::ContactCountL
// ---------------------------------------------------------------------------
// 
TInt CVPbkFindView::ContactCountL() const
    {
    return iContactMapping.Count();
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::ContactAtL
// ---------------------------------------------------------------------------
// 
const MVPbkViewContact& CVPbkFindView::ContactAtL(
        TInt aIndex ) const
    {
    __ASSERT_ALWAYS( aIndex >= 0,
        VPbkError::Panic( VPbkError::EInvalidContactIndex ) );
    if ( aIndex >= iContactMapping.Count() )
        {
        User::Leave( KErrArgument );
        }

    return iBaseView.ContactAtL(iContactMapping[aIndex]);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::CreateLinkLC
// ---------------------------------------------------------------------------
// 
MVPbkContactLink* CVPbkFindView::CreateLinkLC( TInt aIndex ) const
    {
    __ASSERT_ALWAYS( aIndex >= 0,
        VPbkError::Panic( VPbkError::EInvalidContactIndex ) );
    if ( aIndex >= iContactMapping.Count() )
        {
        User::Leave( KErrArgument );
        }

    return iBaseView.ContactAtL(iContactMapping[aIndex]).CreateLinkLC();
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::IndexOfLinkL
// ---------------------------------------------------------------------------
// 
TInt CVPbkFindView::IndexOfLinkL(
        const MVPbkContactLink& aContactLink ) const
    {
    TInt baseIndex = iBaseView.IndexOfLinkL(aContactLink);
    return iContactMapping.Find(baseIndex);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::AddObserverL
// ---------------------------------------------------------------------------
// 
void CVPbkFindView::AddObserverL( 
        MVPbkContactViewObserver& aObserver )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::AddObserverL(0x%x)"), &aObserver);

    VPbkEngUtils::MAsyncCallback* notifyObserver =
        VPbkEngUtils::CreateAsyncCallbackLC(*this,
                &CVPbkFindView::DoAddObserverL,
                &CVPbkFindView::AddObserverError,
                aObserver);
    iAsyncOperation->CallbackL(notifyObserver);
    CleanupStack::Pop(notifyObserver);

    /// Insert to first position because event are send in reverse order.
    /// Last inserted gets notifcation last.
    iObservers.InsertL( &aObserver, 0 );
    
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::AddObserverL(0x%x) end"), &aObserver);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::DoAddObserverL
// ---------------------------------------------------------------------------
// 
void CVPbkFindView::DoAddObserverL(
        MVPbkContactViewObserver& aObserver )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::DoAddObserverL(0x%x)"), &aObserver);

    if ( iObservers.Find( &aObserver ) != KErrNotFound )
        {
        if ( iIsReady )
            {
            VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
            ("CVPbkFindView::DoAddObserverL(0x%x) contact view ready"), 
                &aObserver);

            // If this view is ready and there was no error,
            // tell it to the observer
            aObserver.ContactViewReady( *this );
            }
        else
            {
            aObserver.ContactViewUnavailable( *this );
            }    
        }
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::DoAddObserverL(0x%x) end"), &aObserver);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::AddObserverError
// ---------------------------------------------------------------------------
// 
void CVPbkFindView::AddObserverError(
        MVPbkContactViewObserver& aObserver, TInt aError )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::AddObserverError(0x%x, %d)"), &aObserver, aError);

    aObserver.ContactViewError(*this, aError, EFalse);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::RemoveObserver
// ---------------------------------------------------------------------------
// 
void CVPbkFindView::RemoveObserver(
        MVPbkContactViewObserver& aObserver )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::RemoveObserverError(0x%x)"), &aObserver);

    const TInt index = iObservers.Find( &aObserver );
    if (index != KErrNotFound)
        {
        iObservers.Remove(index);
        }
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::MatchContactStore
// ---------------------------------------------------------------------------
// 
TBool CVPbkFindView::MatchContactStore(
        const TDesC& aContactStoreUri ) const
    {
    return iBaseView.MatchContactStore(aContactStoreUri);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::MatchContactStoreDomain
// ---------------------------------------------------------------------------
// 
TBool CVPbkFindView::MatchContactStoreDomain(
        const TDesC& aContactStoreDomain ) const
    {
    return iBaseView.MatchContactStoreDomain(aContactStoreDomain);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::CreateBookmarkLC
// ---------------------------------------------------------------------------
// 
MVPbkContactBookmark* CVPbkFindView::CreateBookmarkLC(
        TInt aIndex ) const
    {
    return iBaseView.ContactAtL(iContactMapping[aIndex]).CreateBookmarkLC();
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::IndexOfBookmarkL
// ---------------------------------------------------------------------------
// 
TInt CVPbkFindView::IndexOfBookmarkL(
        const MVPbkContactBookmark& aContactBookmark ) const
    {
    TInt baseIndex = iBaseView.IndexOfBookmarkL(aContactBookmark);
    return iContactMapping.Find(baseIndex);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::ViewFiltering
// ---------------------------------------------------------------------------
//         
MVPbkContactViewFiltering* CVPbkFindView::ViewFiltering()
    {    
    return this;
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::CreateFilteredViewLC
// ---------------------------------------------------------------------------
//    
MVPbkContactViewBase* CVPbkFindView::CreateFilteredViewLC( 
        MVPbkContactViewObserver& aObserver,
        const MDesCArray& aFindWords,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::CreateFilteredViewLC(0x%x)"), this);
        
    return CVPbkFindView::NewLC( *this, 
                                 aObserver, 
                                 aFindWords, 
                                 aAlwaysIncludedContacts,
                                 *iMasterFieldTypeList );    
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::UpdateFilterL
// ---------------------------------------------------------------------------
//     
void CVPbkFindView::UpdateFilterL( 
        const MDesCArray& aFindWords,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::UpdateFilterL(0x%x)"), this);
        
    SetFindStringsL( aFindWords );
    SetAlwaysIncludedContactsL( aAlwaysIncludedContacts );
    
    // Remove first if this called second time when updating.
    iBaseView.RemoveObserver( *this );
    // This will cause an asynchrnous view event to this view from the
    // parent view.
    iBaseView.AddObserverL( *this );
    }
    
// ---------------------------------------------------------------------------
// CVPbkFindView::BuildViewMappingL
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::BuildViewMappingL()
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::BuildViewMappingL(0x%x)"), this);

    iContactMapping.Reset();

    // Append always included contact
    if ( iAlwaysIncludedContacts )
        {
        const TInt alwaysIncluded( iAlwaysIncludedContacts->Count() );
        for ( TInt i(0); i < alwaysIncluded; ++i )
            {
            TInt index( 
                iBaseView.IndexOfBookmarkL( 
                    iAlwaysIncludedContacts->At( i ) ) );
            if ( index > KErrNotFound )
                {
                User::LeaveIfError( iContactMapping.InsertInOrder(index) );
                }        
            }    
        }
    
    // Append matched contacts    
    const TInt countContacts = iBaseView.ContactCountL();
    for (TInt i(0); i < countContacts; ++i)
        {
        const MVPbkViewContact& contact = iBaseView.ContactAtL( i );
        
        TBool match( ETrue );
        const TInt countStrings( iFindStrings->MdcaCount() );
        for ( TInt j(0); j < countStrings; ++j )
            {
            if ( !ContactMatchRefineL( contact, iFindStrings->MdcaPoint( j ) ) )
                {                
                match = EFalse;                
                }            
            }                

        if ( match && iContactMapping.FindInOrder( i ) == KErrNotFound ) 
            {                    
            User::LeaveIfError( iContactMapping.InsertInOrder( i ) );
            }            
        }

    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::BuildViewMappingL(0x%x) end"), this);
    }

// ---------------------------------------------------------------------------
// CVPbkFindView::HandleBuildViewMapping
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::HandleBuildViewMapping()
    {
    TRAPD( res, BuildViewMappingL() );
    if ( res == KErrNone )
        {
        // Mapping was succesfully built. Send ready event
        SendViewEventToObservers( 
            &MVPbkContactViewObserver::ContactViewReady );
        }
    else
        {
        // Building the mapping failed. Leave mappings as they are and
        // send error
        SendViewErrorEventToObservers( res, EFalse );
        }
    }
    
// ---------------------------------------------------------------------------
// From class MVPbkContactViewBase.
// CVPbkFindView::ContactViewReady
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::ContactViewReady( MVPbkContactViewBase& aView )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ContactViewReady(0x%x)"), &aView);

    iIsReady = ETrue;
    HandleBuildViewMapping();
    
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ContactViewReady(0x%x) end"), &aView);
    }

// ---------------------------------------------------------------------------
// From class MVPbkContactViewBase.
// CVPbkFindView::ContactViewUnavailable
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::ContactViewUnavailable( 
        MVPbkContactViewBase& aView )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ContactViewUnavailable(0x%x)"), &aView);

    iIsReady = EFalse;
    SendViewEventToObservers(
        &MVPbkContactViewObserver::ContactViewUnavailable );

    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ContactViewUnavailable(0x%x)"), &aView);
    }

// ---------------------------------------------------------------------------
// From class MVPbkContactViewBase.
// CVPbkFindView::ContactViewError
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::ContactViewError( MVPbkContactViewBase& aView, 
        TInt aError, TBool aErrorNotified )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ContactViewError(0x%x)"), &aView);

    iIsReady = EFalse;
    SendViewErrorEventToObservers( aError, aErrorNotified );

    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING
        ("CVPbkFindView::ContactViewError(0x%x)"), &aView);
    }

// ---------------------------------------------------------------------------
// From class MVPbkContactViewBase.
// CVPbkFindView::ContactAddedToView
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::ContactAddedToView
        (MVPbkContactViewBase& /*aView*/, TInt aIndex,
        const MVPbkContactLink& aContactLink)
    {
    // To keep indexes in iContactMapping up to date rebuild mappings.
    TRAPD( res, BuildViewMappingL() );
    if ( res == KErrNone )
        {
        // Do a binary search to mapping
        TInt index = iContactMapping.FindInOrder( aIndex );
        if ( index != KErrNotFound )
            {
            // Mapping was succesfully updated and the new contact
            // belongs to filter.
            SendEventToObservers( *this, iObservers,
                &MVPbkContactViewObserver::ContactAddedToView, index, 
                aContactLink );
            }
        }
    else
        {
        SendViewErrorEventToObservers( res, EFalse );
        }
    }

// ---------------------------------------------------------------------------
// From class MVPbkContactViewBase.
// CVPbkFindView::ContactRemovedFromView
// ---------------------------------------------------------------------------
//         
void CVPbkFindView::ContactRemovedFromView(
        MVPbkContactViewBase& /*aView*/, TInt aIndex, 
        const MVPbkContactLink& aContactLink)
    {
    // Do a binary search to mapping
    TInt index = iContactMapping.FindInOrder( aIndex );
    
    // To keep indexes in iContactMapping up to date rebuild mappings.
    TRAPD( res, BuildViewMappingL() );
    if ( res == KErrNone )
        {
        if ( index != KErrNotFound )
            {
            // Mapping was succesfully updated and the new contact
            // belongs to filter.
            SendEventToObservers( *this, iObservers,
                &MVPbkContactViewObserver::ContactRemovedFromView, index, 
                aContactLink );
            }
        }
    else
        {
        SendViewErrorEventToObservers( res, EFalse );
        }
    }
    
// --------------------------------------------------------------------------
// CVPbkFindView::ContactMatchRefineL
// --------------------------------------------------------------------------
//    
TBool CVPbkFindView::ContactMatchRefineL(         
        const MVPbkViewContact& aViewContact,
        TPtrC aFindWord )
    {
    TBool match( EFalse );
    iFieldTypeRefsList->Reset(); 
    
    // Create iterator                        
    MVPbkBaseContactFieldIterator* iterator = 
        iNameConstructionPolicy->NameConstructionFieldsLC( 
            aViewContact.Fields(),
            *iFieldTypeRefsList );                            
             
    // Loop iterator
    while ( iterator->HasNext() )
        {                    
        const MVPbkBaseContactField* field = iterator->Next();
        
        // Check field's type
        if ( field->FieldData().DataType() == EVPbkFieldStorageTypeText )
            {                    
            const MVPbkContactFieldTextData& data = 
                MVPbkContactFieldTextData::Cast( field->FieldData() );
                
            // Match refine
            if ( iContactFindPolicy->MatchRefineL( data.Text(), aFindWord ) )
                {
                match = ETrue;
                break;
                }                
            }
        }           
    CleanupStack::PopAndDestroy( 1 ); //iterator    
    
    return match;    
    }    

// --------------------------------------------------------------------------
// CVPbkFindView::SetFindStringsL
// --------------------------------------------------------------------------
// 
void CVPbkFindView::SetFindStringsL( const MDesCArray& aFindWords )
    {
    iFindStrings->Reset();
    
    const TInt count( aFindWords.MdcaCount() );
    for ( TInt i(0); i < count; ++i )
        {
        iFindStrings->AppendL( aFindWords.MdcaPoint( i ) );
        }
    }

// --------------------------------------------------------------------------
// CVPbkFindView::SetAlwaysIncludedContactsL
// --------------------------------------------------------------------------
// 
void CVPbkFindView::SetAlwaysIncludedContactsL( 
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts )
    {
    iAlwaysIncludedContacts = aAlwaysIncludedContacts;
    }

// --------------------------------------------------------------------------
// CVPbkFindView::SendViewEventToObservers
// --------------------------------------------------------------------------
// 
void CVPbkFindView::SendViewEventToObservers( 
    void( MVPbkContactViewObserver::* aObserverFunc)( MVPbkContactViewBase& ) )
    {
    // Cancel async operation to avoid duplicate event sending to observers
    iAsyncOperation->Purge();
    SendEventToObservers( *this, iObservers, aObserverFunc );
    }

// --------------------------------------------------------------------------
// CVPbkFindView::SendViewErrorEventToObservers
// --------------------------------------------------------------------------
// 
void CVPbkFindView::SendViewErrorEventToObservers( TInt aError, TBool 
        aErrorNotified )
    {
    // Cancel async operation to avoid duplicate event sending to observers
    iAsyncOperation->Purge();
    SendEventToObservers( *this, iObservers,
        &MVPbkContactViewObserver::ContactViewError, 
        aError, aErrorNotified );
    }
// End of File
