/*
* 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 "CRefineView.h"

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

// From Virtual Phonebook
#include <MVPbkSimContact.h>
#include <CVPbkSimCntField.h>
#include <CVPbkContactFindPolicy.h>
#include <MVPbkContactLink.h>
#include <CVPbkContactViewSortPolicy.h>

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

// Debugging headers
#include <VPbkProfile.h>

namespace VPbkSimStore {

inline CRefineView::CRefineView( MParentViewForFiltering& aParentView,
        CContactView& aAllContactsView,
        MAlwaysIncludedContacts& aAlwaysIncluded ) 
        :   CFindViewBase( aParentView, aAllContactsView, EFalse ),
            iAlwaysIncluded( aAlwaysIncluded )
    {
    }

// --------------------------------------------------------------------------
// CRefineView::~CRefineView
// --------------------------------------------------------------------------
//
CRefineView::~CRefineView()
    {
    delete iIdleUpdate;
    }

// --------------------------------------------------------------------------
// CRefineView::NewLC
// --------------------------------------------------------------------------
//
CRefineView* CRefineView::NewLC(
        const MDesCArray& aFindStrings,
        MParentViewForFiltering& aParentView,
        CContactView& aAllContactsView,
        MVPbkContactViewObserver& aExternalViewObserver,
        MVPbkContactFindPolicy& aContactFindPolicy,
        MAlwaysIncludedContacts& aAlwaysIncluded )
    {
    CRefineView* self =
        new ( ELeave ) CRefineView( aParentView, aAllContactsView, 
            aAlwaysIncluded );
    CleanupStack::PushL( self );
    self->BaseConstructL( aExternalViewObserver, aFindStrings, 
        aContactFindPolicy  );
    return self;
    }

// --------------------------------------------------------------------------
// CRefineView::ContactViewReady
// --------------------------------------------------------------------------
//
void CRefineView::ContactViewReady( MVPbkContactViewBase& /*aView*/ )
    {
    // Send view ready event to all observers
    SendViewStateEventToObservers();
    }
    
// --------------------------------------------------------------------------
// CRefineView::ContactViewReadyForFiltering
// --------------------------------------------------------------------------
//
void CRefineView::ContactViewReadyForFiltering( 
        MParentViewForFiltering& /*aView*/ )
    {
    // Cancel update so that there won't be double matching in case
    // the parent view is updating itself and also this view is updating
    // itself.
    if ( iIdleUpdate )
        {
        iIdleUpdate->Cancel();
        }
        
    // Parent view is ready -> Call base class function to start
    // matching. MatchContactsL will also call internal observer interface
    // to send internal view events to higher level filtered views. This
    // way all the CRefineViews are updated before any external observer
    // gets the view event. 
    // External observers will get view event from this view when the
    // CFindView  (CFindView::ViewFindCompletedL) calls 
    // SendViewStateEventToObservers.
    TRAPD( res, MatchContactsL() );
    if ( res != KErrNone )
        {
        ContactViewError( *this, res, EFalse );
        }
    }
    
// --------------------------------------------------------------------------
// CRefineView::MatchL
// --------------------------------------------------------------------------
//
void CRefineView::MatchL( RPointerArray<MVPbkSimContact>& aMatchedContacts )
    {
    const TInt contactCount = iParentView.ContactCountL();
    for ( TInt i = 0; i < contactCount; ++i )
        {
        // iParentView is always VPbkSimStore view and the contacts type
        // is CViewContact
        const CViewContact& candidate = static_cast<const CViewContact&>(
            iParentView.ContactAtL( i ) );
        if ( IsContactAlwaysIncluded( candidate ) || IsMatchL( candidate ) )
            {
            // iParentView is always CFindView or CRefineView. The contacts type
            // is CViewContact. It can be safely casted. Const cast be done
            // because aContact is actually owned by CFindView.
            MVPbkSimContact* nativeContact = 
                const_cast<MVPbkSimContact*>( candidate.NativeContact() );
            aMatchedContacts.AppendL( nativeContact );
            }
        }    
    }

// --------------------------------------------------------------------------
// CRefineView::DoContactAddedToViewL
// --------------------------------------------------------------------------
//
void CRefineView::DoContactAddedToViewL( MVPbkContactViewBase& aView,
        TInt aIndex, const MVPbkContactLink& /*aContactLink*/,
        RPointerArray<MVPbkSimContact>& aMatchedContacts )
    {
    if ( &iParentView == &aView && 
         IsMatchL( iParentView.ContactAtL( aIndex )))
        {
        // iParentView is always CFindView or CRefineView the contacts type
        // is CViewContact. It can be safely casted.
        const CViewContact& contact = static_cast<const CViewContact&>(
            iParentView.ContactAtL( aIndex ) );
        
        // 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();
        // Create a view contact instance for comparing contacts.
        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( contact, *current );
            if ( res < 0 )
                {
                index = i;
                }
            }
        sortPolicy->SortCompleted();
        CleanupStack::PopAndDestroy( 2, sortPolicy );
        
        // Const cast be done
        // because nativeContact is actually owned by CFindView.
        MVPbkSimContact* nativeContact = 
            const_cast<MVPbkSimContact*>( contact.NativeContact() );
        if ( index != KErrNotFound )
            {
            aMatchedContacts.InsertL( nativeContact, index );
            }
        else
            {
            aMatchedContacts.AppendL( nativeContact );
            }
        }    
    }

// --------------------------------------------------------------------------
// CRefineView::UpdateFilterL
// --------------------------------------------------------------------------
//
void CRefineView::UpdateFilterL( 
        const MDesCArray& aFindWords,
        const MVPbkContactBookmarkCollection* /*aAlwaysIncludedContacts*/ )
    {
    if ( !iIdleUpdate )
        {
        iIdleUpdate = CIdle::NewL( CActive::EPriorityStandard );
        }
        
    // NOTE:
    // aAlwaysIncludedContacts is not used by CRefineView. It uses 
    // MAlwaysIncludedContacts interface for that purpose.
    SetFindStringsL( aFindWords );
    // Start idle update.
    if ( !iIdleUpdate->IsActive() )
        {
        iIdleUpdate->Start( TCallBack( StartUpdateCallback, this ) ); 
        }
    }

// --------------------------------------------------------------------------
// CRefineView::IsNativeMatchingRequestActive
// --------------------------------------------------------------------------
//
TBool CRefineView::IsNativeMatchingRequestActive()
    {
    // Forward to CFindView
    return iParentView.IsNativeMatchingRequestActive();
    }
    
// --------------------------------------------------------------------------
// CRefineView::IsContactAlwaysIncluded
// --------------------------------------------------------------------------
//
TBool CRefineView::IsContactAlwaysIncluded( 
        const CViewContact& aContact ) const
    {
    return iAlwaysIncluded.IsContactAlwaysIncluded( aContact );
    }

// --------------------------------------------------------------------------
// CRefineView::StartUpdateCallback
// --------------------------------------------------------------------------
//
TInt CRefineView::StartUpdateCallback( TAny* aThis )
    {
    CRefineView* view = static_cast<CRefineView*>( aThis );
    // If the CFindView is doing async match from SIM server then
    // do nothing here and wait that parent view notifies this view.
    if ( !view->IsNativeMatchingRequestActive() )
        {
        TRAPD( res, view->ActivateContactMatchL() )
        if ( res != KErrNone )
            {
            view->ContactViewError( *view, res, EFalse );
            }
        }
    // Do not continue idle
    return EFalse;
    }
} // namespace VPbkSimStore

// End of File
