/*
* Copyright (c) 2005-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: 
*
*/

//#include <columbo.h>
#include <cntitem.h>
#include <cntfldst.h>

#include "cntmetadataoperation.h"

CCntMetadataOperation::CCntMetadataOperation(RColumboSession& aSession) 
: CActive(EPriorityStandard), iSession(aSession), iContactId(-1)
    {
    }

/**
 * Construct a new metadata operation object. Note that this object will destroy
 * itself after either save or delete operations have been called.
 * 
 * The new object will be left on the cleanup stack.
 * 
 * @param aSession Session with the metadata service.
 * @return The new metadata operation instance.
 */
CCntMetadataOperation* CCntMetadataOperation::NewLC(RColumboSession& aSession)
    {
    CCntMetadataOperation* self = new (ELeave) CCntMetadataOperation(aSession);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

/**
 * Construct a new metadata operation object. Note that this object will destroy
 * itself after either save or delete operations have been called.
 * 
 * @param aSession Session with the metadata service.
 * @return The new metadata operation instance.
 */
CCntMetadataOperation* CCntMetadataOperation::NewL(RColumboSession& aSession)
    {
    CCntMetadataOperation* self = CCntMetadataOperation::NewLC(aSession);
    CleanupStack::Pop();
    return self;
    }

/**
 * Note: upon construction, the active object is set active and waiting
 * for a completion event to destroy itself.
 */
void CCntMetadataOperation::ConstructL()
    {
    CActiveScheduler::Add(this);
    }

CCntMetadataOperation::~CCntMetadataOperation()
    {
    // (iSession is not owned)
    delete iDocument;
    delete iKey;
    }

/**
 * The DoCancel() function will only delete the object in production builds.
 * If __PROFILE_DEBUG__ is defined in the persistence layer MMP file,
 * error codes will be logged should they occur.
 */
void CCntMetadataOperation::DoCancel()
    {
    // Log that the operation is cancelled.
#ifdef __PROFILE_DEBUG__
    _LIT(KCancelFormat, "CCntMetadataOperation::DoCancel(): Operation Cancelled: Contact ID = %d");
    RDebug::Print(KCancelFormat(), iContactId);
#endif
    
    // Self destruct - operation cancelled.
    delete this;
    }

/**
 * The RunL() function will only delete the object in production builds.
 * If __PROFILE_DEBUG__ is defined in the persistence layer MMP file,
 * error codes will be logged should they occur.
 */
void CCntMetadataOperation::RunL()
    {
#ifdef __PROFILE_DEBUG__
    if (iStatus.Int() != KErrNone)
        {
        _LIT(KErrorFormat, "CCntMetadataOperation::RunL(): Operation failed [Contact ID = %d] Error = %d");
        RDebug::Print(KErrorFormat(), iContactId, iStatus.Int());
        }
    else
        {
        _LIT(KErrorFormat, "CCntMetadataOperation::RunL(): Operation complete [Contact ID = %d]");
        RDebug::Print(KErrorFormat(), iContactId);
        }
#endif // __PROFILE_DEBUG__
    
    // Self destruct - operation complete.
    delete this;
    }

TInt CCntMetadataOperation::RunError(TInt aError)
    {
    // RunL() doesn't really leave, this should never happen.
    // Log but do nothing.
#ifdef __PROFILE_DEBUG__
    _LIT(KRunErrorLog, "CCntMetadataOperation::RunError(): aError = %d");
    RDebug::Print(KRunErrorLog(), aError);
#endif // __PROFILE_DEBUG__
    return aError;
    }

/**
 * Save the contact @a aContact in the metadata service. If there is
 * already a contact entry with this ID, it will be updated. Otherwise,
 * a new contact metadata entry is added.
 * 
 * The metadata operation object will be destroyed when the operation
 * completes. Do not attempt to use this object after calling.
 * 
 * @param aContact The contact item to index for metadata.
 */
void CCntMetadataOperation::SaveContactLD(const CContactItem& aContact)
    {
    // For logging purposes
    iContactId = aContact.Id();
    
    // Create a new generic metadata object for this contact item.
    if (iDocument)
        {
        delete iDocument;
        iDocument = NULL;
        }
    iDocument = DocumentFromContactL(aContact);
    iDocument->Store(iSession, iStatus);
    SetActive();
    }

/**
 * Remove the contact described by @a aContactId from the metadata store.
 * 
 * The metadata operation object will be destroyed when the operation
 * completes. Do not attempt to use this object after calling.
 * 
 * @param aContactId The contact to be removed from the metadata service
 */
void CCntMetadataOperation::DeleteContactLD(TInt aContactId)
    {
    // For logging purposes
    iContactId = aContactId;
    
    // Get the key and delete the document
    if (iKey)
		{
		delete iKey;
		iKey = NULL;
		}
    HBufC* iKey = ContactMetadataKeyLC(aContactId);
    CleanupStack::Pop();
    
    iSession.DeleteDocument(*iKey, iStatus);
    SetActive();
    }

/**
 * Construct a new generic metadata object to represent a contact item.
 * 
 * @param aContact The contact item to encode as a generic metadata object.
 * @return The newly constructed metadata object instance.
 */
CMetadataDocument* CCntMetadataOperation::DocumentFromContactL(const CContactItem& aContact)
    {
    CMetadataDocument* document = CMetadataDocument::NewLC();
    
    // Assign a unique key for cntmodel purposes
    HBufC* key = ContactMetadataKeyLC(aContact.Id());
    document->SetUserKeyL(*key);
    CleanupStack::PopAndDestroy(key);
    
    // Iterate through fields, and produce a metadata string for this contact
    CContactItemFieldSet& fields = aContact.CardFields();
    const TInt KFieldCount(fields.Count());
    
    if (KFieldCount > 0)
        {
        // Find out how much space we need for the metadata buffer
        TInt bufferLength(0);
        for(TInt i(0); i < KFieldCount; ++i)
            {
            CContactItemField& field = fields[i];
            if (field.StorageType() != KStorageTypeText)
                {
                // Can only index text, no point if text is 0 length
                continue;
                }
            
            // No point in indexing 0 length data
            TInt fieldLength = field.TextStorage()->Text().Length();
            if (fieldLength == 0)
                {
                continue;
                }
            
            // Add field length plus the length of a space to
            // seperate it from the next field
            bufferLength += (fieldLength + 1);
            }
        
        // Allocate a buffer and add all the fields to it
        HBufC* metadata = HBufC::NewLC(bufferLength);
        TPtr bufferPtr = metadata->Des();
        
        for(TInt i(0); i < KFieldCount; ++i)
            {
            CContactItemField& field = fields[i];
            if (field.StorageType() != KStorageTypeText)
                {
                // Can only index text
                continue;
                }
            
            // No point in indexing 0 length data
            TInt fieldLength = field.TextStorage()->Text().Length();
            if (fieldLength == 0)
                {
                continue;
                }
            
            TPtrC fieldContent = field.TextStorage()->Text();
            bufferPtr.Append(fieldContent);

            // Add a space as a seperator
            _LIT(KSpace, " ");
            bufferPtr.Append(KSpace);
            }

        // Add the metadta to the generic document
        document->SetMetadataL(*metadata);
        CleanupStack::PopAndDestroy(metadata);
        }
    
    CleanupStack::Pop(document);
    return document;
    }

/**
 * Generate an RDNS style key to identify the metadata object for cntmodel purposes.
 * 
 * @param aContactId The contact model assigned contact ID.
 * @return A descriptor containing the new unique identifier for the document object.
 */
HBufC* CCntMetadataOperation::ContactMetadataKeyLC(TInt aContactId)
    {
    // Key format for cntmodel entries in the metadata service
    _LIT(KKeyFormat, "com.nokia.contact.id.%d");
    
    // Allocate a big enough buffer for just the key format with
    // an integer added to the end
    HBufC* key = HBufC::NewLC(KKeyFormat().Length() + 32);
    TPtr keyPtr = key->Des();
    keyPtr.AppendFormat(KKeyFormat(), aContactId);
    
    // Leaving key on the cleanup stack
    return key;
    }

