/*
* Copyright (c) 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:  Phonebook 2 merge contacts command.
*
*/


// INCLUDE FILES

//Phonebook2
#include "CPbk2MergeContactsCmd.h"
#include "CPbk2MergeResolver.h"
#include <MPbk2CommandObserver.h>
#include <MPbk2ContactUiControl.h>
#include <MPbk2ApplicationServices.h>
#include <MPbk2AppUi.h>
#include <MPbk2DialogEliminator.h>
#include <MPbk2ContactLinkIterator.h>
#include <CPbk2FetchDlg.h>
#include <CPbk2ContactViewBuilder.h>
#include <CVPbkFilteredContactView.h>
#include <CPbk2ApplicationServices.h>
#include <CPbk2PresentationContact.h>
#include <CPbk2FieldPropertyArray.h>
#include <MPbk2ContactViewSupplier.h>
#include <CPbk2StoreConfiguration.h>
#include <CPbk2StorePropertyArray.h>
#include <CPbk2StoreProperty.h>
#include <Pbk2StoreProperty.hrh>
#include <Pbk2ProcessDecoratorFactory.h>
#include <MPbk2ContactNameFormatter.h>

#include <CPbk2MergeConflictsDlg.h>
#include <CPbk2MergePhotoConflictDlg.h>

#include <Pbk2UIControls.rsg> 
#include <Pbk2CmdExtRes.rsg>
#include <Pbk2CommonUi.rsg>
//Virtual Phonebook
#include <MVPbkContactLink.h>
#include <MVPbkContactViewBase.h>
#include <CVPbkContactStoreUriArray.h>
#include <MVPbkContactStore.h>
#include <MVPbkContactStoreProperties.h>
#include <MVPbkContactOperationBase.h>
#include <CVPbkContactManager.h>
#include <MVPbkStoreContact.h>
#include <MVPbkContactFieldBinaryData.h>
#include <MVPbkContactFieldTextData.h>
#include <VPbkEng.rsg>
#include <MVPbkContactGroup.h>

//System
#include <aknnotewrappers.h>
#include <StringLoader.h>
//#include <aknnavide.h>
#include <akntitle.h>
#include <imageconversion.h>

// Debugging headers
#include <Pbk2Debug.h>
#include <Pbk2Profile.h>

/// Unnamed namespace for local definitions
namespace {

const TInt KFirstContact = 0;
const TInt KSecondContact = 1;
const TInt KAmountToMerge = 2;
const TInt KDeletionDelay = 1000000; // 1s

_LIT( KLocalStore, "cntdb://c:contacts.cdb" );

enum TPbk2PanicCodes
        {
        EPbk2CommandObserverMissing,
        EPbk2WronglyActivated,
        EPbk2DuplicateCallToExecuteLD,
        EPbk2ViewsAlreadyCreated,
        EPbk2WrongTypeOfData,
		EPbk2PhotoConflictError
        };
    
    void Panic(TPbk2PanicCodes aReason)
        {
        _LIT( KPanicText, "CPbk2_Merge_Contacts" );
        User::Panic(KPanicText,aReason);
        }

} /// namespace


// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::CPbk2MergeContactsCmd
// --------------------------------------------------------------------------
//
CPbk2MergeContactsCmd::CPbk2MergeContactsCmd(
    MPbk2ContactUiControl& aUiControl ) :
        CActive( EPriorityStandard ),
        iNextPhase( EPhaseNone ),
        iUiControl( &aUiControl )
    {
    CActiveScheduler::Add( this );
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::~CPbk2MergeContactsCmd
// --------------------------------------------------------------------------
//
CPbk2MergeContactsCmd::~CPbk2MergeContactsCmd()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2NlxMergeContactsCmd(%x)::~CPbk2MergeContactsCmd()"), 
        this);
    
    Cancel();
    
    if ( iUiControl )
        {
        iUiControl->RegisterCommand( NULL );
        }

    CleanAfterFetching();
    iTimer.Cancel();
    iTimer.Close();
    delete iFirstContactString;
    delete iSecondContactString;
    delete iMergedContactString;
    delete iRetrieveOperation;
    delete iDeleteOperation;
    delete iCommitOperation;
	delete iDeleteMergedOperation;
    Release( iAppServices );
    delete iContactFirst;
    delete iContactSecond;
    delete iMergedContactLink;
    delete iStoreContactFirst;
    delete iStoreContactSecond;
    delete iMergedContact;
    delete iMergeResolver;
    delete iWaitDecorator;
    if( iGroupsToAdd )
        {
        iGroupsToAdd->ResetAndDestroy();
        delete iGroupsToAdd;
        }
    if ( iContactToCommit )
        {
        iContactToCommit->Reset();
        delete iContactToCommit;
        }
    if ( iContactsToDelete )
        {
        iContactsToDelete->Reset();
        delete iContactsToDelete;
        }
    if( iGroupLinksFirst )
        {
        iGroupLinksFirst->ResetAndDestroy();
        delete iGroupLinksFirst;
        }
    if( iGroupLinksSecond )
        {
        iGroupLinksSecond->ResetAndDestroy();
        delete iGroupLinksSecond;
        }
    TRAP_IGNORE( SetTitlePaneL( EFalse ) );
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::NewL
// --------------------------------------------------------------------------
//
CPbk2MergeContactsCmd* CPbk2MergeContactsCmd::NewL(
    MPbk2ContactUiControl& aUiControl )
    {
    CPbk2MergeContactsCmd* self = 
        new ( ELeave ) CPbk2MergeContactsCmd( aUiControl );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ConstructL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ConstructL()
    {    
    if( iUiControl )
        {
        iUiControl->RegisterCommand( this );
        }    
    
    iContactManager = &Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager();
    iAppServices = CPbk2ApplicationServices::InstanceL();
    iPhotoConflictIndex = KErrNotFound;
    User::LeaveIfError( iTimer.CreateLocal() );
    }
    
// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ExecuteLD
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ExecuteLD()
    {
    __ASSERT_ALWAYS( iCommandObserver, Panic( EPbk2CommandObserverMissing ));	    
    __ASSERT_ALWAYS( !IsActive(), Panic( EPbk2WronglyActivated ));	
    __ASSERT_ALWAYS( iNextPhase == EPhaseNone, Panic( EPbk2DuplicateCallToExecuteLD ));
    
    if( iUiControl )
        {
        // Blank UI control to avoid flicker
        iUiControl->SetBlank( ETrue );
        }
    StartNext( EPhaseGetSelection );
    }
    
// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ResetUiControl
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ResetUiControl( 
    MPbk2ContactUiControl& aUiControl )
    {
    if ( iUiControl == &aUiControl )
        {
        iUiControl = NULL;
        }
    }
    
// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::AddObserver
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::AddObserver( 
    MPbk2CommandObserver& aObserver )
    {
    iCommandObserver = &aObserver;
    }        

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::AutomaticMergeL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::AutomaticMergeL()
    {
    if(iStoreContactFirst && iStoreContactSecond)
        {
        iMergeResolver = CPbk2MergeResolver::NewL(
                CPbk2PresentationContact::NewL( *iStoreContactFirst, iAppServices->FieldProperties() ), 
                CPbk2PresentationContact::NewL( *iStoreContactSecond, iAppServices->FieldProperties() ) );
        iMergeResolver->MergeL();
        StartNext(EPhaseResolveConflicts);
        }
    else
        {
        Finish(KErrArgument);
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::AddGroupsL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::AddGroupsL()
    {
    TInt countGroups = iGroupsToAdd->Count();
    if(countGroups)
        {
        MVPbkStoreContact *group = iGroupsToAdd->At(countGroups - 1);
        group->LockL(*this);
        }
    else
        {
        DeleteSourceContactsL();
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::RunL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::RunL()
    {
    if( !iUiControl )
        {
        //UiControl reseted, so cancel all processing
        iNextPhase = EPhaseFinish ;
        }

    switch( iNextPhase )
    	{
    	case EPhaseGetSelection:
    	    {
    	    GetContactsFromUiFetchL();    	    
    	    }
    	    break;
    	case EPhaseGetStoreContacts:
            {
            GetStoreContacts();
            }   
            break;
    	case EPhaseMerge:
    	    {
            AutomaticMergeL();
    	    }
            break;
    	case EPhaseResolveConflicts:
            {
            ResolveConflictsL();
            }
            break;
        case EPhaseResolvePhotoConflict:
            {
            ResolvePhotoConflictL();
            }
            break;
    	case EPhaseCreateMergedContact:
        	{
        	iWaitDecorator = Pbk2ProcessDecoratorFactory::CreateWaitNoteDecoratorL
                                   ( R_QTN_MERGE_CONTACTS_WAIT_NOTE, EFalse );
        	iWaitDecorator->SetObserver( *this );
            iWaitDecorator->ProcessStartedL( 0 );
            FinalizeMergeL();
        	}
    		break;
    	case EPhaseGetGroups:
            {
            GetGroupsL();
            }
            break;
    	case EPhaseAddGroups:
            {
            AddGroupsL();
            }
            break;
    	case EPhaseFinish:
            {
            if ( iWaitDecorator )
                {
                // Decorator calls ProcessDismissed
                iWaitDecorator->ProcessStopped();
                }
            else
                {
                // In case Decorator wasn't initialized we invoke
                // ProcessDismissed to finish merge command execution
                ProcessDismissed( KErrNone );
                }
            }
            break;
            
    	default:
    		Panic( EPbk2WronglyActivated );
    		break;
    	}
    }    

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::RunError
// --------------------------------------------------------------------------
//
TInt CPbk2MergeContactsCmd::RunError(TInt aError)
	{
	Finish( aError );
    return KErrNone;
	}
    
// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::FinalizeMergeL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::FinalizeMergeL()
    {
    if( !iMergeResolver ||  
            ( !iMergeResolver->CountMerged() && !iMergeResolver->CountConflicts() ) )
        {
        Finish( KErrNone );
        return;
        }
    
    MVPbkContactStore& store = iStoreContactFirst->ParentStore();
    iMergedContact = store.CreateNewContactLC();
    CleanupStack::Pop();
    
    // the conflict thumbnail data's field, used for comparing
    MVPbkStoreContactField* thumbnailPicField = NULL;
    
    TInt conflictCount = iMergeResolver->CountConflicts();
    // add conflicted fields
    for ( TInt i = 0; i < conflictCount; i++ )
        {
        MPbk2MergeConflict& conflict = iMergeResolver->GetConflictAt( i );
        
        RPointerArray<MVPbkStoreContactField> fields;
        conflict.GetChosenFieldsL( fields );
        
        TInt newFields = fields.Count();
        /// if newFields == 0 not resolved conflict exist
        
        for ( TInt j = 0; j < newFields; j++ )
            {
            MVPbkStoreContactField* field = fields[ j ];
            const MVPbkFieldType* fieldType = field->BestMatchingFieldType();
            
            // assign value to thumnailPicField with the field's value
            if ( fieldType->FieldTypeResId() == R_VPBK_FIELD_TYPE_THUMBNAILPIC )
                {
                thumbnailPicField = field;
                }
            AddFieldToMergedContactL( *field );
            } 
        fields.Close();
        }
    
    TInt mergedFieldsCount = iMergeResolver->CountMerged();
        
    // add merged fields
    for ( TInt i = 0; i < mergedFieldsCount; i++ )
        {
        MVPbkStoreContactField& field = iMergeResolver->GetMergedAt( i );
        const MVPbkFieldType* fieldType = field.BestMatchingFieldType();
        
        // avoid merging filePath when the two field unmatched to a contact
        if ( fieldType->FieldTypeResId() == R_VPBK_FIELD_TYPE_CALLEROBJIMG && thumbnailPicField )
            {
            if ( !thumbnailPicField->ParentContact().IsSame( field.ParentContact() ) )
                {
                continue;
                }
            }
        AddFieldToMergedContactL( field );
        }
    
    iContactToCommit = new ( ELeave ) CArrayPtrFlat<MVPbkStoreContact>( 1 );
    iContactToCommit->AppendL( iMergedContact );
    iCommitOperation = iContactManager->CommitContactsL( iContactToCommit->Array(), *this );
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ShowContactsMergedNoteL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ShowContactsMergedNoteL()
    {
    HBufC* unnamed = StringLoader::LoadLC( R_QTN_PHOB_UNNAMED );
    CDesCArrayFlat* strings = new(ELeave) CDesCArrayFlat( 3 );
    CleanupStack::PushL( strings );
    
    HBufC* prompt = NULL;

    if ( ( iFirstContactString->Compare( *unnamed ) || iSecondContactString->Compare( *unnamed ) )
            && iMergedContactString->Compare( *unnamed ) )
        {
        strings->AppendL( *iFirstContactString );
        strings->AppendL( *iSecondContactString );
        strings->AppendL( *iMergedContactString );

        prompt = StringLoader::LoadLC( R_QTN_PHOB_NOTE_CONTACTS_WERE_MERGED, *strings );
        }
    else
        {
        strings->AppendL( *unnamed );
        prompt = StringLoader::LoadLC( R_QTN_PHOB_NOTE_UNNAMED_CONTACTS_WERE_MERGED , *strings );
        }

    CAknQueryDialog* dlg = CAknQueryDialog::NewL();
    dlg->ExecuteLD( R_PBK2_MERGE_CONTACTS_CONFIRMATION_NOTE, *prompt );
    
    CleanupStack::PopAndDestroy( prompt );
    CleanupStack::PopAndDestroy( strings );
    CleanupStack::PopAndDestroy( unnamed );

    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactAsStringLC
// --------------------------------------------------------------------------
//
HBufC* CPbk2MergeContactsCmd::ContactAsStringL( MVPbkStoreContact* aStoreContact )
    {
    MVPbkStoreContactFieldCollection& fields = aStoreContact->Fields();
    HBufC* result = NULL;
    const TInt KDefaultListFormatting = 
            MPbk2ContactNameFormatter::EDisableCompanyNameSeparator;
    result = Phonebook2::Pbk2AppUi()->
            ApplicationServices().NameFormatter().GetContactTitleL( fields, KDefaultListFormatting );
    if( !result )
        {
        result = StringLoader::LoadLC( R_QTN_PHOB_UNNAMED );
        CleanupStack::Pop( result );
        }
    return result;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::DoCancel
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::DoCancel()
    {
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::NotifyObservers
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::NotifyObservers()
    {
    if ( iCommandObserver )
        {
        iCommandObserver->CommandFinished( *this );
        }
    
    if ( iUiControl )
        {
        iUiControl->SetBlank( EFalse );
        iUiControl->UpdateAfterCommandExecution();
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::AcceptFetchSelectionL
// --------------------------------------------------------------------------
//
MPbk2FetchDlgObserver::TPbk2FetchAcceptSelection CPbk2MergeContactsCmd::AcceptFetchSelectionL(
    TInt /*aNumMarkedEntries*/,
    MVPbkContactLink& /*aLastSelection*/ )
    {
    return MPbk2FetchDlgObserver::EFetchYes;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::FetchCompletedL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::FetchCompletedL(
    MVPbkContactLinkArray* aMarkedEntries )
    {   
    if ( aMarkedEntries )
        {
        TInt count = aMarkedEntries->Count();
        if ( count == 2 )
            {
            iContactFirst = aMarkedEntries->At( KFirstContact ).CloneLC();
            CleanupStack::Pop();
            iContactSecond = aMarkedEntries->At( KSecondContact ).CloneLC();
            CleanupStack::Pop();
            StartNext( EPhaseGetStoreContacts );
            }
        else
            {
            // Show a note
            HBufC* prompt = NULL;
            prompt = StringLoader::LoadLC( R_QTN_PHOB_NOTE_SELECT_2_TO_MERGE );
            CAknQueryDialog* dlg = CAknQueryDialog::NewL();
            dlg->ExecuteLD( R_PBK2_MERGE_CONTACTS_ERROR_NOTE, *prompt );
            CleanupStack::PopAndDestroy( prompt ); 
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::FetchCanceled
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::FetchCanceled()
    {
    Finish( KErrCancel );
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::FetchAborted
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::FetchAborted()
    {
    Finish( KErrCancel );
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::FetchOkToExit
// --------------------------------------------------------------------------
//
TBool CPbk2MergeContactsCmd::FetchOkToExit()
    {
    if ( iNextPhase == EPhaseFinish )
        {
        return ETrue;
        }
    
    if ( iContactFirst && iContactSecond )
        {
        return ETrue;
        }
    else
        {
        return EFalse;
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::GetContactsFromUiFetchL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::GetContactsFromUiFetchL()
    {
    __ASSERT_DEBUG( !iAllContactsView, Panic( EPbk2ViewsAlreadyCreated ));
    CPbk2FetchDlg::TParams params;
    params.iResId = R_PBK2_MULTIPLE_ENTRY_FETCH_NO_GROUPS_DLG;
    params.iCbaId = R_PBK2_SOFTKEYS_MERGE_BACK_MARK;
    params.iNaviPaneId = R_PBK2_MERGE_CONTACTS_FETCH_NAVILABEL;
    params.iMinSelection = KAmountToMerge;
    
    CPbk2StorePropertyArray& storeProperties =
        Phonebook2::Pbk2AppUi()->ApplicationServices().StoreProperties();
    
    // Fetch dlg uses this view instead of AllNameslistView if it is not suitable
    iStoreUris = Phonebook2::Pbk2AppUi()->
        ApplicationServices().StoreConfiguration().CurrentConfigurationL();
    
    TBool createNewView = EFalse;
    for ( TInt i = iStoreUris->Count()-1; i >= 0; --i )
        {
        const CPbk2StoreProperty* storeProperty = 
            storeProperties.FindProperty( ( *iStoreUris )[i] );
        
        if ( storeProperty != NULL && storeProperty->StoreUri().UriDes().Compare( KLocalStore ) )
            {
            iStoreUris->Remove( (*iStoreUris)[i] );
            createNewView = ETrue;
            }
        }    
    
    if ( createNewView )
        {
        iAllContactsView = CVPbkFilteredContactView::NewL
            ( *Phonebook2::Pbk2AppUi()->ApplicationServices().ViewSupplier().
              AllContactsViewL(), *this, *this, iContactManager->FieldTypes() );
        
        params.iNamesListView = iAllContactsView;
        iObservedView = iAllContactsView;
        }
    else{
        params.iNamesListView =
            Phonebook2::Pbk2AppUi()->ApplicationServices().ViewSupplier().AllContactsViewL();
        iObservedView =
            Phonebook2::Pbk2AppUi()->ApplicationServices().ViewSupplier().AllContactsViewL();
        }    

    iObservedView->AddObserverL( *this );
        
    params.iGroupsListView = NULL;
    params.iFlags = CPbk2FetchDlg::EFetchMultiple;
    params.iExitCallback = this;
    
    CPbk2FetchDlg* dlg = CPbk2FetchDlg::NewL( params, *this );
    iFetchDlgEliminator = dlg;
    iFetchDlgEliminator->ResetWhenDestroyed( &iFetchDlgEliminator );
    
    SetTitlePaneL( ETrue );
    dlg->ExecuteLD(); // Completion is signalled with a callback.
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::IsFromReadOnlyStore
// --------------------------------------------------------------------------
//
TBool CPbk2MergeContactsCmd::IsFromReadOnlyStore
        ( const MVPbkContactLink& aContactLink ) const
    {
    TBool ret = EFalse;

    const MVPbkContactStore& store = aContactLink.ContactStore();
    if ( store.StoreProperties().ReadOnly() )
        {
        ret = ETrue;
        }

    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::CleanAfterFetching
// insure the fetch dlg is deleted and destroy resources used by it 
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::CleanAfterFetching()
    {
    if ( iFetchDlgEliminator )
        {
        iFetchDlgEliminator->ForceExit();
        }
    if ( iObservedView )
        {
        iObservedView->RemoveObserver( *this );
        }
    delete iAllContactsView;
    iAllContactsView = NULL;   
    delete iStoreUris;
    iStoreUris = NULL;
    }

// ---------------------------------------------------------------------------
// CPbk2MergeContactsCmd::Finish
// ---------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::Finish( TInt aReason )
	{
	if ( aReason != KErrNone && aReason != KErrCancel )
	    {
	    CCoeEnv::Static()->HandleError( aReason );
	    }
	
	if( iNextPhase != EPhaseFinish )
		{
    	StartNext( EPhaseFinish );
		}
	}

// ---------------------------------------------------------------------------
// CPbk2MergeContactsCmd::StartNext
// ---------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::StartNext( TPhase aPhase )
    {
    __ASSERT_DEBUG( !IsActive(), Panic( EPbk2WronglyActivated )); 
    
    iNextPhase = aPhase;
    
    if ( iNextPhase == EPhaseGetGroups )
        {
        iTimer.After( iStatus, KDeletionDelay ); 
        }
    else
        {
        TRequestStatus* status = &iStatus;
        User::RequestComplete( status, KErrNone );
        }
    SetActive();
    }

// ---------------------------------------------------------------------------
// CPbk2MergeContactsCmd::StartNext
// ---------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::StartNext()
    {
    __ASSERT_DEBUG( !IsActive(), Panic( EPbk2WronglyActivated ));    
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, KErrNone);
    SetActive();
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::RetrieveContactL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::RetrieveContact(
        const MVPbkContactLink& aContactLink )
    {
    // Retrieve the actual store contact from the given link
    TRAPD( error, 
        iRetrieveOperation = iAppServices->
        ContactManager().RetrieveContactL( aContactLink, *this );
    );
    if( error != KErrNone )
        {
        Finish( error );
        }
    }

////////////////////////////// CALLBACKS /////////////////////////////////////

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::VPbkSingleContactOperationComplete
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::VPbkSingleContactOperationComplete(
        MVPbkContactOperationBase& aOperation,
        MVPbkStoreContact* aContact )
    {
    if ( &aOperation == iRetrieveOperation )
        {
        delete iRetrieveOperation;
        iRetrieveOperation = NULL;

        if( !iStoreContactFirst )
            {
            iStoreContactFirst = aContact;
            TRAPD( error, iStoreContactFirst->LockL( *this ) );
            if( error != KErrNone )
                {
                Finish( error );
                }
            }
        else if( !iStoreContactSecond )
            {
            iStoreContactSecond = aContact;
            TRAPD( error, iStoreContactSecond->LockL( *this ) );
            if( error != KErrNone )
                {
                Finish( error );
                }
            }
        else if( iNextPhase == EPhaseGetGroups 
                 && aContact->Group()
                 && iGroupLinksFirst 
                 && iGroupLinksSecond
                 && iGroupsToAdd )
            {
            TBool sameGroup = EFalse;
            for( TInt idx = 0; idx < iGroupsToAdd->Count(); idx++ )
                {
                if( aContact->IsSame( *iGroupsToAdd->At( idx ) ) )
                    {
                    sameGroup = ETrue;
                    break;
                    }
                }
            TInt error = KErrNone;
            if( !sameGroup )
                {
                TRAP( error, iGroupsToAdd->AppendL( aContact ); );
                if ( error != KErrNone )
                    {
                    delete aContact;
                    DeleteMergedContact();
                    return;
                    }
                }
            else
                {
                delete aContact;
                }
            
            TInt countFirst = iGroupLinksFirst->Count();
            TInt countSecond = iGroupLinksSecond->Count();
            
            if( countFirst )
                {
                delete &( iGroupLinksFirst->At( countFirst - 1 ) );
                iGroupLinksFirst->Remove( countFirst - 1 );
                }
            else if( countSecond )
                {
                delete &( iGroupLinksSecond->At( countSecond - 1 ) );
                iGroupLinksSecond->Remove( countSecond - 1 );
                }
            
            countFirst = iGroupLinksFirst->Count();
            countSecond = iGroupLinksSecond->Count();
            
            if( countFirst )
                {
                RetrieveContact( iGroupLinksFirst->At( countFirst - 1 ) );
                }
            else if( countSecond )
                {
                RetrieveContact( iGroupLinksSecond->At( countSecond - 1 ) );
                }
            else
                {
                StartNext( EPhaseAddGroups );
                }
            if ( error != KErrNone )
                {
                delete aContact;
                DeleteMergedContact();
                }
            }
        else
            {
            delete aContact;
            DeleteMergedContact();
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::VPbkSingleContactOperationFailed
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::VPbkSingleContactOperationFailed(
        MVPbkContactOperationBase& aOperation,
        TInt aError )
    {
    if ( &aOperation == iRetrieveOperation )
        {
        delete iRetrieveOperation;
        iRetrieveOperation = NULL;
        
        Finish( aError );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactOperationCompleted
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactOperationCompleted(
        TContactOpResult aResult )
    {
    if( aResult.iOpCode == MVPbkContactObserver::EContactLock 
            && iNextPhase == EPhaseGetStoreContacts && !iStoreContactSecond )
        {
        RetrieveContact( *iContactSecond );
        }
    else if( aResult.iOpCode == MVPbkContactObserver::EContactLock 
            && iNextPhase == EPhaseGetStoreContacts )
        {
        StartNext( EPhaseMerge );
        }
    else if( aResult.iOpCode == MVPbkContactObserver::EContactLock )
        {
        TInt countGroups = iGroupsToAdd->Count();
        if( countGroups )
            {
            MVPbkStoreContact* group = iGroupsToAdd->At( countGroups - 1 );
            TRAPD( error, 
                group->Group()->AddContactL( *iMergedContactLink );
                group->CommitL( *this );
            );
            if ( error != KErrNone )
                {
                DeleteMergedContact();
                }
            }
        }
    else if( aResult.iOpCode == MVPbkContactObserver::EContactCommit) 
        {
        TInt countGroups = iGroupsToAdd->Count();
        if( countGroups )
            {
            MVPbkStoreContact* group = iGroupsToAdd->At( countGroups - 1 );
            delete group;
            iGroupsToAdd->Delete( countGroups - 1 );
            }
        countGroups = iGroupsToAdd->Count();
        if( countGroups )
            {
            MVPbkStoreContact* group = iGroupsToAdd->At( countGroups - 1 );
            TRAPD( error, 
                group->LockL( *this );
            );
           if ( error != KErrNone )
               {
               DeleteMergedContact();
               }
            }
        else
            {
            TRAPD( error, 
                DeleteSourceContactsL();
            );
           if ( error != KErrNone )
               {
               DeleteMergedContact();
               }
            }
        }
    delete aResult.iStoreContact;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactOperationFailed
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactOperationFailed(
        TContactOp aOpCode, TInt aErrorCode, TBool /*aErrorNotified*/ )
    {
    if( aOpCode == MVPbkContactObserver::EContactLock && iNextPhase == EPhaseGetStoreContacts )
        {
        Finish( aErrorCode );
        }
    else
        {
        DeleteMergedContact();
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ProcessDismissed
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ProcessDismissed
        ( TInt /*aCancelCode*/ )
    {
    if( iMergedContact )
        {
        TRAP_IGNORE( ShowContactsMergedNoteL() );
        }
    NotifyObservers();
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactViewReady
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactViewReady
        ( MVPbkContactViewBase& /*aView*/ )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactViewUnavailable
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactViewUnavailable
        ( MVPbkContactViewBase& /*aView*/ )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactAddedToView
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactAddedToView
        ( MVPbkContactViewBase& /*aView*/, TInt /*aIndex*/,
          const MVPbkContactLink& aContactLink )
    {
    if ( iMergedContact && iNextPhase == EPhaseCreateMergedContact )
        {
        TRAPD( error,
            if( !iMergedContactLink  )
                {
                iMergedContactLink = iMergedContact->CreateLinkLC();
                if ( iMergedContactLink )
                    {
                    CleanupStack::Pop();
                    }
                else
                    {
                    User::Leave( KErrGeneral );
                    }
                }
            if ( iMergedContactLink && iMergedContactLink->IsSame( aContactLink ) )
                {
                StartNext( EPhaseGetGroups );
                }
        );
        if ( error != KErrNone )
            {
            Finish( error );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactRemovedFromView
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactRemovedFromView
        ( MVPbkContactViewBase& /*aView*/, TInt /*aIndex*/,
          const MVPbkContactLink& /*aContactLink*/ )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ContactViewError
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ContactViewError
        ( MVPbkContactViewBase& /*aView*/, TInt /*aError*/,
          TBool /*aErrorNotified*/ )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::OkToExitL
// --------------------------------------------------------------------------
//
TBool CPbk2MergeContactsCmd::OkToExitL( TInt /*aCommandId*/ )
    {
    return ETrue;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::IsContactIncluded
// --------------------------------------------------------------------------
//
TBool CPbk2MergeContactsCmd::IsContactIncluded
        ( const MVPbkBaseContact& aContact )
    {
    for ( TInt i = iStoreUris->Count()-1; i >= 0; --i )
        {
        if ( aContact.MatchContactStore((*iStoreUris)[i].UriDes()) )
            {
            return ETrue;
            }
        }  
    return EFalse;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::StepComplete
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::StepComplete( MVPbkContactOperationBase& /*aOperation*/, TInt /*aStepSize*/ )
    {
    
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::StepFailed
// --------------------------------------------------------------------------
//
TBool CPbk2MergeContactsCmd::StepFailed( MVPbkContactOperationBase& aOperation, 
                                         TInt /*aStepSize*/, TInt aError )
    {
    if ( &aOperation == iCommitOperation )
        {
        delete iCommitOperation;
        iCommitOperation = NULL;
        iContactToCommit->Reset();
        delete iContactToCommit;
        iContactToCommit = NULL;
        Finish( aError );
        }
    else if ( &aOperation == iDeleteOperation )
        {
        delete iDeleteOperation;
        iDeleteOperation = NULL;
        // Merged contact must be deleted
        DeleteMergedContact();
        }
    else if ( &aOperation == iDeleteMergedOperation )
        {
        delete iDeleteMergedOperation;
        iDeleteMergedOperation = NULL;
        Finish( aError );
        }
    return EFalse;
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::perationComplete
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::OperationComplete( MVPbkContactOperationBase& aOperation )
    {
    if ( &aOperation == iCommitOperation )
        {
        delete iCommitOperation;
        iCommitOperation = NULL;
        iContactToCommit->Reset();
        delete iContactToCommit;
        iContactToCommit = NULL;
        }
    else if ( &aOperation == iDeleteOperation )
        {
        delete iDeleteOperation;
        iDeleteOperation = NULL;
        Finish( KErrNone );
        }
    else if ( &aOperation == iDeleteMergedOperation )
        {
        delete iDeleteMergedOperation;
        iDeleteMergedOperation = NULL;
        delete iMergedContact;
        iMergedContact = NULL;
        Finish( KErrInUse );
        }
    }


// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::SetTitlePaneL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::SetTitlePaneL( TBool aCustom )
    {
    if ( iAvkonAppUi )
        {
        CEikStatusPane* statusPane = iAvkonAppUi->StatusPane();
        if ( statusPane && statusPane->PaneCapabilities( TUid::Uid( EEikStatusPaneUidTitle ) ).IsPresent() )
            {
            CAknTitlePane* titlePane = static_cast<CAknTitlePane*>
                ( statusPane->ControlL ( TUid::Uid( EEikStatusPaneUidTitle ) ) );
        
            if ( aCustom )
                {
                HBufC* title = StringLoader::LoadLC( R_QTN_PHOB_TITLE_MERGE_CONTACTS );
                titlePane->SetTextL( *title );
                CleanupStack::PopAndDestroy( title );  
                }
            else
                {
                titlePane->SetTextToDefaultL();
                }
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::CheckPhotoConflictL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::CheckPhotoConflictL()
    {
    iImageTypeConflictsCount = 0;
    iPhotoConflictIndex = KErrNotFound;
    TInt conflictsCount = iMergeResolver->CountConflicts();
    for ( TInt i = 0; i < conflictsCount; i++ )
        {
        MPbk2MergeConflict& conflict = iMergeResolver->GetConflictAt( i );
        TInt type = conflict.GetConflictType();
        if ( type == EPbk2ConflictTypeImage )
            {
            iImageTypeConflictsCount++;
            CPbk2MergeConflict& conflict =
                ( CPbk2MergeConflict& ) iMergeResolver->GetConflictAt( i );
            const MVPbkStoreContactField* firstField;
            const MVPbkStoreContactField* secondField;
            conflict.GetFieldsL( firstField, secondField );
            
            const MVPbkFieldType* fieldType = firstField->BestMatchingFieldType();
            if ( fieldType )
                {
                TArray<TVPbkFieldVersitProperty> versitPropArr = fieldType->VersitProperties();
                TInt count = versitPropArr.Count();
            
                for( TInt idx = 0; idx < count; idx++ )
                    {
                    TVPbkFieldVersitProperty versitProp = versitPropArr[idx];
                    if( versitProp.Name() == EVPbkVersitNamePHOTO )
                        {
                        if ( firstField->FieldData().DataType() == EVPbkFieldStorageTypeBinary && 
                                secondField->FieldData().DataType() == EVPbkFieldStorageTypeBinary )
                            {
                            iPhotoConflictIndex = i;
                            break;
                            }
                        }
                    }
                }
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ResolveAllPhotoConflicts
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ResolveAllPhotoConflicts( 
        EPbk2ConflictedNumber aConflictResolutionNumber )
    {
    TInt conflictsCount = iMergeResolver->CountConflicts();
    for ( TInt i = 0; i < conflictsCount; i++ )
        {
        MPbk2MergeConflict& conflict = iMergeResolver->GetConflictAt( i );
        if ( conflict.GetConflictType() == EPbk2ConflictTypeImage )
            {
            conflict.ResolveConflict( aConflictResolutionNumber );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::ResolvePhotoConflictL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ResolvePhotoConflictL()
    {
    if ( iStatus != KErrNone || iPhotoConflictIndex == KErrNotFound )
        {
        StartNext( EPhaseCreateMergedContact );
        return;
        }
    
    TInt result = EPbk2ConflictedFirst;
    
    CPbk2MergePhotoConflictDlg* dlg = 
        CPbk2MergePhotoConflictDlg::NewL( iStoreContactFirst, iStoreContactSecond, &result );
    if ( !dlg->ExecuteLD( R_PBK2_MERGE_CONTACTS_PHOTO_CONFLICT_RESOLUTION_DLG ) )
        {
        // dlg returns 0 if canceled
        Finish( KErrNone );
        }
    else
        {
        ResolveAllPhotoConflicts( (EPbk2ConflictedNumber) result );
        StartNext( EPhaseCreateMergedContact );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::AddFieldToMergedContactL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::AddFieldToMergedContactL( MVPbkStoreContactField& field )
    {
    const MVPbkFieldType* type = field.BestMatchingFieldType();
    if ( type )
        {
        MVPbkStoreContactField* newField = iMergedContact->CreateFieldLC( *type );
        newField->SetFieldLabelL( field.FieldLabel() );
        newField->FieldData().CopyL( field.FieldData() );
        iMergedContact->AddFieldL( newField );
        CleanupStack::Pop( newField );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::DoResolveConflictsL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::ResolveConflictsL()
    {
    CheckPhotoConflictL();
    
    TBool isPhotoConflict = iPhotoConflictIndex != KErrNotFound;
    TInt normalConflicts = 0;
    if( iMergeResolver )
        {
        normalConflicts = iMergeResolver->CountConflicts() - iImageTypeConflictsCount;
        }
    
    if( normalConflicts )
        {
        // resolve conflicts
        CPbk2MergeConflictsDlg* dlg = CPbk2MergeConflictsDlg::NewL( iMergeResolver, isPhotoConflict );
        if ( !dlg->ExecuteLD( R_PBK2_MERGE_CONTACTS_CONFLICT_RESOLUTION_DLG ) )
            {
            // dlg returns 0 if canceled
            Finish( KErrCancel );
            }
        else
            {
            StartNext( EPhaseResolvePhotoConflict );
            }
        }
    else
        {
        StartNext( EPhaseResolvePhotoConflict );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::GetStoreContactsL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::GetStoreContacts()
    {
    if( iContactFirst && iContactSecond )
        {
        if( !iStoreContactFirst )
            {
            RetrieveContact( *iContactFirst );
            }
        else
            {
            Finish( KErrArgument );
            }
        }
    else
        {
        Finish( KErrArgument );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::DeleteSourceContactsL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::DeleteSourceContactsL()
    {
    User::LeaveIfNull( iContactFirst );
    User::LeaveIfNull( iContactSecond );
    
    iFirstContactString = ContactAsStringL( iStoreContactFirst );
    iSecondContactString = ContactAsStringL( iStoreContactSecond );
    iMergedContactString = ContactAsStringL( iMergedContact );
    
    delete iStoreContactFirst;
    iStoreContactFirst = NULL;
    
    delete iStoreContactSecond;
    iStoreContactSecond = NULL;
    
    if( iContactsToDelete )
        {
        delete iContactsToDelete;
        iContactsToDelete = NULL;
        }
    iContactsToDelete = CVPbkContactLinkArray::NewL();
    iContactsToDelete->AppendL( iContactSecond );
    iContactsToDelete->AppendL( iContactFirst );
    if( iDeleteOperation )
        {
        delete iDeleteOperation;
        iDeleteOperation = NULL;
        }
    iDeleteOperation = iContactManager->DeleteContactsL( *iContactsToDelete, *this );
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::DeleteMergedContact
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::DeleteMergedContact()
    {
    TRAPD( error, 
            DeleteMergedContactL();
    );
    if( error != KErrNone )
        {
        Finish( KErrGeneral );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::DeleteMergedContactL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::DeleteMergedContactL()
    {
    if ( iContactsToDelete )
        {
        iContactsToDelete->Reset();
        }
    else
        {
        iContactsToDelete = CVPbkContactLinkArray::NewL();
        }
    
    if ( iMergedContact )
        {
        if( !iMergedContactLink  )
            {
            MVPbkContactLink* iMergedContactLink = iMergedContact->CreateLinkLC();
            if ( iMergedContactLink )
                {
                CleanupStack::Pop();
                }
            else
                {
                User::Leave( KErrGeneral );
                }
            }
        iContactsToDelete->AppendL( iMergedContactLink );
        iDeleteMergedOperation = iContactManager->DeleteContactsL( *iContactsToDelete, *this );
        }
    else
        {
        User::Leave( KErrGeneral );
        }
    }

// --------------------------------------------------------------------------
// CPbk2MergeContactsCmd::GetGroupsL
// --------------------------------------------------------------------------
//
void CPbk2MergeContactsCmd::GetGroupsL()
    {
    if( iStoreContactFirst && iStoreContactFirst && iMergedContact )
        {
        if( !iGroupsToAdd )
            {
            iGroupsToAdd = new ( ELeave ) CArrayPtrFlat<MVPbkStoreContact>( 1 );
            }
        iGroupLinksFirst = static_cast<CVPbkContactLinkArray*>( iStoreContactFirst->GroupsJoinedLC() );
        CleanupStack::Pop(); 
        iGroupLinksSecond = static_cast<CVPbkContactLinkArray*>( iStoreContactSecond->GroupsJoinedLC() );
        CleanupStack::Pop(); 
        
        TInt countFirst = iGroupLinksFirst->Count();
        TInt countSecond = iGroupLinksSecond->Count();
        
        if( countFirst )
            {
            RetrieveContact( iGroupLinksFirst->At( countFirst - 1 ) );
            }
        else if( countSecond )
            {
            RetrieveContact( iGroupLinksSecond->At( countSecond - 1 ) );
            }
        else
            {
            DeleteSourceContactsL();
            }
        }
    else
        {
        DeleteMergedContactL();
        }
    }
//  End of File
