/*
* Copyright (c) 2002-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:  Sim store filtered contact view implementation.
*
*/


#include "CFindView.h"

// From VPbkSimStore
#include "CContactStore.h"
#include "CContactView.h"
#include "CViewContact.h"
#include "CContactLink.h"

// VPbkEng
#include <MVPbkSimStoreOperation.h>
#include <MVPbkSimCntView.h>
#include <CVPbkSimContact.h>
#include <RVPbkStreamedIntArray.h>
#include <MVPbkContactViewObserver.h>
#include <MVPbkSimCntStore.h>
#include <MVPbkContactLink.h>
#include <CVPbkContactFindPolicy.h>
#include <CVPbkContactNameConstructionPolicy.h>
#include <CVPbkContactViewSortPolicy.h>
#include <MVPbkContactBookmarkCollection.h>
#include <RVPbkStreamedIntArray.h>

// System includes
#include <cntviewbase.h>
#include <featmgr.h>

// Debugging headers
#include <VPbkProfile.h>
#include <VPbkDebug.h>

namespace VPbkSimStore {

// --------------------------------------------------------------------------
// CFindView::CFindView
// --------------------------------------------------------------------------
//
inline CFindView::CFindView( CContactView& aAllContactsView ) :        
        CFindViewBase( aAllContactsView, aAllContactsView, ETrue  )
    {
    }

// --------------------------------------------------------------------------
// CFindView::~CFindView
// --------------------------------------------------------------------------
//
CFindView::~CFindView()
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING(
        "VPbkSimStore:CFindView(%x)::~CFindView"), this );
         
    delete iFindOperation;
    delete iFindPolicy;
    iAlwaysIncluded.Close();
    iSIMMatchedContacts.Close();
    }

// --------------------------------------------------------------------------
// CFindView::ConstructL
// --------------------------------------------------------------------------
//
void CFindView::ConstructL( const MDesCArray& aFindStrings,
        MVPbkContactViewObserver& aExternalViewObserver,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts,
        RFs& aRFs )
    {
    CVPbkContactFindPolicy::TParam findPolicyParams(
        iAllContactsView.Store().MasterFieldTypeList(), aRFs );
    iFindPolicy = CVPbkContactFindPolicy::NewL( findPolicyParams );
    BaseConstructL( aExternalViewObserver, aFindStrings, 
        *iFindPolicy );
    
    SetAlwaysIncludedContactsL( aAlwaysIncludedContacts );
    }
    
// --------------------------------------------------------------------------
// CFindView::NewLC
// --------------------------------------------------------------------------
//
CFindView* CFindView::NewLC(      
        const MDesCArray& aFindStrings,
        CContactView& aAllContactsView,
        MVPbkContactViewObserver& aExternalViewObserver,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts,
        RFs& aRFs )
    {
    CFindView* self = new ( ELeave ) CFindView( aAllContactsView );
    CleanupStack::PushL( self );
    self->ConstructL( aFindStrings, aExternalViewObserver,
        aAlwaysIncludedContacts, aRFs );
    CleanupStack::Check( self );
    return self;
    }

// --------------------------------------------------------------------------
// CFindView::SetAlwaysIncludedContactsL
// --------------------------------------------------------------------------
//    
void CFindView::SetAlwaysIncludedContactsL( 
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts )
    {
    iAlwaysIncluded.Reset();

    if ( aAlwaysIncludedContacts )
        {
        MVPbkContactStore& store = ParentObject().ContactStore();
        const TInt count = aAlwaysIncludedContacts->Count();
        for ( TInt i = 0; i < count; ++i )
            {
            const CContactLink* bookmark =            
                dynamic_cast<const CContactLink*>( 
                    &aAlwaysIncludedContacts->At( i ) );
            // If bookmark was from VPbkCntModel and if it's from same store
            // as this view then it's added to array.
            if ( bookmark &&
                 &bookmark->ContactStore() == &store )
                {
                iAlwaysIncluded.AppendL( bookmark->SimIndex() );
                }
            }
        }
    }

// --------------------------------------------------------------------------
// CFindView::ContactViewReady
// --------------------------------------------------------------------------
//    
void CFindView::ContactViewReady( MVPbkContactViewBase& /*aView*/ )
    {
    // CFindView is only interested in ContactViewReadyForFiltering that
    // will start an asynchronous native match.
    }
    
// --------------------------------------------------------------------------
// CFindView::ContactViewReadyForFiltering
// --------------------------------------------------------------------------
//    
void CFindView::ContactViewReadyForFiltering(
        MParentViewForFiltering& aView )
    {
    // Cancel existing match if aready active.
    delete iFindOperation;
    iFindOperation = NULL;
    
    // Parent view is ready. Start asynchronous find from native SIM view.
    TRAPD( error,
        {
        iFindOperation = 
            iAllContactsView.NativeView().ContactMatchingPrefixL(
                FindStrings(), *this );
        });
    
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING(
        "VPbkSimStore:CFindView(%x)::ContactViewReadyForFiltering:\
        findOp(%x)"), this, iFindOperation ); 
        
    if ( error != KErrNone )
        {
        ContactViewError( aView, error, EFalse );
        }
    }

// --------------------------------------------------------------------------
// CFindView::MatchL
// --------------------------------------------------------------------------
//
void CFindView::MatchL( RPointerArray<MVPbkSimContact>& aMatchedContacts )
    {
    if (iAlwaysIncluded.Count() == 0)
        {
        // No always included contacts. The match can be done using
        // only the SIM view matched contacts.
        CViewContact* viewContact = CViewContact::NewL( iAllContactsView, 
            SortOrder() );
        CleanupStack::PushL(viewContact);
        MVPbkSimCntStore& simStore = iAllContactsView.Store().NativeStore();
        const TInt count = iSIMMatchedContacts.Count();
        for (TInt i = 0; i < count; ++i)
            {
            const TDesC8* cntBuf = simStore.AtL( iSIMMatchedContacts[i] );
            if ( cntBuf )
                {
                CVPbkSimContact* cnt = 
                    CVPbkSimContact::NewL( *cntBuf, simStore );
                CleanupStack::PushL( cnt );
                viewContact->SetSimContactL( *cnt );
                if ( IsMatchL( *viewContact ) )
                    {
                    // Keep the order of the contacts same and move contact
                    // from iContactsModelMatchContacts to aMatchedContacts
                    aMatchedContacts.AppendL( cnt );
                    CleanupStack::Pop( cnt );
                    }
                else
                    {
                    CleanupStack::PopAndDestroy( cnt );
                    }
                }
            }   
        CleanupStack::PopAndDestroy( viewContact );
        }
    else
        {
        // Do it slowly by looping all the parent view contacts.
        const TInt contactCount = iParentView.ContactCountL();
        for ( TInt i = 0; i < contactCount; ++i )
            {
            // iParentView is always VPbkCntModel view and the contacts type
            // is CViewContact
            const CViewContact& candidate = static_cast<const CViewContact&>(
                iParentView.ContactAtL( i ) );
            MatchContactL( candidate, aMatchedContacts );
            }
        }
    
    // Free the memory
    iSIMMatchedContacts.Reset(); 
    }
        
// --------------------------------------------------------------------------
// CFindView::ViewFindCompleted
// --------------------------------------------------------------------------
//      
void CFindView::ViewFindCompleted( 
        MVPbkSimCntView& aSimCntView,
        const RVPbkStreamedIntArray& aSimIndexArray )
    {
    TRAPD( error, ViewFindCompletedL( aSimCntView, aSimIndexArray ) );
    VPBK_PROFILE_END(VPbkProfile::ESimStoreFind);    
    
    if ( error != KErrNone )
        {
        ContactViewError( *this, error, EFalse );
        }
    }

// --------------------------------------------------------------------------
// CFindView::ViewFindError
// --------------------------------------------------------------------------
//                  
void CFindView::ViewFindError( 
        MVPbkSimCntView& /*aSimCntView*/, 
        TInt aError )
    {
    ContactViewError( *this, aError, EFalse );
    }

// --------------------------------------------------------------------------
// CFindView::UpdateFilterL
// --------------------------------------------------------------------------
//
void CFindView::UpdateFilterL( 
        const MDesCArray& aFindWords,
        const MVPbkContactBookmarkCollection* aAlwaysIncludedContacts )
    {
    SetFindStringsL( aFindWords );
    SetAlwaysIncludedContactsL( aAlwaysIncludedContacts );
    ActivateContactMatchL();
    }

// --------------------------------------------------------------------------
// CFindView::IsNativeMatchingRequestActive
// --------------------------------------------------------------------------
//
TBool CFindView::IsNativeMatchingRequestActive()
    {
    if ( iFindOperation )
        {
        return ETrue;
        }
    return EFalse;
    }
    
// --------------------------------------------------------------------------
// CFindView::IsContactAlwaysIncluded
// --------------------------------------------------------------------------
//
TBool CFindView::IsContactAlwaysIncluded( const CViewContact& aContact ) const
    {
    return iAlwaysIncluded.Find( aContact.SimIndex() ) != KErrNotFound;
    }
    
// --------------------------------------------------------------------------
// CFindView::DoContactAddedToViewL
// --------------------------------------------------------------------------
//
void CFindView::DoContactAddedToViewL(
        MVPbkContactViewBase& aView, 
        TInt aIndex, 
        const MVPbkContactLink& /*aContactLink*/,
        RPointerArray<MVPbkSimContact>& aMatchedContacts )
    {
    if ( &iParentView == &aView )
        {
        const CViewContact& viewContact = static_cast<const CViewContact&>(
            iParentView.ContactAtL( aIndex ) );
        if ( IsMatchL( viewContact ) )
            {
            MVPbkSimCntStore& simStore = 
                iAllContactsView.Store().NativeStore();
            const TDesC8* cntBuf = simStore.AtL( viewContact.SimIndex() );
            if ( cntBuf )
                {
                CVPbkSimContact* cnt = 
                    CVPbkSimContact::NewL( *cntBuf, simStore );
                CleanupStack::PushL( cnt );
                
                // We have to insert the new contact to the correct
                // location. VPbk sort policy is used for that.
                CVPbkContactViewSortPolicy::TParam param(
                    iAllContactsView.Store().MasterFieldTypeList(), SortOrder() );
                CVPbkContactViewSortPolicy* sortPolicy = 
                    CVPbkContactViewSortPolicy::NewL( param );
                CleanupStack::PushL( sortPolicy );
                sortPolicy->SortStartL();
                CViewContact* current = 
                    CViewContact::NewL( iAllContactsView, SortOrder() );
                CleanupStack::PushL( current );
                
                const TInt count = aMatchedContacts.Count();
                TInt index = KErrNotFound;
                for ( TInt i = 0; i < count && index == KErrNotFound; ++i )
                    {
                    current->SetSimContactL( *aMatchedContacts[i] );
                    TInt res = 
                        sortPolicy->CompareContacts( viewContact, *current );
                    // CompareContacts returns -1, 0 or 1.
                    if ( res < 0 )
                        {
                        index = i;
                        }
                    }
                sortPolicy->SortCompleted();
                CleanupStack::PopAndDestroy( 2, sortPolicy ); // current
                if ( index != KErrNotFound )
                    {
                    // Contact is inserted to matched contacts list
                    aMatchedContacts.InsertL( cnt, index );
                    }
                else
                    {
                    // List is empty so append contact to it
                    aMatchedContacts.AppendL( cnt );
                    }
                CleanupStack::Pop( cnt );
                }
            }
        }
    }

// --------------------------------------------------------------------------
// CFindView::ViewFindCompletedL
// --------------------------------------------------------------------------
//  
void CFindView::ViewFindCompletedL( 
        MVPbkSimCntView& /*aSimCntView*/,
        const RVPbkStreamedIntArray& aSimIndexArray )
    {
    VPBK_DEBUG_PRINT(VPBK_DEBUG_STRING(
        "VPbkSimStore:CFindView(%x)::ViewFindCompletedL:found %d contacts"),
         this, aSimIndexArray.Count() );
         
    // Native SIM view find is ready.
    
    // Clean the table
    iSIMMatchedContacts.Reset();
    
    // Copy matched indexes
    const TInt count( aSimIndexArray.Count() );
    for ( TInt i(0); i < count; ++i )
        {
        iSIMMatchedContacts.AppendL( aSimIndexArray[i] );
        }
    
    // The operation is not needed after indexes have been copied
    delete iFindOperation;
    iFindOperation = NULL;
    
    // Call base class matching function. It will send internal observer
    // event that this view is ready for filtering.
    MatchContactsL();
    // Send view ready event to both internal&external observers.
    SendViewStateEventToObservers();
    }

// --------------------------------------------------------------------------
// CFindView::MatchContactL
// --------------------------------------------------------------------------
//  
void CFindView::MatchContactL( const CViewContact& aViewContact,
            RPointerArray<MVPbkSimContact>& aMatchedContacts )
    {
    // aContact matches if it's one of the always included contacts OR
    // (if it's one of Contacts Model matched contacts AND it also
    // passes our own match).
    TInt matchArrayIndex = KErrNotFound;
    TBool matched = EFalse;
    if ( IsContactAlwaysIncluded( aViewContact ) )
        {
        // Remove from match array to save memory
        RemoveFromMatchArrayIfFound( aViewContact );
        matched = ETrue;
        }
    else if ( IsSIMMatchL( aViewContact, matchArrayIndex ) )
        {
        // Remove from match array to save memory
        iSIMMatchedContacts.Remove( matchArrayIndex );
        iSIMMatchedContacts.Compress();
        
        if ( IsMatchL( aViewContact ) )
            {
            matched = ETrue;
            }
        }
    
    if ( matched )
        {
        // Contact matched.
        // CFindView owns its contacts so create a copy
        MVPbkSimCntStore& simStore = iAllContactsView.Store().NativeStore();
        const TDesC8* cntBuf = simStore.AtL( aViewContact.SimIndex() );
        if ( cntBuf )
            {
            CVPbkSimContact* cnt = 
                CVPbkSimContact::NewL( *cntBuf, simStore );
            CleanupStack::PushL( cnt );
            aMatchedContacts.AppendL( cnt );
            CleanupStack::Pop( cnt );
            }
        }
    }

// --------------------------------------------------------------------------
// CFindView::RemoveFromMatchArrayIfFound
// --------------------------------------------------------------------------
//  
void CFindView::RemoveFromMatchArrayIfFound( const CViewContact& aViewContact )
    {
    TInt index = FindFromMatchArray( aViewContact );
    if ( index != KErrNotFound )
        {
        iSIMMatchedContacts.Remove( index );
        iSIMMatchedContacts.Compress();
        }
    }

// --------------------------------------------------------------------------
// CFindView::IsSIMMatchL
// --------------------------------------------------------------------------
//  
TBool CFindView::IsSIMMatchL( const CViewContact& aContact,
        TInt& aMatchArrayIndex ) const
    {
    aMatchArrayIndex = FindFromMatchArray( aContact );
    return aMatchArrayIndex != KErrNotFound;
    }

// --------------------------------------------------------------------------
// CFindView::FindFromMatchArray
// --------------------------------------------------------------------------
//  
TInt CFindView::FindFromMatchArray( const CViewContact& aContact ) const
    {
    return iSIMMatchedContacts.Find( aContact.SimIndex() );
    }
} // namespace VPbkSimStore 
// End of File
