/*
* Copyright (c) 2002 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: 
*      A class to convert Phonebook items to vCard stream and vice versa
*
*/


// INCLUDE FILES
#include "BCardEng.h" // This class

// System APIs
#include <s32mem.h>
#include <barsc.h>
#include <barsread.h>
#include <cntfldst.h>
#include <vcard.h>

// Series 60 APIs
#include <StringLoader.h>
#include <featmgr.h>
#include <bldvariant.hrh>

// Phonebook APIs
#include <PbkEngUtils.h>
#include <CPbkContactEngine.h>
#include <CPbkContactItem.h>
#include <CPbkFieldsInfo.h>
#include <CPbkFieldInfo.h>
#include <CPbkAttachmentFile.h>
#include <CPbkConstants.h>

#include <BCardEng.rsg>
#include "CBCardParserParamArray.h"
#include "CBCardFieldAction.h"
#include "CBCardFieldUidMapping.h"
#include "CBCardExportProperty.h"
#include "TPbkMatchPriorityLevel.h"
#include "TBCardVersitPlugin.h"
#include "CBCardParser.h"
#include "PbkDataCaging.hrh"

/// Unnamed namespace for local definitons
namespace {

// LOCAL CONSTANTS AND MACROS
_LIT(KResFileName, "BCardEng.rsc");
_LIT(KBusinessCardText, "Business Card");   // Business card signature
_LIT(KCloseParenthesis, ")");
_LIT(KTelText, "tel ");
_LIT(KFaxText, "fax ");
_LIT(KSeparatorText, ", ");

const TText KOpenParenthesis = '(';
const TInt KBufferSize = 200;
const TInt KStartPosition = 4;
const TInt KLabelStartPosition = 5;
const TInt KLabelValueDelimiterLength = 2;

} // namespace

// ================= MEMBER FUNCTIONS =======================

inline CBCardEngine::CBCardEngine()
    {
    }

EXPORT_C CBCardEngine* CBCardEngine::NewL(CPbkContactEngine *aEngine)
    {
	CBCardEngine* self = new(ELeave) CBCardEngine();
	CleanupStack::PushL(self);
	self->ConstructL(aEngine);
	CleanupStack::Pop(self);
	return self;
    }

/*
 * ConstructL() reads the import/export definitions from the resource file
 * and stores them into class member arrays.
 */
void CBCardEngine::ConstructL(CPbkContactEngine* aEngine)
    {
    // Open a file server session
    RFs fs;
	User::LeaveIfError(fs.Connect());
    CleanupClosePushL(fs);

    // Initialize feature manager
    FeatureManager::InitializeLibL();

    // Open the resource file
    RResourceFile resourceFile;
    PbkEngUtils::FindAndOpenResourceFileLC(fs, KResFileName,
		KDC_RESOURCE_FILES_DIR, KPbkRomFileDrive, resourceFile);

	TResourceReader resReader;

	// Read picture filenames
	resReader.SetBuffer(resourceFile.AllocReadLC(R_QTN_BCARD_FILENAME_PICTURE));
	iPictureFileName = resReader.ReadHBufCL();
    CleanupStack::PopAndDestroy();  // R_QTN_BCARD_FILENAME_PICTURE

	resReader.SetBuffer(resourceFile.AllocReadLC(R_QTN_BCARD_FILENAME_THUMBNAIL));    
    iThumbnailFileName = resReader.ReadHBufCL();
    CleanupStack::PopAndDestroy();  // R_QTN_BCARD_FILENAME_THUMBNAIL
    
    // Read propery name (+index) <-> Uid mapping
    resReader.SetBuffer(resourceFile.AllocReadLC(R_PBK_BCARD_NAME_UID_MAPPING));
    iNameUidMap = CBCardFieldUidMapping::NewL(resReader);
    CleanupStack::PopAndDestroy(); // R_PBK_BCARD_NAME_UID_MAPPING
    
    // Read export properties
    resReader.SetBuffer(resourceFile.AllocReadLC(R_PBK_BCARD_EXPORT_VCARD));
	TInt count = resReader.ReadInt16();
    while (--count >= 0)
        {
        CBCardExportProperty* property = 
            CBCardExportProperty::NewLC(resReader, aEngine->FieldsInfo());
        // Do not export non-existing fields, this check is required for
        // western version not to export japanese reading fields
        if (property->InfoCount() > 0)
            {
            // Array takes ownership of the property
            User::LeaveIfError(iExportProperties.Append(property));
            CleanupStack::Pop(property);
            }
        else
            {
            CleanupStack::PopAndDestroy(property);
            }
        }
    CleanupStack::PopAndDestroy(3);  // resourceFile, fs
    
    iEngine = aEngine;
    iFieldAction = CBCardFieldAction::NewL(
        *aEngine,*iNameUidMap, *iPictureFileName, *iThumbnailFileName);
    }

EXPORT_C CBCardEngine::~CBCardEngine()
    {
    delete iNameUidMap;
	delete iThumbnailFileName;
	delete iPictureFileName;
    delete iFieldAction;
    iExportProperties.ResetAndDestroy();
    iExportProperties.Close();
    FeatureManager::UnInitializeLib();
    }

/*
 * THE MAIN IMPORT FUNCTION
 * Properties are imported from vCard file according to the specifications
 * loaded from the resource file.
 */ 
EXPORT_C void CBCardEngine::ImportBusinessCardL(
    CPbkContactItem& aDestItem, RReadStream &aSourceStream)
    {
    // Must use CParserVCard here, not the own parser class!
    // Otherwise there will be problems, and the parsing goes wrongly
    CParserVCard* parser = CParserVCard::NewL();
    CleanupStack::PushL(parser);

    if (FeatureManager::FeatureSupported(KFeatureIdJapanese))
        {
        // if in Japanese mode, use shift-jis by default
        parser->SetDefaultCharSet(Versit::EShiftJISCharSet);
        parser->SetFlags(CVersitParser::EUseDefaultCharSetForAllProperties);
        }

    // This has to be trapped
    TRAPD(err,parser->InternalizeL(aSourceStream));        
    if ((err != KErrNone) && (err != KErrEof))
        {
        User::Leave(err);        
        }

    CArrayPtr<CParserProperty>* arr = parser->ArrayOfProperties(EFalse);   
    if (!arr)
        {
        // Return if there are no properties
        CleanupStack::PopAndDestroy(parser); 
        return;
        }

    // Loop through different prioritylevels
    for (TPbkMatchPriorityLevel priorityLevel(iEngine->FieldsInfo().CreateMatchPriority());
        !priorityLevel.End(); 
        priorityLevel.Next())
        {
        // Loop through all the fields
        for (TInt i=0; i<arr->Count(); i++)
            {
            CParserProperty* prop = arr->At(i);        
            if (prop)
                {
                iFieldAction->AddFieldL(aDestItem, *prop, priorityLevel);
                }    
            }
        }

    CleanupStack::PopAndDestroy(parser); // parser
    }

/**
 * The main Export function.
 */
EXPORT_C void CBCardEngine::ExportBusinessCardL(
        RWriteStream& aDestStream, 
        CPbkContactItem& aSourceItem)
    {
    CParserVCard* parser = CParserVCard::NewL();
    CleanupStack::PushL(parser);
    
    // For Japanese: Set charset based on local variation setting
    if (FeatureManager::FeatureSupported(KFeatureIdJapanese))
        {
        if (iEngine->Constants()->LocallyVariatedFeatureEnabled(EPbkLVUseJapaneseUTF8))
            {
            parser->SetDefaultCharSet(Versit::EUTF8CharSet);
            }
        else
            {
            parser->SetDefaultCharSet(Versit::EShiftJISCharSet);
            }
        parser->SetFlags(CVersitParser::EUseDefaultCharSetForAllProperties);
        }
    
    // use plug in for disabling encoding
    TBCardVersitPlugin* exportPlugin = new (ELeave) TBCardVersitPlugin;
    CleanupStack::PushL(exportPlugin);
    parser->SetPlugIn(exportPlugin);

    for (TInt i = 0; i < iExportProperties.Count(); i++)
        {
        ProcessExportPropertyL(*parser, aSourceItem, *iExportProperties[i]);       
        }

    // Externalize the Parser to the Stream
    parser->ExternalizeL(aDestStream);

    CleanupStack::PopAndDestroy(exportPlugin);
    CleanupStack::PopAndDestroy(parser);
    }

void CBCardEngine::ProcessExportPropertyL(
        CParserVCard& aParser, 
        CPbkContactItem& aSourceItem, 
        const CBCardExportProperty& aExportProperty)
    {
    if (aExportProperty.StorageType() == EPbkVersitPropertyCDesCArray)
        {
        iFieldAction->ExportArrayOfFieldsL(aParser, aSourceItem, aExportProperty);
        }
    else
        {
        const CPbkFieldInfo* info = aExportProperty.InfoAt(0);
        
        TBool doContinue = (info->Multiplicity() == EPbkFieldMultiplicityMany);
        
        TInt index = 0;
        do 
            {           
            TPbkContactItemField* field = aSourceItem.FindField(*info, index);            
            if (field)
                {
                ++index;
                iFieldAction->ExportContactFieldL(
                    aExportProperty, 
                    aParser, 
                    aSourceItem, 
                    *field);
                }
            else
                {
                doContinue = EFalse;
                }
        
            } while (doContinue);
        }
    }

TInt CBCardEngine::GetByteFromStream(TUint8& aByte, RReadStream& aSourceStream)
    {
    TRAPD(err, aByte = aSourceStream.ReadUint8L());
    return err;
    }

TInt CBCardEngine::GetLineFromStreamL(TDes& aBuf, RReadStream& aSourceStream)
    {
    aBuf.SetLength(0);
    TUint8 byte=0;
    while(GetByteFromStream(byte, aSourceStream) == KErrNone)
        {
        if(byte == EKeyLineFeed)
            {
            break;
            }
        if(byte != EKeyEnter)
            {
            if (aBuf.Length() < aBuf.MaxLength())
                {
                aBuf.Append(byte);
                }
            else
                {
                User::Leave(KErrCorrupt);
                }
            }
        }
    return aBuf.Length();
    }

EXPORT_C TBool CBCardEngine::ImportCompactBusinessCardL(
    CPbkContactItem& aDestItem, RReadStream &aSourceStream)
    {
    TBuf<KBufferSize> buf;

    // "Business Card" signature
    if(GetLineFromStreamL(buf, aSourceStream))
        {
        if(buf.Match(KBusinessCardText)==KErrNotFound)
            {
            return EFalse;
            }
        }        

    // Name
    if(GetLineFromStreamL(buf, aSourceStream))
        {
        iFieldAction->AddCompactCardFieldL(aDestItem, EPbkFieldIdLastName, buf);
        }

    // Company
    if(GetLineFromStreamL(buf, aSourceStream))
        {
        iFieldAction->AddCompactCardFieldL(aDestItem, EPbkFieldIdCompanyName, buf);
        }

    // Title
    if(GetLineFromStreamL(buf, aSourceStream))
        {
        iFieldAction->AddCompactCardFieldL(aDestItem, EPbkFieldIdJobTitle, buf);
        }

    // Telephone numbers
    TInt len=0;
    while((len = GetLineFromStreamL(buf, aSourceStream))>0)
        {
        TPbkFieldId fieldid = EPbkFieldIdNone;
        if(buf.Find(KTelText)>=0)
            {
            fieldid = EPbkFieldIdPhoneNumberStandard;
            } 
        else if(buf.Find(KFaxText)>=0) 
            {
            fieldid = EPbkFieldIdFaxNumber;
            }

        if(fieldid == EPbkFieldIdNone)
            {
            break;
            }
        else if(len <= KStartPosition)
            {
            // no data on this field, try next
            continue;
            }
        
		// Read tel num label
		TInt position = KStartPosition;
        HBufC* label = NULL;
        if(buf[KStartPosition] == KOpenParenthesis)
            {
            position = buf.Find(KCloseParenthesis);
            if(position == KErrNotFound)
                {
                continue;
                }
            label = HBufC::NewLC(position-KStartPosition);
    		TPtr writebuf = label->Des();
	    	writebuf = KNullDesC;
            for(TInt i=KLabelStartPosition; i<position; i++)
                {
                writebuf.Append(buf[i]);
                }
			// Jump few characters
            position += KLabelValueDelimiterLength;
            }
            
        if(position < buf.Length())
            {
    		// Read value
            HBufC* value = HBufC::NewLC(buf.Length()-position);
       		TPtr writebuf = value->Des();
        	writebuf = KNullDesC;
            for(TInt i=position; i<buf.Length(); i++)
                {
                writebuf.Append(buf[i]);
                }

    		iFieldAction->AddCompactCardFieldL(aDestItem, fieldid,
    			*value, EFalse, label);

            CleanupStack::PopAndDestroy(value); 
            }
            
        if(label)
            {
            CleanupStack::PopAndDestroy(label); 
            }
        }

    // Email address
    if(len)
        {
        iFieldAction->AddCompactCardFieldL(aDestItem,
			EPbkFieldIdEmailAddress, buf);
        }

    // Postal address (one or more lines)
    // First read all the address lines and store them
    // in a list
    CDesCArray* arr = new(ELeave) CDesCArrayFlat(2); // typically 2 lines
    CleanupStack::PushL(arr);
    len=0;
    while(GetLineFromStreamL(buf, aSourceStream))
        {
        arr->AppendL(buf);
        if(len)
            {
            len+=KSeparatorText().Length();
            }
        len+=buf.Length();
        }

    // Then concatenate them with commas into one string
    // and use that as field value.
    HBufC* fielddata = HBufC::NewLC(len);
	TPtr writebuf = fielddata->Des();
	writebuf = KNullDesC;
    for(TInt i=0;i<arr->Count();i++)
        {
        if(i)
            {
            writebuf.Append(KSeparatorText);
            }
        writebuf.Append(arr->MdcaPoint(i));
        }

    iFieldAction->AddCompactCardFieldL(aDestItem, EPbkFieldIdPostalAddress,
		*fielddata);
    
    CleanupStack::PopAndDestroy(2); // fielddata, arr

    return ETrue;
    }

EXPORT_C TBool CBCardEngine::SupportsFieldType(TPbkFieldId aFieldId)
    {
    TBool isSupported = EFalse;
    CBCardExportProperty* property;
    
    // Go trough the array of supported fields and try to find the fieldId
    for (TInt i=0; i < iExportProperties.Count(); i++)
        {
        property = iExportProperties[i];
        for (TInt j=0; j < property->InfoCount(); j++)
            {
            if (property->InfoAt(j)->FieldId() == aFieldId)
                {
                isSupported = ETrue;
                break;
                }
            }
        }
    return isSupported;
    }

// End of File
