/*
* 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 MMC UI extension copy to MMC command.
*
*/


#include "CPmuCopyToMmcCmd.h"

// Phonebook 2
#include <MPbk2ContactUiControl.h>
#include <MPbk2ApplicationServices.h>
#include <MPbk2AppUi.h>
#include <MPbk2ContactNameFormatter.h>
#include <MPbk2CommandObserver.h>
#include <Pbk2ProcessDecoratorFactory.h>
#include <MPbk2ProcessDecorator.h>
#include <TPbk2CopyContactsResults.h>
#include <Pbk2MmcUIRes.rsg>
#include <CPbk2DriveSpaceCheck.h>

// Virtual Phonebook
#include <MVPbkContactOperationBase.h>
#include <MVPbkStoreContact.h>
#include <CVPbkVCardEng.h>
#include <CVPbkContactManager.h>
#include <CVPbkContactLinkArray.h>

// System includes
#include <pathinfo.h>
#include <StringLoader.h>
#include <aknnotewrappers.h>

#include <AknCommonDialogsDynMem.h>
#include <CAknMemorySelectionDialogMultiDrive.h>
#include <driveinfo.h>

// Debug Header
#include <Pbk2Debug.h>

/// Unnamed namespace for local definitions
namespace {

_LIT( KPbk2VCardFileExtension, ".vcf" );
_LIT( KInvalidFileNameChars, "?*<>/\"|\\:" );
_LIT( KNumberFormat, "%d" );
_LIT( KOpeningParenthesis, "(" );
_LIT( KClosingParenthesis, ")" );
_LIT( KZero, "0" );
const TInt KOneContact = 1;

/**
 * Removes invalid characters from given descriptor.
 *
 * @param aPtr  The descriptor to inspect.
 */
void RemoveInvalidCharacters( TPtr& aPtr )
    {
    for ( TInt i = aPtr.Length() - 1; i >= 0; --i )
        {
        TChar ch = aPtr[i];
        if ( KInvalidFileNameChars().Locate( ch ) != KErrNotFound )
            {
            aPtr.Delete( i, 1 ); // ch is not a valid character
            }
        }
    }
    
enum TPmuCopyToMmcCmdState
    {
    EPmuCopyToMmcCmdStartProcess,
    EPmuCopyToMmcCmdDeleteOldEntries,
    EPmuCopyToMmcCmdRun,
    EPmuCopyToMmcCmdComplete,
    EPmuCopyToMmcCmdCancel
    };    

#ifdef _DEBUG

enum TPanicCode
    {
    EPanic_CopyNextL_OOB = 1,
    EPanic_OperationComplete_PreCond,
    EPanic_ShowResultsL_PreCond
    };

void Panic( TPanicCode aPanic )
    {
    _LIT( KPanicCat, "CPmuCopyToMmcCmd" );
    User::Panic( KPanicCat(), aPanic );
    }

#endif // _DEBUG

} /// namespace

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

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::~CPmuCopyToMmcCmd
// --------------------------------------------------------------------------
//
CPmuCopyToMmcCmd::~CPmuCopyToMmcCmd()
    {
    Cancel();
    
    if ( iUiControl )
        {
        iUiControl->RegisterCommand( NULL );
        }

    iWriteStream.Close();
    delete iFileMan;
    delete iDecorator;
    delete iStoreContact;
    delete iPreviousContact;
    delete iContactLinks;
    delete iVCardEngine;
    delete iDriveSpaceCheck;
    }

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

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::ConstructL
// --------------------------------------------------------------------------
//
inline void CPmuCopyToMmcCmd::ConstructL()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING("CPmuCopyToDriveCmd::ConstructL"));
    
    iVCardEngine = CVPbkVCardEng::NewL
        ( Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager() );

    iContactLinks = iUiControl->SelectedContactsOrFocusedContactL();

    iDecorator = Pbk2ProcessDecoratorFactory::CreateProgressDialogDecoratorL
        ( R_PMU_COPY_PROGRESS_NOTE, ETrue );
    iDecorator->SetObserver( *this );
    
    iUiControl->RegisterCommand( this );
    
    // set the default contacts path
    iContactsPath = PathInfo::MemoryCardContactsPath();
    
    iDriveSpaceCheck = CPbk2DriveSpaceCheck::NewL
            ( CCoeEnv::Static()->FsSession() );
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::RunL
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::RunL()
    {
    switch( iState )
        {
        case EPmuCopyToMmcCmdStartProcess:
            {
            iDecorator->ProcessStartedL(iContactLinks->Count());                        
            
            if ( iDeleteOldEntries )
                {
                iState = EPmuCopyToMmcCmdDeleteOldEntries;
                IssueRequest();
                }
            else
                {
                Phonebook2::Pbk2AppUi()->ApplicationServices().
                    ContactManager().FsSession().MkDirAll
                        ( iContactsPath );
                
                iState = EPmuCopyToMmcCmdRun;
                IssueRequest();
                }
            break;
            }
        case EPmuCopyToMmcCmdDeleteOldEntries:
            {
            DeleteOldEntriesL();
            Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager().
                FsSession().MkDirAll( iContactsPath );
            break;
            }        
        case EPmuCopyToMmcCmdRun:
            {                        
            if (iFileMan)
                {
                delete iFileMan;
                iFileMan = NULL;
                }

            if (iStoreContact)
                {
                HBufC* fileName = CreateFileNameLC(*iStoreContact);
                iWriteStream.Create( Phonebook2::Pbk2AppUi()->
                    ApplicationServices().ContactManager().FsSession(),
                        *fileName, EFileWrite );
                iExportOperation = iVCardEngine->ExportVCardForSyncL(
                    iWriteStream, *iStoreContact, *this );
                CleanupStack::PopAndDestroy(fileName);
                }
            else if (iCurrentIndex < iContactLinks->Count())
                {
                TRAPD( err, CopyNextL() );
                if( err != KErrNone )
                    User::LeaveIfError( HandleError( err ) );
                }
            else
                {
                iState = EPmuCopyToMmcCmdComplete;
                IssueRequest();
                }
            break;
            }
        case EPmuCopyToMmcCmdComplete:
            {
            // Copy complete, decorator calls processdismissed
            iDecorator->ProcessStopped();
            break;
            }
        default:
            {            
            }
        }    
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::DoCancel
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::DoCancel()
    {
    iState = EPmuCopyToMmcCmdCancel;
    
    delete iRetrieveOperation;
    iRetrieveOperation = NULL;
    delete iExportOperation;
    iExportOperation = NULL;
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::RunError
// --------------------------------------------------------------------------
//
TInt CPmuCopyToMmcCmd::RunError( TInt aError )
    {
    return HandleError( aError );
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::ExecuteLD
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::ExecuteLD()
    {
    CleanupStack::PushL( this );

    // Show memory selection dialog if multiple drives available.
    if ( ShowMemorySelectionDialogL() )
        {
        // User selected a drive
        ShowConfirmationQueryL();
        }
    else // If user cancelled the selection
        {
        iCommandObserver->CommandFinished( *this );
        }
    CleanupStack::Pop( this );
    }

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

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

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::ProcessDismissed
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::ProcessDismissed( TInt /*aCancelCode*/ )
    {
    Cancel();
    delete iRetrieveOperation;
    iRetrieveOperation = NULL;
    delete iExportOperation;
    iExportOperation = NULL;

    // It is a not big deal if result note is not shown to user
    TRAP_IGNORE( ShowResultsL() );
    if ( iUiControl )
        {
        iUiControl->UpdateAfterCommandExecution();
        } 
    iCommandObserver->CommandFinished( *this );
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::VPbkSingleContactOperationComplete
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::VPbkSingleContactOperationComplete
        ( MVPbkContactOperationBase& aOperation,
          MVPbkStoreContact* aContact )
    {
    __ASSERT_DEBUG( iDecorator, Panic( EPanic_OperationComplete_PreCond ));
    
    if ( &aOperation == iRetrieveOperation )
        {
        delete iRetrieveOperation;
        iRetrieveOperation = NULL;

        iStoreContact = aContact;
        }
    else if ( &aOperation == iExportOperation )
        {
        delete iExportOperation;
        iExportOperation = NULL;

        // After the copying take the contact pointer,
        // because if this was last contact we need it in result note
        delete iPreviousContact;
        iPreviousContact = iStoreContact; // iPreviousContact takes ownership
        iStoreContact = NULL;

        iWriteStream.Close();
        
        // Copy next and update progressbar
        ++iCurrentIndex;
        ++iCountOfContacts;
        iDecorator->ProcessAdvance( 1 );
        }
    IssueRequest();        
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::VPbkSingleContactOperationFailed
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::VPbkSingleContactOperationFailed
        ( MVPbkContactOperationBase& aOperation, TInt /*aError*/ )
    {
    if (&aOperation == iRetrieveOperation)
        {
        delete iRetrieveOperation;
        iRetrieveOperation = NULL;
        }
    else if (&aOperation == iExportOperation)
        {
        delete iExportOperation;
        iExportOperation = NULL;
        delete iStoreContact;
        iStoreContact = NULL;
        }
    
    // Copy next    
    ++iCurrentIndex;
    IssueRequest();        
    }
    
// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::NotifyFileManStarted
// --------------------------------------------------------------------------
//    
MFileManObserver::TControl CPmuCopyToMmcCmd::NotifyFileManStarted()
    {
    if ( iState == EPmuCopyToMmcCmdCancel )
        {
        return MFileManObserver::EAbort;
        }
    return MFileManObserver::EContinue;
    }
    
// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::NotifyFileManOperation
// --------------------------------------------------------------------------
//
MFileManObserver::TControl CPmuCopyToMmcCmd::NotifyFileManOperation()
    {
    if ( iState == EPmuCopyToMmcCmdCancel )
        {
        return MFileManObserver::EAbort;
        }
    return MFileManObserver::EContinue;
    }    

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::CopyNextL
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::CopyNextL()
    {
    __ASSERT_DEBUG( iContactLinks->Count() > iCurrentIndex,
         Panic( EPanic_CopyNextL_OOB ) );
    
    //Check free storage space
    iDriveSpaceCheck->SetStore( iContactsPath );
    iDriveSpaceCheck->DriveSpaceCheckL();
    
    iRetrieveOperation = Phonebook2::Pbk2AppUi()->ApplicationServices().
        ContactManager().RetrieveContactL
            ( iContactLinks->At( iCurrentIndex ), *this );
    }

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

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::CreateFileNameLC
// --------------------------------------------------------------------------
//
HBufC* CPmuCopyToMmcCmd::CreateFileNameLC
        ( const MVPbkStoreContact& aContact ) const
    {
    HBufC* title = Phonebook2::Pbk2AppUi()->ApplicationServices().
        NameFormatter().GetContactTitleL( aContact.Fields(), 0 );

    CleanupStack::PushL(title);
    TPtr titlePtr = title->Des();

    RemoveInvalidCharacters(titlePtr);

    TBool fileNameFound = EFalse;
    TInt number = 0;
    TFileName fileName;
    while (!fileNameFound)
        {
        fileName.Zero();
        fileName.Append( iContactsPath );
        fileName.Append(titlePtr);

        if (number > 0)
            {
            fileName.Append(KOpeningParenthesis);
            if (number < 10)
                {
                fileName.Append(KZero);
                }
            fileName.AppendFormat(KNumberFormat, number);
            fileName.Append(KClosingParenthesis);
            }

        fileName.Append(KPbk2VCardFileExtension);

        TEntry entry;
        TInt err = Phonebook2::Pbk2AppUi()->ApplicationServices().
            ContactManager().FsSession().Entry(fileName, entry);
        if (err == KErrNotFound)
            {
            fileNameFound = ETrue;
            }
        else
            {
            User::LeaveIfError( err );
            ++number;
            }
        }
    CleanupStack::PopAndDestroy(title);

    return fileName.AllocLC();
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::DeleteOldEntriesL
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::DeleteOldEntriesL()
    {        
    iFileMan = CFileMan::NewL( Phonebook2::Pbk2AppUi()->
        ApplicationServices().ContactManager().FsSession(), this );
    User::LeaveIfError( iFileMan->Delete( 
            iContactsPath,
            CFileMan::ERecurse, iStatus ) );
    iState = EPmuCopyToMmcCmdRun;            
    SetActive();    
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::ShowConfirmationQueryL
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::ShowConfirmationQueryL()
    {
    HBufC* prompt = StringLoader::LoadLC(R_QTN_PHOB_QUERY_REMOVE_ALL_CONTACTS);
    CAknQueryDialog* dlg = CAknQueryDialog::NewL();

    TInt result = dlg->ExecuteLD
        (R_PHONEBOOK2_CONFIRM_DELETE_EXISTING_CONTACTS, *prompt);
    CleanupStack::PopAndDestroy(prompt);
    iDeleteOldEntries = (result != 0);
        
    iState = EPmuCopyToMmcCmdStartProcess;
    IssueRequest();
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::ShowMemorySelectionDialogL
// --------------------------------------------------------------------------
//
TBool CPmuCopyToMmcCmd::ShowMemorySelectionDialogL()
    {
    TBool driveSelected( EFalse );

    TDriveNumber selectedMem;
    CAknMemorySelectionDialogMultiDrive* dlg =
            CAknMemorySelectionDialogMultiDrive::NewL(
                    ECFDDialogTypeSelect,
                    R_PHONEBOOK2_MEMORY_SELECTION_DIALOG,
                    EFalse,
                    AknCommonDialogsDynMem::EMemoryTypeMMCExternal |
                    AknCommonDialogsDynMem::EMemoryTypeInternalMassStorage );
    CleanupStack::PushL( dlg );
    driveSelected = dlg->ExecuteL( selectedMem );
    CleanupStack::PopAndDestroy(); // dlg

    // If user selected a drive
    if ( driveSelected )
        {
        TInt err = PathInfo::GetFullPath( iContactsPath, 
                                           selectedMem,
                                           PathInfo::EMemoryCardContactsPath );
        User::LeaveIfError( err );
        }

    return driveSelected;
    }

// --------------------------------------------------------------------------
// CPmuCopyToMmcCmd::ShowResultsL
// --------------------------------------------------------------------------
//
void CPmuCopyToMmcCmd::ShowResultsL()
    {        
    const TInt contactCount = iContactLinks->Count();
    if (contactCount == KOneContact)
        {
        HBufC* title = NULL;

        // If we do not have iPreviousContact, then probably coping is failed
        if ( iPreviousContact )
            {
            title = Phonebook2::Pbk2AppUi()->ApplicationServices().
                NameFormatter().GetContactTitleL( iPreviousContact->Fields(),
                    MPbk2ContactNameFormatter::EPreserveLeadingSpaces);
            CleanupStack::PushL(title);
            }
        else if ( iStoreContact )
            {
            // If we still have iStoreContact, then retrieve is atleast 
            // completed and we can show note.
            title = Phonebook2::Pbk2AppUi()->ApplicationServices().
                NameFormatter().GetContactTitleL( iStoreContact->Fields(),
                    MPbk2ContactNameFormatter::EPreserveLeadingSpaces );
            CleanupStack::PushL(title);
            }
            
        if ( title )
            {
            TPbk2CopyContactsResults results( iCountOfContacts, *title );
            results.SetOneContactCopiedTextRes
                ( R_QTN_PBCOP_NOTE_CONTACT_COPIED_PB2 );
            results.SetOneContactNotCopiedTextRes
                ( R_QTN_PBCOP_NOTE_ENTRY_NOT_COPIED );
            results.ShowNoteL();

            CleanupStack::PopAndDestroy( title );
            }                            
        }
    else
        {
        TPbk2CopyContactsResults results( 
            iCountOfContacts, iContactLinks->Count());
        results.ShowNoteL();
        }
    }

TInt CPmuCopyToMmcCmd::HandleError( TInt aError )
    {
    TInt err = aError;
    
    delete iRetrieveOperation;
    iRetrieveOperation = NULL;
    delete iExportOperation;
    iExportOperation = NULL;
       
    // if there are already copied contacts, dont show error
    if( iCountOfContacts )
        err = KErrNone;
    
    if ( iDecorator )
        {
        iDecorator->ProcessStopped();
        }
    
    return err;
    }

// End of File
