/*
* Copyright (c) 2006-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:  Exporting the contact field from contact to 
*                CParserProperty field
*
*/


#include "CVPbkVCardPropertyExporter.h"
#include "CVPbkVCardParserParamArray.h"
#include "CVPbkVCardFieldTypeProperty.h"
#include "CVPbkVCardIdNameMapping.h"
#include "VPbkVCardEngError.h"
#include "CVPbkVCardData.h"
#include <MVPbkContactStore.h>
#include <MVPbkBaseContactField.h>
#include <MVPbkStoreContact.h>
#include <MVPbkStoreContactFieldCollection.h>
#include <TVPbkFieldVersitProperty.h>
#include <MVPbkContactFieldTextData.h>
#include <MVPbkContactFieldUriData.h>
#include <MVPbkContactFieldDateTimeData.h>
#include <MVPbkContactFieldBinaryData.h>
#include <VPbkError.h>
#include <MVPbkSingleContactOperationObserver.h>
#include <CVPbkContactManager.h>
#include <MVPbkContactAttributeManager.h>
#include <CVPbkDefaultAttribute.h>
#include <CVPbkLocalVariationManager.h>

// System includes
#include <vcard.h>
#include <bldvariant.hrh>

// Debug includes
#include <VPbkDebug.h>

#include <centralrepository.h> 

// CONSTANTS
const TInt KNsmlDsGeoField = 0x0000;  //  defined in DS cenrep
const TUid KCRUidGeoFieldInternalKeys = { 0x20022EE1 };  // 0x20022EE1 defined in DS cenrep

_LIT8(KGEO, "GEO");

namespace
    {
    _LIT8(KPropEncoding, "ENCODING");
    _LIT8(KPropBase64, "BASE64");
    _LIT8(KPropType, "TYPE");
    _LIT(KFormatJPEG, "JPEG");
    _LIT8(KPropPREF, "PREF");
    const TInt KOne = 1;
    }

CVPbkVCardPropertyExporter* CVPbkVCardPropertyExporter::NewLC(
            CVPbkVCardData& aData,
            const MVPbkStoreContact* aSourceItem,
            RWriteStream& aDestStream,
            MVPbkSingleContactOperationObserver& aObserver,
            const TBool aBeamed)
    {
    CVPbkVCardPropertyExporter* self =
        new (ELeave) CVPbkVCardPropertyExporter(
            aData,
            aSourceItem,
            aDestStream,
            aObserver,
            aBeamed);
    CleanupStack::PushL( self );
    self->ConstructL(aData);
    return self;
    }

CVPbkVCardPropertyExporter::CVPbkVCardPropertyExporter(
            CVPbkVCardData& aData,
            const MVPbkStoreContact* aSourceItem,
            RWriteStream& aDestStream,
            MVPbkSingleContactOperationObserver& aObserver,
            const TBool aBeamed)
    :   CVPbkVCardActiveIncremental( EPriorityStandard,
                                     *this,
                                     aData,
                                     aObserver),
        iContact ( aSourceItem ),
        iDestinationStream( aDestStream ),
        iCurrentPropertyIndex( 0 ),
        iMaxPropertyCount( aData.Properties().Count() ),
        iState( EExporting ),
        iMaskedFieldsArray(aData.GetMaskedFields()),
        iBeamed(aBeamed)
    {
    }

CVPbkVCardPropertyExporter* CVPbkVCardPropertyExporter::NewLC(
            CVPbkVCardData& aData,
            RWriteStream& aDestStream,
            MVPbkSingleContactOperationObserver& aObserver,
            const TBool aBeamed)
    {
    CVPbkVCardPropertyExporter* self =
        new (ELeave) CVPbkVCardPropertyExporter(
            aData,
            aDestStream,
            aObserver,
            aBeamed);
    CleanupStack::PushL( self );
    self->ConstructL(aData);
    return self;
    }

CVPbkVCardPropertyExporter::CVPbkVCardPropertyExporter(
            CVPbkVCardData& aData,
            RWriteStream& aDestStream,
            MVPbkSingleContactOperationObserver& aObserver,
            const TBool aBeamed)
    :   CVPbkVCardActiveIncremental( EPriorityStandard,
                                     *this,
                                     aData,
                                     aObserver),
        iDestinationStream( aDestStream ),
        iCurrentPropertyIndex( 0 ),
        iMaxPropertyCount( aData.Properties().Count() ),
        iState( ECreatingRetriever ),
        iMaskedFieldsArray(aData.GetMaskedFields()),
        iBeamed(aBeamed)
    {
    }

CVPbkVCardPropertyExporter::~CVPbkVCardPropertyExporter()
    {
    iValues.ResetAndDestroy();
    delete iOwnedContact;
    delete iOperation;
    delete iParser;
    delete iOwncardHandler;
    delete iGroupcardHandler;
    }

void CVPbkVCardPropertyExporter::ExportFieldL( CVPbkVCardFieldTypeProperty& aVCardProperty )
    {
    iVCardProperty = &aVCardProperty;
    
   if ( iVCardProperty->Name().Compare(KGEO) == 0 )
   {
   	   	        // Read from cenrep	
	      CRepository* repository = CRepository::NewLC( KCRUidGeoFieldInternalKeys );
	      TBool geoFeatureValue(1); 
	      // Get the geo feature value..
	      TInt err = repository->Get( KNsmlDsGeoField, geoFeatureValue );
	      User::LeaveIfError(err);
	      CleanupStack::PopAndDestroy(repository);
	      if (!geoFeatureValue)
	        {
	          return; //no Need to add GEO field during Sync/BT
	        }
   }  
    
    // If the current operation is a beamed export
    if (iBeamed)
    {
        TInt count(iMaskedFieldsArray ? iMaskedFieldsArray->Count() : 0);
        TInt val(-1);
        for (TInt i(0); i < count; i++)
        {
            val = iVCardProperty->Name().Find( (*iMaskedFieldsArray)[i] );
            if (0 == val)
            {
                // This is a masked field
                return;
            }
        }
    }
    
    GetValuesFromIndexedPropertiesL( iVCardProperty->Count() == KOne );
    }

void CVPbkVCardPropertyExporter::GetValuesFromIndexedPropertiesL(
    TBool aSingleItem )
    {
    CVPbkVCardParserParamArray* params = NULL;
    TInt findIndex( 0 );

    if ( aSingleItem )
        {
        TBool doContinue( ETrue );
        const TVPbkVCardIndexedProperty& indexedProperty =
            iVCardProperty->IndexedPropertyAt( 0 );

        while( doContinue )
            {
            InitDataL();
            params =
                new (ELeave) CVPbkVCardParserParamArray( KOne );
            CleanupStack::PushL( params );

            doContinue = GetSingleValueAndParamsL( indexedProperty,
                                                   *params,
                                                   findIndex );
            ++findIndex;
            CleanupStack::Pop( params );
            // The params is pushed straight back to the CleanupStack in AddPropertyParserL
            // unless AddPropertyToParserL returns EFalse
            if( !AddPropertyToParserL( iValues, params ) )
                {
                delete params;
                params = NULL;
                }
            }
        }
    else
        {
        TInt indexedPropertyCount( iVCardProperty->Count() );
        InitDataL();
        params =
            new (ELeave) CVPbkVCardParserParamArray( indexedPropertyCount );
        CleanupStack::PushL( params );

        for ( TInt i = 0; i < indexedPropertyCount; ++i )
            {
            const TVPbkVCardIndexedProperty& indexedProperty =
                iVCardProperty->IndexedPropertyAt( i );
            findIndex = 0;
            GetSingleValueAndParamsL( indexedProperty, *params, findIndex );
            }

        CleanupStack::Pop( params );
        // The params are pushed straight back to the CleanupStack in 
        // AddPropertyParserL unless AddPropertyToParserL returns EFalse
        if( !AddPropertyToParserL( iValues, params ) )
            {
            delete params;
            params = NULL;
            }
        }
    }

TBool CVPbkVCardPropertyExporter::GetSingleValueAndParamsL(
    const TVPbkVCardIndexedProperty& aIndexedProperty,
    CVPbkVCardParserParamArray& aParamArray,
    TInt& aFindIndex )
    {
    MVPbkStoreContactField* field =
        FindFieldFromContact( aIndexedProperty, aFindIndex );
    if( !field )
        {
        return EFalse;
        }
    // Fetch field value and additional params
    CParserPropertyValue* value = FetchDataFromFieldL( *field, aParamArray );
    if( !value )
        {
        return ETrue;
        }
    CleanupStack::PushL( value );
    TInt index ( aIndexedProperty.Index() );
    iValues[ index ] = value;
    CleanupStack::Pop( value );

    return ETrue;
    }

void CVPbkVCardPropertyExporter::ConstructL(CVPbkVCardData& aData)
    {
    iParser = CParserVCard::NewL();
    
    iOwncardHandler = CVPbkOwnCardHandler::NewL(aData);
    iGroupcardHandler = CVPbkGroupCardHandler::NewL(aData);
    }

MVPbkStoreContactField*
CVPbkVCardPropertyExporter::FindFieldFromContact(
    const TVPbkVCardIndexedProperty& aIndexedProperty, TInt& aIndex )
    {
    const MVPbkFieldType* exportPropertyType =
        aIndexedProperty.FindMatch( iData.SupportedFieldTypes());
    
    TInt fieldCount = iContact->Fields().FieldCount();
    for ( TInt n = aIndex; n < fieldCount; ++n )
        {
        // We have to find out additional properties of the contact field so
        // we need non-const reference of the field class.
        MVPbkStoreContactField& sourceField =
            const_cast<MVPbkStoreContact*>( iContact )->Fields().FieldAt( n );

        const TInt maxMatchPriority = 
                    iData.SupportedFieldTypes().MaxMatchPriority();
        for ( TInt matchPriority = 0;
             matchPriority <= maxMatchPriority;
             ++matchPriority )
            {
            const MVPbkFieldType* sourceFieldType =
                sourceField.MatchFieldType( matchPriority );
            if ( sourceFieldType &&
                 exportPropertyType->IsSame( *sourceFieldType ) )
                {
                aIndex = n;
                return &sourceField;
                }
            }
        }
    return NULL;
    }

CParserPropertyValue* CVPbkVCardPropertyExporter::FetchDataFromFieldL(
    MVPbkStoreContactField& aField, CVPbkVCardParserParamArray& aParameters )
    {
    CParserPropertyValue* value = NULL;
    TVPbkFieldStorageType storageType = aField.FieldData().DataType();
    switch ( storageType )
        {
        case EVPbkFieldStorageTypeText:
            {
            value = GetExportTextValueL( aField );
            break;
            }
            
         case EVPbkFieldStorageTypeUri:
            {
            value = GetExportUriValueL( aField );
            break;
            }    

        case EVPbkFieldStorageTypeBinary:
            {
            value = GetExportBinaryValueL(aField, aParameters);
            break;
            }

        case EVPbkFieldStorageTypeDateTime:
            {
            value = GetExportDateValueL( aField );
            break;
            }

        default:
            {
            value = NULL;
            break;
            }
        }

    if ( value )
        {
        CleanupStack::PushL( value );
        GetAdditionalParamsL( aField, aParameters );
        CleanupStack::Pop( value );
        }

    return value;
    }


TBool CVPbkVCardPropertyExporter::AddPropertyToParserL(
    RPointerArray< CParserPropertyValue >& aValues,
    CVPbkVCardParserParamArray* aParams )
    {
    if( !HasValues() )
        {
        return EFalse;
        }

    CleanupStack::PushL( aParams );
    CParserPropertyValue* value = GetValueL( aValues );

    __ASSERT_DEBUG( value,
        VPbkVCardEngError::Panic( VPbkVCardEngError::ENullParserProperty ) );

    CleanupStack::PushL(value);
    CParserProperty* property =
        CParserProperty::NewL( *value, iVCardProperty->Name(), aParams );
    CleanupStack::Pop( value );
    CleanupStack::Pop( aParams ); // The ownership has taken by CParserProperty
    // NOTE: Versit pushes the CParserProperty* pointer stright away
    // to the cleanup stack, so to avoid duplicate entries in
    // the stack pointer must not be pushed there.
    iParser->AddPropertyL( property );

    return ETrue;
    }

TBool CVPbkVCardPropertyExporter::HasValues()
    {
    for ( TInt i = 0; i < iValues.Count(); ++i )
        {
        if ( iValues[ i ] )
            {
            return ETrue;
            }
        }
    return EFalse;
    }

CParserPropertyValue* CVPbkVCardPropertyExporter::GetValueL(
    RPointerArray< CParserPropertyValue >& aValues)
    {
    CParserPropertyValue* value = NULL;
    if ( aValues.Count() == KOne )
        {
        value = aValues[ 0 ];
        aValues.Remove( 0 );
        }
    else
        {
        CDesCArrayFlat* desarr = new(ELeave) CDesCArrayFlat( aValues.Count() );
        CleanupStack::PushL( desarr );

        while ( aValues.Count() > 0 )
            {
            CParserPropertyValueHBufC* tmpValue =
                static_cast<CParserPropertyValueHBufC*>( aValues[ 0 ] );
            CleanupStack::PushL( tmpValue );
            aValues.Remove( 0 );

            if ( !tmpValue || tmpValue->Value().Length() == 0 )
                {
                desarr->AppendL( KNullDesC );
                }
            else
                {
                desarr->AppendL( tmpValue->Value() );
                }

            CleanupStack::PopAndDestroy( tmpValue );
            }

        value = new (ELeave) CParserPropertyValueCDesCArray( desarr );
        CleanupStack::Pop( desarr );
        }

    __ASSERT_DEBUG( aValues.Count() == 0,
        VPbkVCardEngError::Panic(
            VPbkVCardEngError::EInvalidLengthOfPropertyArray ) );

    return value;
    }

CParserPropertyValue* CVPbkVCardPropertyExporter::GetExportTextValueL(
    const MVPbkBaseContactField& aField )
    {
    TPtrC text( MVPbkContactFieldTextData::Cast( aField.FieldData() ).Text() );
    if( !ContainsValidTextualDataL( text ) )
        {
        return NULL;
        }

    CParserPropertyValueHBufC* value = CParserPropertyValueHBufC::NewL( text );

    return value;
    }
    
 CParserPropertyValue* CVPbkVCardPropertyExporter::GetExportUriValueL(
    const MVPbkBaseContactField& aField )
    {
    TPtrC text( MVPbkContactFieldUriData::Cast( aField.FieldData() ).Uri() );
    if( !ContainsValidTextualDataL( text ) )
        {
        return NULL;
        }

    CParserPropertyValueHBufC* value = CParserPropertyValueHBufC::NewL( text );

    return value;
    }
    

CParserPropertyValue* CVPbkVCardPropertyExporter::GetExportDateValueL(
    const MVPbkBaseContactField& aField)
    {
    TTime time =
        MVPbkContactFieldDateTimeData::Cast( aField.FieldData() ).DateTime();
    TVersitDateTime* vdt =
        new (ELeave) TVersitDateTime( time.DateTime(),
                                      TVersitDateTime::EIsMachineLocal );
    CleanupStack::PushL( vdt );
    CParserPropertyValueDate* value =
        new (ELeave) CParserPropertyValueDate( vdt );
    CleanupStack::Pop( vdt );
    return value;
    }

CParserPropertyValue*
CVPbkVCardPropertyExporter::GetExportBinaryValueL(
    const MVPbkBaseContactField& aField,
    CVPbkVCardParserParamArray& aParameters )
    {
    const TDesC8& data =
        MVPbkContactFieldBinaryData::Cast( aField.FieldData() ).BinaryData();

    CParserParam* param = CParserParam::NewL( KPropEncoding, KPropBase64 );
    CleanupStack::PushL( param );
    aParameters.AppendL( param );
    CleanupStack::Pop( param );

    // format is always jpeg when retrieved from db
    param = CParserParam::NewL( KPropType, KFormatJPEG );
    CleanupStack::PushL( param );
    aParameters.AppendL( param );
    CleanupStack::Pop( param );

    CParserPropertyValueBinary* value = CParserPropertyValueBinary::NewL( data );
    return value;
    }

TBool CVPbkVCardPropertyExporter::ContainsValidTextualDataL(
    const TDesC& aDataText) const
    {
    if ( !aDataText.Length() )
        {
        return EFalse;
        }

    HBufC* tmpText = aDataText.AllocLC();
    TPtr tmpPtr = tmpText->Des();
    tmpPtr.Trim();

    if ( tmpPtr.Length() == 0 )
        {
        CleanupStack::PopAndDestroy(); // tmpText
        return EFalse;
        }

    CleanupStack::PopAndDestroy(); // tmpText
    return ETrue;
    }

void CVPbkVCardPropertyExporter::GetAdditionalParamsL(
    MVPbkStoreContactField& aField, CVPbkVCardParserParamArray& aParams )
    {
    if ( aParams.Count() > 0 ) // Fetch params only once.
        {
        return;
        }

    const MVPbkFieldType* fieldType = NULL;
    const TInt maxMatchPriority = iData.SupportedFieldTypes().MaxMatchPriority();
    for (TInt matchPriority = 0;
         matchPriority <= maxMatchPriority && !fieldType;
         ++matchPriority)
        {
        fieldType = aField.MatchFieldType( matchPriority );
        }
    if ( !fieldType )
        {
        return;
        }

    TBool match( EFalse );
    TVPbkFieldTypeParameters params;
    TArray<TVPbkFieldVersitProperty> array = fieldType->VersitProperties();
    for ( TInt n = 0; n < array.Count() && !match; ++n )
        {
        TVPbkFieldVersitProperty vProperty = array[ n ];
        for ( TInt priority = 0;
            priority <= maxMatchPriority && !match;
            ++priority )
            {
            match = fieldType->Matches( vProperty, priority );
            if ( match )
                {
                params.AddAll( vProperty.Parameters() );
                }
            }
        }
    
    TInt count = iData.ParamIdNameMappings().Count();
    for ( TInt i = 0; i < count; ++i )
        {
        if ( params.Contains(
                static_cast<TVPbkFieldTypeParameter>(
                    iData.ParamIdNameMappings()[ i ]->Id() ) ) )
            {
            CParserParam* param =
                CParserParam::NewL( iData.ParamIdNameMappings()[ i ]->Name(),
                                    KNullDesC );
            CleanupStack::PushL( param );
            aParams.AppendL( param );
            CleanupStack::Pop( param );
            }
        }

    // Set PREF param if field has default attribute set
    if ( iData.AttributeManagerL().HasFieldAttributeL(
                                CVPbkDefaultAttribute::Uid(), aField) )
        {
        CParserParam* param = CParserParam::NewL( KPropPREF, KNullDesC );
        CleanupStack::PushL( param );
        aParams.AppendL( param );
        CleanupStack::Pop( param );
        }
    }

void CVPbkVCardPropertyExporter::InitDataL()
    {
    TInt indexedPropertyCount( iVCardProperty->Count() );

    iValues.ResetAndDestroy();
    // Initialize array with NULL values
    while ( iValues.Count() < indexedPropertyCount )
        {
        iValues.AppendL( NULL );
        }
    }

TInt CVPbkVCardPropertyExporter::Next(TInt& aRemainingSteps)
    {
    __ASSERT_DEBUG( iState == EExporting,
        VPbkVCardEngError::Panic( VPbkVCardEngError::EInvalidStateOfExporter ) );
    TInt err( KErrNone );
    TRAP( err, DoNextL() );

    ++iCurrentPropertyIndex;
    aRemainingSteps = (iCurrentPropertyIndex < iMaxPropertyCount ) ?
        ( iMaxPropertyCount - iCurrentPropertyIndex ) :
        0;
    
 	if(!iBeamed && err == KErrNone && !aRemainingSteps )
	    {
	    TRAP( err, HandleOwnCardL() );
            if(err == KErrNone ) 
                TRAP( err, HandleGroupCardL() );
	    }
    return err;
    }

// ----------------------------------------------------------------------------
// CVPbkVCardPropertyExporter::HandleGroupCardL
// ----------------------------------------------------------------------------
void CVPbkVCardPropertyExporter::HandleGroupCardL()
    {
    CParserProperty* property = iGroupcardHandler->CreateXGroupPropertyL(iContact);
    if (property)
        {
        CleanupStack::PushL( property );
        iParser->AddPropertyL( property );  
        CleanupStack::Pop( property );
        }
    }

void CVPbkVCardPropertyExporter::HandleOwnCardL()
	{
	iOwncardHandler->DoInitL(iContact->ParentStore());
    if(iOwncardHandler->IsOwnContactL(iContact))
    	{
    	CParserProperty* property =iOwncardHandler->CreateXSelfPropertyL();
    	CleanupStack::PushL( property );
	   	iParser->AddPropertyL( property );	
	   	CleanupStack::Pop( property );
    	}
	}
void CVPbkVCardPropertyExporter::DoNextL()
    {
    ExportFieldL( *iData.Properties()[ iCurrentPropertyIndex ] );
    }

void CVPbkVCardPropertyExporter::CloseL()
    {
    iParser->ExternalizeL( iDestinationStream );
    iObserver.VPbkSingleContactOperationComplete( *this, NULL );
    }

void CVPbkVCardPropertyExporter::HandleLeave( TInt aError )
    {
    iObserver.VPbkSingleContactOperationFailed( *this, aError );
    }

void CVPbkVCardPropertyExporter::HandleCancel()
    {
    iObserver.VPbkSingleContactOperationFailed( *this, KErrCancel );
    }

void CVPbkVCardPropertyExporter::InitLinkL(
    const MVPbkContactLink& aContactLink,
    CVPbkContactManager& aContactManager )
    {
    iOperation =
        aContactManager.RetrieveContactL( aContactLink, *this );
    }

void CVPbkVCardPropertyExporter::VPbkSingleContactOperationComplete(
        MVPbkContactOperationBase& VPBK_DEBUG_ONLY( aOperation ),
        MVPbkStoreContact* aContact)
    {
    __ASSERT_DEBUG( iOperation == &aOperation,
        VPbkVCardEngError::Panic( VPbkVCardEngError::EStrayOperation ) );
    iOwnedContact = aContact;
    iContact = iOwnedContact;
    iState = EExporting;
    Complete( KErrNone );
    }

void CVPbkVCardPropertyExporter::VPbkSingleContactOperationFailed
        ( MVPbkContactOperationBase& VPBK_DEBUG_ONLY( aOperation ), TInt aError )
    {
    __ASSERT_DEBUG( iOperation == &aOperation,
        VPbkVCardEngError::Panic( VPbkVCardEngError::EStrayOperation ) );
    Complete( aError );
    }

void CVPbkVCardPropertyExporter::StartL()
    {
    if ( iState == ECreatingRetriever )
        {
        StartWaiting();
        }
    else
        {
        Start();
        }
    }


// End of file
