/*
* 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:  Phonebook2 contact editor dialog.
*
*/


// INCLUDE FILES
#include <CPbk2ContactEditorDlg.h>

// Phonebook 2
#include "CPbk2ContactEditorDlgImpl.h"
#include "Pbk2ContactEditorStrategyFactory.h"
#include "MPbk2ContactEditorStrategy.h"
#include <CPbk2ContactRelocator.h>
#include <CPbk2FieldPropertyArray.h>
#include <MPbk2ContactEditorEventObserver.h>
#include <MPbk2EditedContactObserver.h>
#include <CPbk2PresentationContact.h>
#include <CPbk2StoreSpecificFieldPropertyArray.h>
#include <CPbk2PresentationContactFieldCollection.h>
#include <MPbk2FieldProperty.h>
#include <MPbk2ApplicationServices.h>
#include <MPbk2AppUi.h>

// Virtual Phonebook
#include <CVPbkContactManager.h>
#include <MVPbkStoreContact.h>
#include <MVPbkContactStore.h>
#include <MVPbkContactFieldTextData.h>
#include <MVPbkContactStoreProperties.h>
#include <MVPbkFieldType.h>
#include <barsread.h>


// System includes
#include <coemain.h>

/// Unnamed namespace for local definitions
namespace {

#ifdef _DEBUG
enum TPanicCode
    {
    ENullPointer,
    EPanicPreCond_ResetWhenDestroyed
    };

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

} /// namespace

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::CPbk2ContactEditorDlg
// --------------------------------------------------------------------------
//
CPbk2ContactEditorDlg::CPbk2ContactEditorDlg
    ( TPbk2ContactEditorParams& aParams,
      MPbk2EditedContactObserver& aContactObserver,
      MPbk2ApplicationServices* aAppServices,
      TInt aEditorFieldsId ) :
        iParams( aParams ),
        iContactObserver( aContactObserver ),
        iAddItemFieldTypeResourceId( KErrNotFound ),
        iAppServices( aAppServices ),
        iEditorFieldsId( aEditorFieldsId )
    {
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::~CPbk2ContactEditorDlg
// --------------------------------------------------------------------------
//
CPbk2ContactEditorDlg::~CPbk2ContactEditorDlg()
    {
    ForceExit();
    
	delete iTitleText;
    if ( iSelfPtr )
        {
        *iSelfPtr = NULL;
        }
    if ( iDestroyedPtr )
        {
        *iDestroyedPtr = ETrue;
        }
    delete iContact;
    delete iSpecificFieldProperties;
    delete iFieldProperties;
    delete iEditorStrategy;
    delete iContactRelocator;
    delete iStoreContact;
    delete iAddItemFieldTypeXspName;       
    }


// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2ContactEditorDlg* CPbk2ContactEditorDlg::NewL(
        TPbk2ContactEditorParams& aParams, MVPbkStoreContact* aContact,
        MPbk2EditedContactObserver& aContactObserver)
    {
    CPbk2ContactEditorDlg* self =
        new(ELeave) CPbk2ContactEditorDlg(aParams, aContactObserver);
    CleanupStack::PushL(self);
    self->ConstructL( aContact );
    CleanupStack::Pop(self);
    return self;
    }


// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2ContactEditorDlg* CPbk2ContactEditorDlg::NewL(
        TPbk2ContactEditorParams& aParams, 
        MVPbkStoreContact* aContact,
        MPbk2EditedContactObserver& aContactObserver,
        MPbk2ApplicationServices* aAppServices,
        HBufC* aTitleText,
        TInt aEditorFieldsId )
    {
    CPbk2ContactEditorDlg* self =
        new(ELeave) CPbk2ContactEditorDlg(aParams, aContactObserver,aAppServices,aEditorFieldsId );
    CleanupStack::PushL(self);
    self->ConstructL( aContact );
	self->iTitleText = aTitleText;
    CleanupStack::Pop(self);
    return self;
    }


// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ConstructL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ConstructL( MVPbkStoreContact* aStoreContact )
    {
    CreatePresentationContactL( *aStoreContact );

    iEditorStrategy =
        Pbk2ContactEditorStrategyFactory::CreateL
            (iParams, iContact);

    // Take ownership of the contact after all leaving initialization has
    // been done.
    iStoreContact = aStoreContact;
    }

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

    TBool destroyed = EFalse;
    iDestroyedPtr = &destroyed;

    // Implementation dialog must be run again e.g when relocation
    // is done due to item addition.
    do
        {
        // Reset state
        iRelocationState.ClearAll();

        iImplementation = CPbk2ContactEditorDlgImpl::NewL
            ( iParams, *iContact, *iFieldProperties, *this,
              *iEditorStrategy, *this, iAppServices, iTitleText ); 
        // ownership is passed
        iTitleText = NULL;
        iEliminator = iImplementation;
        iImplementation->ResetWhenDestroyed( &iEliminator );
        iImplementation->ExecuteLD();
        } while ( ContinueAfterRelocation() );

    if ( destroyed )
        {
        CleanupStack::Pop( this );
        }
    else
        {
        CleanupStack::PopAndDestroy( this );
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::RequestExitL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::RequestExitL( TInt aCommandId )
    {
    __ASSERT_DEBUG( iEliminator, Panic( ENullPointer ) );
    iEliminator->RequestExitL( aCommandId );
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ForceExit
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ForceExit()
    {
    if ( iEliminator )
        {
        iEliminator->ForceExit();
        }
    // The contact relocator should be destructed 
    // if the edit dialog has been destructed
    if ( iContactRelocator )
        {   	
        iRelocationState.ClearAll();
        delete iContactRelocator;
        iContactRelocator = NULL;
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ResetWhenDestroyed
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ResetWhenDestroyed(
        MPbk2DialogEliminator** aSelfPtr )
    {
    __ASSERT_DEBUG(!aSelfPtr || *aSelfPtr == this,
        Panic(EPanicPreCond_ResetWhenDestroyed));
    iSelfPtr = aSelfPtr;  
    }
    
// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContactRelocatedL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ContactRelocatedL
        (MVPbkStoreContact* aRelocatedContact)
    {
    iStoreContact = aRelocatedContact; // take ownership
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContactRelocationFailed
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ContactRelocationFailed
        ( TInt aReason, MVPbkStoreContact* aContact )
    {
    // Take the contact back
    iStoreContact = aContact;
    iAddItemFieldTypeResourceId = KErrNotFound;
    iAddItemFieldTypeXspName = NULL;

    if ( aReason != KErrCancel )
        {
        // Reset state
        iRelocationState.Set( ERelocationFailed );
        // No error note is to be shown to the user when she
        // manually cancels the relocation process, therefore
        // the error code must be converted
        CCoeEnv::Static()->HandleError( aReason );
        }
    else
        {
        // Relocation cancelled
        iRelocationState.Set( ERelocationCancelled );
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContactsRelocationFailed
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ContactsRelocationFailed
        ( TInt /*aReason*/, CVPbkContactLinkArray* /*aContacts*/ )
    {
    // Not called in editor case
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::RelocationProcessComplete
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::RelocationProcessComplete()
    {
    TRAPD( res, HandleRelocationProcessCompleteL() );
    if ( res != KErrNone )
        {
        // Shows system messages
        CCoeEnv::Static()->HandleError( res );
        // If application is not closed then at least destroy
        // the dialog
        iRelocationState.ClearAll();
        iRelocationState.Set( ERelocationFailed );
        delete iImplementation;
        iImplementation = NULL;
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::RelocateContactL
// --------------------------------------------------------------------------
//
TBool CPbk2ContactEditorDlg::RelocateContactL(
        TPbk2RelocationQyeryPolicy aQueryPolicy )
    {
    TBool result = RelocateContactL( KErrNotFound, KNullDesC, aQueryPolicy );
    // Clear the add item state because this is called in other cases than
    // relocation due to add item.
    iRelocationState.Clear( ERelocationDueToItemAddition );
    
    return result;
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::RelocateContactL
// --------------------------------------------------------------------------
//
TBool CPbk2ContactEditorDlg::RelocateContactL( 
        TInt aAddItemFieldTypeResourceId,
        const TDesC& aAddItemXspName,
        TPbk2RelocationQyeryPolicy aQueryPolicy )
    {
    iRelocationState.ClearAll();
    delete iContactRelocator;
    iContactRelocator = NULL;
    iContactRelocator = CPbk2ContactRelocator::NewL();

    TBool result = EFalse;
    if (iContactRelocator->IsPhoneMemoryInConfigurationL())
        {
        CPbk2ContactRelocator::TFlags flags =
            CPbk2ContactRelocator::EPbk2RelocatorExistingContact;
        if ( iParams.iFlags & TPbk2ContactEditorParams::ENewContact )
            {
            flags = CPbk2ContactRelocator::EPbk2RelocatorNewContact;
            }

        result = iContactRelocator->RelocateAndLockContactL(
            iStoreContact, *this,
            aQueryPolicy,
            flags );
        iStoreContact = NULL; // relocator took away ownership
        iAddItemFieldTypeResourceId = aAddItemFieldTypeResourceId;
        if (aAddItemXspName != KNullDesC())
            {
            delete iAddItemFieldTypeXspName;
            iAddItemFieldTypeXspName = NULL;
            iAddItemFieldTypeXspName = aAddItemXspName.AllocL();
            }
        // else not needed because iAddItemFieldTypeXspName is NULL

        /// Dialog must be started again if relocation is done due to
        /// item addition.
        if ( iContactRelocator )
            {
            iRelocationState.Set( ERelocationDueToItemAddition );
            }
        }
    return result;        
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::IsPhoneMemoryInConfigurationL
// --------------------------------------------------------------------------
//
TBool CPbk2ContactEditorDlg::IsPhoneMemoryInConfigurationL()
    {
    CPbk2ContactRelocator* contactRelocator = CPbk2ContactRelocator::NewL();
    CleanupStack::PushL( contactRelocator );
    TBool ret = contactRelocator->IsPhoneMemoryInConfigurationL();
    CleanupStack::PopAndDestroy( contactRelocator );
    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::IsTemplateFieldL
// --------------------------------------------------------------------------
//
TBool CPbk2ContactEditorDlg::IsTemplateFieldL( TInt aResId,
        const TDesC& aXSpName )
    {
    TBool result( EFalse );
    const MVPbkFieldType* fieldType =
        Phonebook2::Pbk2AppUi()->ApplicationServices().ContactManager().
            FieldTypes().Find( aResId );
    if ( fieldType )
        {
        const MPbk2FieldProperty* fieldProp =
            iFieldProperties->FindProperty( *fieldType, aXSpName );
        if ( fieldProp
            && ( fieldProp->Flags() & KPbk2FieldFlagTemplateField ) )
            {
            result = ETrue;
            }
        }
    return result;
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::EditorReadyL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::EditorReadyL()
    {
    // Editor is ready
    if (iAddItemFieldTypeResourceId != KErrNotFound)
        {
        TPtrC xSpName = iAddItemFieldTypeXspName ? *iAddItemFieldTypeXspName :
                KNullDesC();
        // If new field is template field in relocated contact,
        // do not add new field. Just focus the field in contact.
        if ( IsTemplateFieldL( iAddItemFieldTypeResourceId, xSpName ) )
            {
            iImplementation->TryChangeFocusWithTypeIdL
                ( iAddItemFieldTypeResourceId, xSpName );
            }
        else
            {
            // Lets run the add item dialog silently
            iImplementation->AddItemToContactL
                ( iAddItemFieldTypeResourceId, xSpName );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContactEditingComplete
//
// aEditedContact is commented because it is called from the
// CPbk2ContactEditorDlgImpl who actually don't own it. So against
// observer documentation ownership is not changed. This is
// an implementation detail of
// CPbk2ContactEditorDlg <-> CPbk2ContactEditorDlgImpl
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ContactEditingComplete(
        MVPbkStoreContact* /*aEditedContact*/ )
    {
    // Don't inform observer if the dialog need to be run again
    if ( !ContinueAfterRelocation() )
        {
        // Give ownership of the iStoreContact to the observer
        MVPbkStoreContact* contact = iStoreContact;
        iStoreContact = NULL;
        iContactObserver.ContactEditingComplete( contact );
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContactEditingDeletedContact
//
// aEditedContact is commented because it is called from the
// CPbk2ContactEditorDlgImpl who actually don't own it. So against
// observer documentation ownership is not changed. This is
// an implementation detail of
// CPbk2ContactEditorDlg <-> CPbk2ContactEditorDlgImpl
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ContactEditingDeletedContact(
    MVPbkStoreContact* /*aEditedContact*/ )
    {
    // Don't inform observer if the dialog need to be run again
    if ( !ContinueAfterRelocation() )
        {
        // Give ownership of the iStoreContact to the observer
        MVPbkStoreContact* contact = iStoreContact;
        iStoreContact = NULL;
        iContactObserver.ContactEditingDeletedContact( contact );
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContactEditingAborted
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::ContactEditingAborted()
    {
    // Forward from implementation dialog to the actual client
    iContactObserver.ContactEditingAborted();
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::HandleRelocationProcessCompleteL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::HandleRelocationProcessCompleteL()
    {
    if ( !iRelocationState.IsSet( ERelocationCancelled ) &&
         !iRelocationState.IsSet( ERelocationFailed ) )
        {
        // Update contact
        CreatePresentationContactL();

        // Contact has been relocated which means that the state changes
        // from creating a new contact to editing an relocated contact.

        // Remove the new contact flag...
        iParams.iFlags &=  ~TPbk2ContactEditorParams::ENewContact;
        // ... and set the editing flag
        iParams.iFlags |= TPbk2ContactEditorParams::EModified;
        // Update strategy
        delete iEditorStrategy;
        iEditorStrategy = NULL;
        iEditorStrategy = Pbk2ContactEditorStrategyFactory::CreateL
            ( iParams, iContact );

        // Set iFocusedContactField to NULL so that
        // the right field (new field) will be focused
        iParams.iFocusedContactField = NULL;

        TBool informObserver = EFalse;
        if ( !iRelocationState.IsSet( ERelocationDueToItemAddition ) )
            {
            informObserver = ETrue;
            }
        // Delete current implementation dialog nicely and create
        // a new dialog in ExecuteLD if needed.        
        iImplementation->CloseWithoutSaving( informObserver );
        }
    else
        {
        // Reset state if dialog is not closed -> continued with old contact
        iRelocationState.ClearAll();
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::CreatePresentationContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::CreatePresentationContactL()
    {
    CreatePresentationContactL( *iStoreContact );
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::CreatePresentationContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactEditorDlg::CreatePresentationContactL(
        MVPbkStoreContact& aStoreContact)
    {
    delete iSpecificFieldProperties;
    iSpecificFieldProperties = NULL;
    delete iFieldProperties;
    iFieldProperties = NULL;
    
    const MVPbkFieldTypeList& supportedFieldTypes =
        aStoreContact.ParentStore().StoreProperties().SupportedFields();

    //If custom fields are provided (social phonebook)
    if( iEditorFieldsId != KErrNotFound )
    	{
    	HBufC8* res = CCoeEnv::Static()->AllocReadResourceAsDes8L( iEditorFieldsId );
    	CleanupStack::PushL( res );
    	TResourceReader reader;
    	reader.SetBuffer( res );
    	// Create a field property list of the provided fields on reader
		iFieldProperties = CPbk2FieldPropertyArray::NewL
		   ( supportedFieldTypes, reader, CCoeEnv::Static()->FsSession() );
		CleanupStack::PopAndDestroy( res );
		}
    else
    	{
    	// Create a field property list of the supported
		// field types of the used store
		iFieldProperties = CPbk2FieldPropertyArray::NewL
			( supportedFieldTypes, CCoeEnv::Static()->FsSession() );
    	}
    
    // if iAppServices is set. This is used if EditorDlg is used outside of Pbk2 context
    if( iAppServices )
    	{
    	iSpecificFieldProperties = CPbk2StoreSpecificFieldPropertyArray::NewL
			( *iFieldProperties,
			  iAppServices->StoreProperties(),
			  supportedFieldTypes, aStoreContact.ParentStore() );
    	}
    else
    	{
    	iSpecificFieldProperties = CPbk2StoreSpecificFieldPropertyArray::NewL
			( *iFieldProperties,
			  Phonebook2::Pbk2AppUi()->ApplicationServices().StoreProperties(),
			  supportedFieldTypes, aStoreContact.ParentStore() );
    	}

    delete iContact;
    iContact = NULL;
    iContact = CPbk2PresentationContact::NewL
        ( aStoreContact, *iSpecificFieldProperties );
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ContinueAfterRelocation
// --------------------------------------------------------------------------
//
TBool CPbk2ContactEditorDlg::ContinueAfterRelocation()
    {
    TBool ret = EFalse;

    // Currently 4 ways to continue after relocation:
    // 1) User cancels relocation -> editor stays open with the OLD contact.
    // 2) Relocation done due to add item and user approves
    //    relocation -> editor stays open with the NEW contact
    // 3) Relocation done due to other reason than add item.
    //    User approves relocation -> editor is closed.
    // 4) Error case

    if ( iRelocationState.IsSet( ERelocationDueToItemAddition ) &&
         !iRelocationState.IsSet( ERelocationCancelled ) &&
         !iRelocationState.IsSet( ERelocationFailed )  )
        {
        // Continue only in case 2
        ret = ETrue;
        }

    return ret;
    }

// End of File
