/*
* 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:  Phonebook 2 delete contacts command.
*
*/


// INCLUDE FILES
#include "CPbk2DeleteContactsCmd.h"

// Phonebook2
#include <MPbk2CommandObserver.h>
#include "MPbk2ProcessDecorator.h"
#include "Pbk2ProcessDecoratorFactory.h"
#include <Pbk2Commands.rsg>
#include <Pbk2UIControls.rsg>
#include <MPbk2ContactUiControl.h>
#include <MPbk2ContactNameFormatter.h>
#include <CPbk2GeneralConfirmationQuery.h>
#include <MPbk2ContactLinkIterator.h>
#include <CPbk2ApplicationServices.h>
#include <MPbk2AppUi.h>

// Virtual Phonebook
#include <CVPbkContactManager.h>
#include <MVPbkContactLink.h>
#include <MVPbkStoreContact.h>
#include <MVPbkContactOperationBase.h>
#include <MVPbkContactStore.h>
#include <MVPbkContactStoreProperties.h>
#include <CVPbkContactLinkArray.h>

// System includes
#include <AknQueryDialog.h>

// Debugging headers
#include <Pbk2Debug.h>


/// Unnamed namespace for local definitions
namespace {

const TInt KOneContact = 1;

// LOCAL DEBUG CODE
#ifdef _DEBUG
enum TPanicCode
    {
    EPanicPreCond_ResetWhenDestroyed = 1,
    ERunL_InvalidState,
    EPanicLogic_DoDeleteContactsL,
    EPanicPreCond_Null_Pointer
    };

void Panic(TPanicCode aReason)
    {
    _LIT(KPanicText, "CPbk2DeleteContactsCmd");
    User::Panic(KPanicText,aReason);
    }
#endif // _DEBUG

}

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::CPbk2DeleteContactsCmd
// --------------------------------------------------------------------------
//
inline CPbk2DeleteContactsCmd::CPbk2DeleteContactsCmd(
        MPbk2ContactUiControl& aUiControl ) :
    CActive( EPriorityIdle ),
    iUiControl( &aUiControl )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2DeleteContactsCmd::CPbk2DeleteContactsCmd(0x%x)"), this);

    CActiveScheduler::Add( this );
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::~CPbk2DeleteContactsCmd
// --------------------------------------------------------------------------
//
CPbk2DeleteContactsCmd::~CPbk2DeleteContactsCmd()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2DeleteContactsCmd::~CPbk2DeleteContactsCmd(0x%x)"), this);

    Cancel();
    if (iUiControl)
        {
        iUiControl->RegisterCommand(NULL);
        }
    delete iDecorator;
    delete iContactIterator;
    delete iContactsToDelete;

    delete iRetrieveOperation;
    delete iDeleteOperation;
    delete iListContactsOperation;
    delete iStoreContact;
    Release( iAppServices );
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::ConstructL
// --------------------------------------------------------------------------
//
inline void CPbk2DeleteContactsCmd::ConstructL()
    {
    iAppServices = CPbk2ApplicationServices::InstanceL();

    // register command to ui control
    iUiControl->RegisterCommand(this);

    // Returns NULL if there are no selected contacts.
    // In that case we will delete the focused contact.
    iContactIterator = iUiControl->SelectedContactsIteratorL();

    iContactCount = GetContactCountL();

    if( iContactCount > 1 )
    	{
    	//we are deleting multiple contacts
        iDecorator = Pbk2ProcessDecoratorFactory::CreateProgressDialogDecoratorL
            ( R_QTN_PHOB_NOTE_CLEARING_PB_WITH_CANCEL, EFalse );

        iDecorator->SetObserver(*this);
    	}
    }

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

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::ExecuteLD
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::ExecuteLD()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2DeleteContactsCmd::ExecuteLD(0x%x)"), this);

    CleanupStack::PushL(this);

    if ( iContactCount > KOneContact )
        {
        // Move straight to confirm phase
        iState = EConfirming;
        IssueRequest();
        }
    else if ( iContactCount == KOneContact )
        {
        // There was only one contact, we have to retrieve it to get
        // its name
        iState = ERetrieving;
        IssueRequest();
        }
    else 
        {
        // If iContactCount == 0 we should end this command properly.
        // This happens when contact to be deleted is from read-only store.
        ProcessDismissed( KErrNone );
        }        

    CleanupStack::Pop( this );
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::AddObserver
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::AddObserver(
        MPbk2CommandObserver& aObserver )
    {
    iCommandObserver = &aObserver;
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::ResetUiControl
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::ResetUiControl(
        MPbk2ContactUiControl& aUiControl)
    {
    if (iUiControl == &aUiControl)
        {
        iUiControl = NULL;
        }
    }

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

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::RunL
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::RunL()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2DeleteContactsCmd::RunL() start"), this);

   switch ( iState )
        {
        case ERetrieving:
            {
            // Retrieve phase is executed only when there is one contact
            // to delete. Check is the contact marked one or focused one.            
            if ( iContactIterator )
                {
                iContactIterator->SetToFirst();
                if ( iContactIterator->HasNext() )
                    {
                    MVPbkContactLink* link = iContactIterator->NextL();
                    CleanupDeletePushL( link );
                    if ( link )
                        {
                        RetrieveContactL( *link );
                        }                    
                    CleanupStack::PopAndDestroy(); // link
                    }                   
                }
            else
                {
                if (iUiControl)
                    {
                    const MVPbkBaseContact* focusedContact =
                            iUiControl->FocusedContactL();
                    if ( focusedContact )
                        {
                        MVPbkContactLink* link = focusedContact->CreateLinkLC();
                        RetrieveContactL( *link );
                        CleanupStack::PopAndDestroy(); // link
                        }
                    }
                else
                    {
                    // nothing to delete
                    IssueStopRequest( EStopping );
                    }
                }            
            break;
            }
        case EConfirming:
            {
            ConfirmDeletionL();
            break;
            }
        case EStarting:
            {
            if( iDecorator )
            	{
                iDecorator->ProcessStartedL( iContactCount );
            	}
            
            if ( iContactIterator )
                {
                iContactIterator->SetToLast();
                }
            DoDeleteContactsL();
            break;
            }
        case EDeleting:
            {
            // Do nothing
            break;
            }
        case EStopping:
            {
            // Decorator calls ProcessDismissed
            if( iDecorator )
            	{
                iDecorator->ProcessStopped();            	
            	}
            else
            	{
                ProcessDismissed( KErrNone );
            	}
            break;
            }
        case ECanceling:
            {
            ProcessDismissed( KErrCancel );
            break;
            }
        default:
            {
            __ASSERT_DEBUG(EFalse, Panic(ERunL_InvalidState));
            break;
            }
        }

    PBK2_DEBUG_PRINT
        (PBK2_DEBUG_STRING("CPbk2DeleteContactsCmd::RunL end"));
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::RunError
// --------------------------------------------------------------------------
//
TInt CPbk2DeleteContactsCmd::RunError( 
        TInt aError )
    {
    return FilterErrors( aError );
    }

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

        // We now have a store contact and we can issue a confirmation
        iStoreContact = aContact;
        iState = EConfirming;
        IssueRequest();
        }
    }

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

        // We cannot get the contact, so we have to
        // fail. We cannot continue, since this operation
        // was executed only in case of one contact.
        ProcessDismissed( aError );
        }
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::StepComplete
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::StepComplete(
        MVPbkContactOperationBase& aOperation,
        TInt aStepSize )
    {
    if ( &aOperation == iDeleteOperation )
        {
        // Indicate process advancement to the decorator
        if( iDecorator )
        	{
            iDecorator->ProcessAdvance( aStepSize );
        	}
        }
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::StepFailed
// --------------------------------------------------------------------------
//
TBool CPbk2DeleteContactsCmd::StepFailed(
        MVPbkContactOperationBase& aOperation,
        TInt /*aStepSize*/,
        TInt aError )
    {
    TBool result = ETrue;

    if ( &aOperation == iDeleteOperation )
        {
        // Check whether the error is a serious one
        TInt err = FilterErrors( aError );
        if ( err != KErrNone )
            {
            Cancel();
            result = EFalse;
            }
        }

    return result;
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::OperationComplete
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::OperationComplete(
        MVPbkContactOperationBase& aOperation )
    {
    if ( &aOperation == iDeleteOperation )
        {
        delete iDeleteOperation;
        iDeleteOperation = NULL;

        if ( iContactIterator && iContactIterator->HasPrevious() )
            {
            TRAPD( error, DoDeleteContactsL() );
            if ( error != KErrNone )
                {
                FilterErrors( error );
                IssueStopRequest( EStopping );
                }
            }
        else
            {
            IssueStopRequest( EStopping );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::ProcessDismissed
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::ProcessDismissed( TInt aCancelCode )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2DeleteContactsCmd::ProcessDismissed()"));

    __ASSERT_DEBUG(iCommandObserver, Panic(EPanicPreCond_Null_Pointer));

    Cancel();
    delete iDeleteOperation;
    iDeleteOperation = NULL;

    if ( aCancelCode != KErrCancel && iUiControl)
        {
        // Don't remove list markings if user canceled
        iUiControl->SetBlank(EFalse);
        iUiControl->UpdateAfterCommandExecution();
        }

    // Notify command owner that the command has finished
    iCommandObserver->CommandFinished( *this );
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::RetrieveContactL
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::RetrieveContactL(
        const MVPbkContactLink& aContactLink )
    {
    // Retrieve the actual store contact from the given link
    iRetrieveOperation = iAppServices->
        ContactManager().RetrieveContactL( aContactLink, *this );
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::DoDeleteContactsL
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::DoDeleteContactsL()
    {
    delete iContactsToDelete;
    iContactsToDelete = NULL;
    iContactsToDelete = CVPbkContactLinkArray::NewL();
    const TInt KDeleteBlockSize = 200;

    if ( iContactIterator )
        {
        for ( TInt i = 0; i < KDeleteBlockSize &&
                iContactIterator->HasPrevious(); ++i )
            {
            MVPbkContactLink* link = iContactIterator->PreviousL();
            CleanupDeletePushL( link );
            iContactsToDelete->AppendL( link ); // takes ownership
            CleanupStack::Pop(); // link
            }
        }
    else
        {
        __ASSERT_DEBUG( iStoreContact,
            Panic( EPanicLogic_DoDeleteContactsL ) );

        MVPbkContactLink* link = iStoreContact->CreateLinkLC();
        iContactsToDelete->AppendL( link ); // takes ownership
        CleanupStack::Pop(); // link
        }

    iDeleteOperation = iAppServices->
        ContactManager().DeleteContactsL( *iContactsToDelete, *this );

    if (iUiControl)
        {
        // Blank UI control to avoid flicker
        iUiControl->SetBlank( ETrue );
        }

    iState = EDeleting;
    IssueRequest();
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::FilterErrors
// --------------------------------------------------------------------------
//
inline TInt CPbk2DeleteContactsCmd::FilterErrors( TInt aErrorCode )
	{
	PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
        ( "CPbk2DeleteContactsCmd::FilterErrors(%d) start" ), aErrorCode );

	TInt result = aErrorCode;
	switch ( aErrorCode )
		{
		case KErrNotFound:  // FALLTHROUGH
		case KErrInUse:
			{
			// If you only have one contact or it's the last contact that failed, 
			// remove the progress bar. 
			if ( iContactCount == KOneContact || 
				( ( iContactIterator ) && !( iContactIterator->HasNext() ) ) ) 
				{ 
				Cancel(); 
				if ( iDecorator )
					{
					iDecorator->ProcessStopped();
				 	}
				ProcessDismissed( aErrorCode );
				} 
			else 
				{ 
				// Ignore these errors 
				// KErrNotFound means that somebody got the contact first  
				// KErrInUse means that the contact is open
				result = KErrNone;   
				}
			CCoeEnv::Static()->HandleError( aErrorCode ); 
			
			break; 
			}

		default:  // Something more serious happened -> give up
			{
			Cancel();
			ProcessDismissed( aErrorCode );
			
			break;
			}
		}
	
	PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
		( "CPbk2DeleteContactsCmd::FilterErrors(%d) end" ), result );
	
	return result;
	}

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::IssueRequest
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::IssueRequest()
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, KErrNone );
    SetActive();
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::IssueStopRequest
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::IssueStopRequest( TProcessState aState )
    {
    iState = aState;
    if ( !IsActive() )
        {
        IssueRequest();
        }
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::ConfirmDeletionL
// --------------------------------------------------------------------------
//
void CPbk2DeleteContactsCmd::ConfirmDeletionL()
    {
    CPbk2GeneralConfirmationQuery* query =
            CPbk2GeneralConfirmationQuery::NewL();
    TInt queryResult = KErrNotFound;
    if (iContactCount > 1)
        {
        queryResult = query->ExecuteLD(
            iContactCount, R_QTN_PHOB_QUERY_DELETE_N_ENTRIES );
        }
    else
        {
        queryResult = query->ExecuteLD(
                    *iStoreContact, R_QTN_QUERY_COMMON_CONF_DELETE, MPbk2ContactNameFormatter::EPreserveAllOriginalSpaces );
            
        }

    if (queryResult)
        {
        // Continue with starting the deletion
        iState = EStarting;
        IssueRequest();
        }
    else
        {
        IssueStopRequest(ECanceling);
        }
    }

// --------------------------------------------------------------------------
// CPbk2DeleteContactsCmd::GetContactCountL
// --------------------------------------------------------------------------
//
TInt CPbk2DeleteContactsCmd::GetContactCountL()
    {
    TInt result = 0;

    if ( iContactIterator )
        {
        iContactIterator->SetToFirst();
        while( iContactIterator->HasNext() )
            {
            MVPbkContactLink* link = iContactIterator->NextL();
            if ( link && !IsFromReadOnlyStore( *link ) )
                {
                ++result;
                }
            delete link;
            link = NULL;
            }
        }
    else
        {
        if (iUiControl)
            {            
            // There was no iterator so there are no selected contacts,
            // this means we are deleting only the focused contact
            const MVPbkBaseContact* focusedContact = iUiControl->FocusedContactL();
            if ( focusedContact )
                {
                MVPbkContactLink* link = focusedContact->CreateLinkLC();
                if ( link && !IsFromReadOnlyStore( *link ) )
                    {
                    ++result;
                    }
                CleanupStack::PopAndDestroy(); // link
                }
            }
        }

    return result;
    }

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

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

    return ret;
    }

//  End of File
