/*
* Copyright (c) 2008 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:  ?Description
 *
*/


#include <apmstd.h>
#include <s32stor.h>
#include <badesca.h>  //to use descriptor arrays
#include <eikenv.h>
#include <eikdef.h>
#include <driveinfo.h>
#include <DocumentHandler.h>

#include "logger.h"
#include "s60commonutils.h"
#include "javasymbianoslayer.h"
#include "cjavammsmessagehandler.h"

namespace java
{
namespace wma
{

const TInt KMmsMapLength = 7;
const TInt KReplyToAppIDFieldIndex = 0;
const TInt KAppIDFieldIndex = 1;
const TInt KSubjectFieldIndex = 2;
const TInt KFromFieldIndex = 3;
const TInt KToFieldIndex = 4;
const TInt KBccFieldIndex = 5;
const TInt KCcFieldIndex = 6;
const TInt KBufFlatGranularity = 20;

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

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::CJavaMMSMessageHandler
 * C++ default constructor
 * -----------------------------------------------------------------------------
 */
CJavaMMSMessageHandler::CJavaMMSMessageHandler(RFs aRFs) :
        mRFs(aRFs)
{
    JELOG2(EWMA);
}

/* -----------------------------------------------------------------------------
 * Destructor
 * -----------------------------------------------------------------------------
 */
CJavaMMSMessageHandler::~CJavaMMSMessageHandler()
{
    JELOG2(EWMA);
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::NewL
 * Two-phased constructor.
 * -----------------------------------------------------------------------------
 */
CJavaMMSMessageHandler* CJavaMMSMessageHandler::NewL(RFs aFs)
{
    JELOG2(EWMA);
    CJavaMMSMessageHandler* self = new CJavaMMSMessageHandler(aFs);
    return self;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::SerializeLC
 * This method serializes an MMS (as it is received from the MMS engine) into
 * a byte array which will be sent to java.
 * The format of the serialized MMS is according to the following scheme:
 *     ----------------------------------------------------------------------------------------------------------
 *     | Map | HeaderLengths | AttachmentsCount | AttachmentsLengths | StartContentFlag | Headers | Attachments |
 *     ----------------------------------------------------------------------------------------------------------
 *
 * where Map                 Represents the decimal value of a binary
 *                          representation of MMS header fields
 *                          (applicationID, Subject, From, To, Bcc, Cc)
 *                          Ex: 52 = 110101 means that 'applicationID'
 *                          is present, 'Subject' is present, 'From' is
 *                          not present,'To' is present, 'Bcc' is not
 *                          present and 'Cc' is present
 *                          The lengths of each of the MMS header
 *                          fields which are present
 *      AttachmentsCount    The number of attachments
 *      AttachmentsLengths  The lengths of each of the attachments
 *      StartContentFlag    A boolean indicating if the first
 *                          attachment should be interpreted as
 *                          startContent or not
 *      Headers             The MMS header fields
 *      Attachments         The attachments
 * -----------------------------------------------------------------------------
 */
CBufFlat& CJavaMMSMessageHandler::SerializeLC(CMMSMessageInformation& aMMS)
{
    JELOG2(EWMA);
    // mms fields
    TDesC8& replyToAppID = aMMS.ReplyToApplicationIdL();
    TDesC8& appID = aMMS.ApplicationIdL();
    TDesC8& subject = aMMS.MessageSubjectL();
    TDesC8& from = aMMS.MessageFromAddressL();
    // the address is on the cleanup stack
    HBufC8& to = BuildAddressLC(EMsvRecipientTo, aMMS);
    // the address is on the cleanup stack
    HBufC8& bcc = BuildAddressLC(EMsvRecipientBcc, aMMS);
    // the address is on the cleanup stack
    HBufC8& cc = BuildAddressLC(EMsvRecipientCc, aMMS);

    // calculate the mms Map
    TInt mmsMap = 0;
    mmsMap += CalculateMap(replyToAppID, appID, subject, from, to, bcc, cc);

    // The serialized buffer
    CBufFlat* buffer = CBufFlat::NewL(KBufFlatGranularity);
    CleanupStack::PushL(buffer);
    CCnvCharacterSetConverter* cConv = CCnvCharacterSetConverter::NewL();
    CleanupStack::PushL(cConv);
    RBufWriteStream writeStream(*buffer, 0);

    // add the mmsMap
    writeStream.WriteUint8L(mmsMap);

    // add the mms fields lengths
    WriteDes8LengthL(writeStream, replyToAppID);
    WriteDes8LengthL(writeStream, appID);
    WriteDes8LengthL(writeStream, subject);
    WriteDes8LengthL(writeStream, from);
    WriteDes8LengthL(writeStream, to);
    WriteDes8LengthL(writeStream, bcc);
    WriteDes8LengthL(writeStream, cc);

    // add the attachments count
    TInt8 attchmts = aMMS.AttachmentCount();
    writeStream.WriteUint8L(attchmts);

    // add the attachments lenghts
    TInt firstAttchmt = WriteAttachmentsLengthsL(writeStream, aMMS, *cConv);

    // add the starterCID indicator
    if (attchmts > 0)
    {
        if (firstAttchmt >= 0)
        {
            writeStream.WriteUint8L(1);
        }
        else
        {
            writeStream.WriteUint8L(0);
        }
    }

    // add the priority
    TMmsMessagePriority priority = aMMS.MessagePriority();
    switch (priority)
    {
    case EMmsPriorityLow:
        writeStream.WriteUint8L(KPriorityLow);
        break;
    case EMmsPriorityNormal:
        writeStream.WriteUint8L(KPriorityNormal);
        break;
    case EMmsPriorityHigh:
        writeStream.WriteUint8L(KPriorityHigh);
        break;
    default:
        writeStream.WriteUint8L(KPriorityNormal);
        break;
    }

    // add the date
    WriteInt64L(writeStream, aMMS.MessageTime());

    // add the mms fields
    WriteDes8L(writeStream, replyToAppID);
    WriteDes8L(writeStream, appID);
    WriteDes8L(writeStream, subject);
    WriteDes8L(writeStream, from);
    WriteDes8L(writeStream, to);
    WriteDes8L(writeStream, bcc);
    WriteDes8L(writeStream, cc);

    // add the attachments
    WriteAttachmentsL(writeStream, aMMS, *cConv, firstAttchmt);

    // cleanup
    CleanupStack::PopAndDestroy(cConv);
    CleanupStack::Pop(buffer);
    CleanupStack::PopAndDestroy(&cc);
    CleanupStack::PopAndDestroy(&bcc);
    CleanupStack::PopAndDestroy(&to);
    CleanupStack::PushL(buffer);

    return *buffer;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteAttachmentsL
 * Writes all the attachments into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteAttachmentsL(RBufWriteStream& aWriteStream,
        CMMSMessageInformation& aMMS, CCnvCharacterSetConverter& aCharConv,
        TInt firstAttchmt)
{
    JELOG2(EWMA);
    TInt8 attchmts = aMMS.AttachmentCount();
    if (firstAttchmt >= 0)
    {
        WriteAttachmentL(aWriteStream, aMMS.Attachment(firstAttchmt), aCharConv);
    }
    for (TInt i = 0; i < attchmts; i++)
    {
        if (i != firstAttchmt)
        {
            WriteAttachmentL(aWriteStream, aMMS.Attachment(i), aCharConv);
        }
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteAttachmentL
 * Writes an attachment into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteAttachmentL(RBufWriteStream& aWriteStream,
        CMMSMessageAttachment& aAttch, CCnvCharacterSetConverter& aCharConv)
{
    JELOG2(EWMA);
    // mime type
    WriteDes8L(aWriteStream, aAttch.AttachmentType());
    // content ID
    WriteDes8L(aWriteStream, aAttch.ContentIdL());
    // content location
    WriteDes8L(aWriteStream, aAttch.ContentLocationL());
    // content encoding
    TUint charSetID = aCharConv.ConvertMibEnumOfCharacterSetToIdentifierL(
                          aAttch.Encoding(), mRFs);
    HBufC8* encoding = aCharConv.ConvertCharacterSetIdentifierToStandardNameL(
                           charSetID, mRFs);
    CleanupStack::PushL(encoding);
    if (encoding)
    {
        aWriteStream.WriteL(*(encoding));
    }
    CleanupStack::PopAndDestroy(encoding);
    // content
    WriteDes8L(aWriteStream, aAttch.AttachmentData());
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteAttachmentsLengthsL
 * Writes all attachments lengths (one by one) into the write stream.
 * -----------------------------------------------------------------------------
 */
TInt CJavaMMSMessageHandler::WriteAttachmentsLengthsL(RBufWriteStream& aWriteStream,
        CMMSMessageInformation& aMMS, CCnvCharacterSetConverter& aCharConv)
{
    JELOG2(EWMA);
    TInt firstAttchmt = -1;
    TInt8 attchmts = aMMS.AttachmentCount();
    if (attchmts > 0)
    {
        // quick browse through the attachments to see if the starterCID is used
        for (TInt i = 0; i < attchmts; i++)
        {
            CMMSMessageAttachment& attch = aMMS.Attachment(i);
            if (attch.IsStarterContentId())
            {
                // put the starterCID first
                WriteAttachmentLengthsL(aWriteStream, attch, aCharConv);
                firstAttchmt = i;
                break;
            }
        }
    }
    for (TInt i = 0; i < attchmts; i++)
    {
        if (i != firstAttchmt)
        {
            WriteAttachmentLengthsL(aWriteStream, aMMS.Attachment(i), aCharConv);
        }
    }
    return firstAttchmt;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteAttachmentLengthsL
 * Writes an attachment lengths into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteAttachmentLengthsL(RBufWriteStream& aWriteStream,
        CMMSMessageAttachment& aAttch, CCnvCharacterSetConverter& aCharConv)
{
    JELOG2(EWMA);
    // mime type length
    WriteDesLengthOrZeroL(aWriteStream, aAttch.AttachmentType());
    // content ID length
    WriteDesLengthOrZeroL(aWriteStream, aAttch.ContentIdL());
    // content location length
    WriteDesLengthOrZeroL(aWriteStream, aAttch.ContentLocationL());
    // content encoding length
    TUint charSetID = aCharConv.ConvertMibEnumOfCharacterSetToIdentifierL(
                          aAttch.Encoding(), mRFs);
    HBufC8* encoding = aCharConv.ConvertCharacterSetIdentifierToStandardNameL(
                           charSetID, mRFs);
    CleanupStack::PushL(encoding);
    if (encoding)
    {
        WriteIntL(aWriteStream, encoding->Length());
    }
    else
    {
        WriteIntL(aWriteStream, 0);
    }
    CleanupStack::PopAndDestroy(encoding);
    // content size
    WriteDesLengthOrZeroL(aWriteStream, aAttch.AttachmentData());
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::BuildAddressLC
 * Concatenates all the addresses/recipients into a ";" separated
 * string/descriptor.
 * -----------------------------------------------------------------------------
 */
HBufC8& CJavaMMSMessageHandler::BuildAddressLC(
    TMsvRecipientTypeValues aAddressType, CMMSMessageInformation& aMMS)
{
    JELOG2(EWMA);
    _LIT(KAddressSep, ";");
    HBufC8* address = HBufC8::NewLC(0);
    TPtr8 AddressPtr(address->Des());
    for (int i = 0; i < aMMS.AddressesCount(aAddressType); i++)
    {
        TDesC& currentAddress = aMMS.MessageAddressL(aAddressType, i);
        HBufC8* tmp = NULL;
        if (i < aMMS.AddressesCount(aAddressType) - 1)
        {
            tmp = address->ReAllocL(address->Length() + currentAddress.Length()
                                    + 1);
        }
        else
        {
            tmp
            = address->ReAllocL(address->Length()
                                + currentAddress.Length());
        }
        CleanupStack::Pop(address);
        address = tmp;
        CleanupStack::PushL(address);
        AddressPtr.Set(address->Des());
        AddressPtr.Append(currentAddress);
        if (i < aMMS.AddressesCount(aAddressType) - 1)
        {
            AddressPtr.Append(KAddressSep);
        }
    }
    return *address;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::CalculateMap
 * Calculates the mms map.
 * -----------------------------------------------------------------------------
 */
TInt CJavaMMSMessageHandler::CalculateMap(TDesC8& aReplyToAppID, TDesC8& aAppID,
        TDesC8& aSubject, TDesC8& aFrom,
        HBufC8& aTo, HBufC8& aBcc, HBufC8& aCc)
{
    JELOG2(EWMA);
    TInt mmsMap = 0;
    if (&aReplyToAppID)
    {
        mmsMap += K64BitsMask;
    }
    if (&aAppID)
    {
        mmsMap += K32BitsMask;
    }
    if (&aSubject)
    {
        mmsMap += K16BitsMask;
    }
    if (&aFrom)
    {
        mmsMap += K8BitsMask;
    }
    if (&aTo)
    {
        mmsMap += K4BitsMask;
    }
    if (&aBcc)
    {
        mmsMap += K2BitsMask;
    }
    if (&aCc)
    {
        mmsMap += 1;
    }
    return mmsMap;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteInt64L
 * Writes an integer as 8 bytes into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteInt64L(RBufWriteStream& aWriteStream,
        const TInt64 aInteger)
{
    JELOG2(EWMA);
    aWriteStream.WriteInt8L(aInteger >> K56BitsMask);
    aWriteStream.WriteInt8L(aInteger >> K48BitsMask);
    aWriteStream.WriteInt8L(aInteger >> K40BitsMask);
    aWriteStream.WriteInt8L(aInteger >> K32BitsMask);
    aWriteStream.WriteInt8L(aInteger >> K24BitsMask);
    aWriteStream.WriteInt8L(aInteger >> K16BitsMask);
    aWriteStream.WriteInt8L(aInteger >> K8BitsMask);
    aWriteStream.WriteInt8L(aInteger);
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteIntL
 * Writes an integer as 4 bytes into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteIntL(RBufWriteStream& aWriteStream,
                                       const TInt32 aInteger)
{
    JELOG2(EWMA);
    aWriteStream.WriteUint8L(aInteger >> K24BitsMask);
    aWriteStream.WriteUint8L(aInteger >> K16BitsMask);
    aWriteStream.WriteUint8L(aInteger >> K8BitsMask);
    aWriteStream.WriteUint8L(aInteger);
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteDes8L
 * Writes a descriptor (if not NULL) into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteDes8L(RBufWriteStream& aWriteStream,
                                        const TDesC8& aDes)
{
    JELOG2(EWMA);
    if (&aDes)
    {
        aWriteStream.WriteL(aDes);
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteDes8LengthL
 * Writes a descriptor's length (if not NULL) into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteDes8LengthL(RBufWriteStream& aWriteStream,
        const TDesC8& aDes)
{
    JELOG2(EWMA);
    if (&aDes)
    {
        WriteIntL(aWriteStream, aDes.Length());
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteDes8LengthOrZeroL
 * Writes a descriptor's length (if descriptor is not NULL) or zero (if
 * descriptor is NULL) into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteDes8LengthOrZeroL(RBufWriteStream& aWriteStream,
        const TDesC8& aDes)
{
    JELOG2(EWMA);
    if (&aDes)
    {
        aWriteStream.WriteUint8L(aDes.Length());
    }
    else
    {
        aWriteStream.WriteUint8L(0);
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::WriteDesLengthOrZeroL
 * Writes a descriptor's length (if descriptor is not NULL) or zero (if
 * descriptor is NULL) into the write stream.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::WriteDesLengthOrZeroL(RBufWriteStream& aWriteStream,
        const TDesC8& aDes)
{
    JELOG2(EWMA);
    if (&aDes)
    {
        WriteIntL(aWriteStream, aDes.Length());
    }
    else
    {
        WriteIntL(aWriteStream, 0);
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::DeSerializeL
 * This method de-serializes an MMS (as it is received java) into
 * an internal representation (used by the MMS engine proxy).
 * The format of the serialized MMS is according to the following scheme:
 *     ----------------------------------------------------------------------------------------------------------
 *     | Map | HeaderLengths | AttachmentsCount | AttachmentsLengths | StartContentFlag | Headers | Attachments |
 *     ----------------------------------------------------------------------------------------------------------
 *
 * where Map                 Represents the decimal value of a binary
 *                          representation of MMS header fields
 *                          (applicationID, Subject, From, To, Bcc, Cc)
 *                          Ex: 52 = 110101 means that 'applicationID'
 *                          is present, 'Subject' is present, 'From' is
 *                          not present,'To' is present, 'Bcc' is not
 *                          present and 'Cc' is present
 *                          The lengths of each of the MMS header
 *                          fields which are present
 *      AttachmentsCount    The number of attachments
 *      AttachmentsLengths  The lengths of each of the attachments
 *      StartContentFlag    A boolean indicating if the first
 *                          attachment should be interpreted as
 *                          startContent or not
 *      Headers             The MMS header fields
 *      Attachments         The attachments
 * -----------------------------------------------------------------------------
 */
CMMSMessageInformation& CJavaMMSMessageHandler::DeSerializeL(CBufFlat& aMMS)
{
    JELOG2(EWMA);
    RBufReadStream rStream(aMMS, 0);

    // get the raw mms map
    TInt32 headerLengths[KMmsMapLength] =
        { 0, 0, 0, 0, 0, 0 };

    ResolveMapL(rStream, headerLengths);
    // get the attachments count
    TInt8 attchmts = rStream.ReadUint8L();

    // get the attachments lenghts
    RArray<TInt> mimeLengths(1);
    CleanupClosePushL(mimeLengths);
    RArray<TInt> cidLengths(1);
    CleanupClosePushL(cidLengths);
    RArray<TInt> cLocationLengths(1);
    CleanupClosePushL(cLocationLengths);
    RArray<TInt> cEncodingLengths(1);
    CleanupClosePushL(cEncodingLengths);
    RArray<TInt32> contentLengths(1);
    CleanupClosePushL(contentLengths);
    for (TInt i = 0; i < attchmts; i++)
    {
        // read the mime length
        mimeLengths.InsertL(ReadIntL(rStream), i);
        // read the content ID length
        cidLengths.InsertL(ReadIntL(rStream), i);
        // read the content Location length
        cLocationLengths.InsertL(ReadIntL(rStream), i);
        // read the content Encoding length
        cEncodingLengths.InsertL(ReadIntL(rStream), i);
        // read the content length
        contentLengths.InsertL(ReadIntL(rStream), i);
    }

    // get the startContentId flag
    TInt8 startCID = 0;
    if (attchmts > 0)
    {
        startCID = rStream.ReadUint8L();
    }

    CMMSMessageInformation& msg = ResolveHeadersLC(rStream, headerLengths);
    // msg is on the cleanup stack

    // read the attachments
    CCnvCharacterSetConverter* charConv = CCnvCharacterSetConverter::NewL();
    CleanupStack::PushL(charConv);
    for (TInt i = 0; i < attchmts; i++)
    {
        HBufC8* mime = HBufC8::NewL(mimeLengths[i]);
        CleanupStack::PushL(mime);
        TPtr8 mimePtr(mime->Des());
        HBufC8* cid = HBufC8::NewL(cidLengths[i]);
        CleanupStack::PushL(cid);
        TPtr8 cidPtr(cid->Des());
        HBufC8* cLocation = HBufC8::NewL(cLocationLengths[i]);
        CleanupStack::PushL(cLocation);
        TPtr8 cLocationPtr(cLocation->Des());
        HBufC8* cEncoding = HBufC8::NewL(cEncodingLengths[i]);
        CleanupStack::PushL(cEncoding);
        TPtr8 cEncodingPtr(cEncoding->Des());
        HBufC8* content = HBufC8::NewL(contentLengths[i]);
        CleanupStack::PushL(content);
        TPtr8 contentPtr(content->Des());
        // read the mime
        rStream.ReadL(mimePtr, mimeLengths[i]);
        // read the content ID
        rStream.ReadL(cidPtr, cidLengths[i]);
        // read the content Location
        rStream.ReadL(cLocationPtr, cLocationLengths[i]);
        // read the content Encoding
        rStream.ReadL(cEncodingPtr, cEncodingLengths[i]);
        // read the content
        rStream.ReadL(contentPtr, contentLengths[i]);

        // Save the contentPtr in the file and later add the fileName
        TDesC8* attachmentName = NULL;
        SaveDataToFileL(contentPtr, attachmentName, *mime);
        CMMSMessageAttachment* attch = CMMSMessageAttachment::NewL(mRFs,
                                       *attachmentName, mimePtr, cidPtr, cLocationPtr);
        delete attachmentName;
        attachmentName = NULL;
        TUint charSetID = charConv->ConvertStandardNameOfCharacterSetToIdentifierL
                          (cEncodingPtr, mRFs);
        TUint mibEnum = charConv->ConvertCharacterSetIdentifierToMibEnumL(
                            charSetID, mRFs);
        attch->SetEncoding(mibEnum);
        CleanupStack::PushL(attch);
        if (i == 0 && startCID == 1)
        {
            attch->SetStarterContentId(TRUE);
        }
        else
        {
            attch->SetStarterContentId(FALSE);
        }
        msg.InsertAttachmentL(attch);
        CleanupStack::Pop(attch);
        CleanupStack::PopAndDestroy(content);
        CleanupStack::PopAndDestroy(cEncoding);
        CleanupStack::PopAndDestroy(cLocation);
        CleanupStack::PopAndDestroy(cid);
        CleanupStack::PopAndDestroy(mime);
    }
    CleanupStack::PopAndDestroy(charConv);
    CleanupStack::Pop(&msg); // msg

    // deallocate the dynamic arrays
    CleanupStack::PopAndDestroy(&contentLengths);
    CleanupStack::PopAndDestroy(&cEncodingLengths);
    CleanupStack::PopAndDestroy(&cLocationLengths);
    CleanupStack::PopAndDestroy(&cidLengths);
    CleanupStack::PopAndDestroy(&mimeLengths);
    return msg;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::Divide
 * This method returns what's left from dividing aNum1 with aNum2.
 * It is used in serialization/deserialization, when calculating the mmsMap
 * -----------------------------------------------------------------------------
 */
TInt CJavaMMSMessageHandler::Divide(TInt aNum1, TInt aNum2)
{
    JELOG2(EWMA);
    if (aNum1 / aNum2 > 0)
    {
        return (aNum1 - (aNum2 *(aNum1 / aNum2)));
    }
    return -1;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::ReadIntL
 * Reads an integer as 4 bytes
 * -----------------------------------------------------------------------------
 */
TInt32 CJavaMMSMessageHandler::ReadIntL(RBufReadStream& aReadStream)
{
    JELOG2(EWMA);
    TInt32 byte1 = aReadStream.ReadUint8L();
    TInt32 byte2 = aReadStream.ReadUint8L();
    TInt32 byte3 = aReadStream.ReadUint8L();
    TInt32 byte4 = aReadStream.ReadUint8L();
    return (byte1 << K24BitsMask) + (byte2 << K16BitsMask) + (byte3
            << K8BitsMask) + byte4;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::ReadInt64L
 * Reads an integer as 8 bytes
 * -----------------------------------------------------------------------------
 */
TInt64 CJavaMMSMessageHandler::ReadInt64L(RBufReadStream& aReadStream)
{
    JELOG2(EWMA);
    TInt64 byte1 = aReadStream.ReadUint8L();
    TInt64 byte2 = aReadStream.ReadUint8L();
    TInt64 byte3 = aReadStream.ReadUint8L();
    TInt64 byte4 = aReadStream.ReadUint8L();
    TInt64 byte5 = aReadStream.ReadUint8L();
    TInt64 byte6 = aReadStream.ReadUint8L();
    TInt64 byte7 = aReadStream.ReadUint8L();
    TInt64 byte8 = aReadStream.ReadUint8L();
    return (byte1 << K56BitsMask) + (byte2 << K48BitsMask) + (byte3
            << K40BitsMask) + (byte4 << K32BitsMask) + (byte5 << K24BitsMask)
           + (byte6 << K16BitsMask) + (byte7 << K8BitsMask) + byte8;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::ResolveMapL
 * Resolves the map of the multimedia message. The map of the multimedia
 * message consists of a byte whose bits indicate which of the
 * multimedia message headers are present. See comments of 'DeSerialize' method
 * for more details about the mms map.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::ResolveMapL(RBufReadStream& aReadStream,
        TInt32 aHeaderLengths[])
{
    JELOG2(EWMA);
    TInt8 mmsMap = aReadStream.ReadUint8L();

    // resolve the mms map
    while (true)
    {
        TInt div = Divide(mmsMap, K64BitsMask);
        if (div >= 0)
        {
            // get reply to app id length
            aHeaderLengths[KReplyToAppIDFieldIndex] = ReadIntL(aReadStream);
            if (div == 0)
                break;
            mmsMap = div;
        }
        div = Divide(mmsMap, K32BitsMask);
        if (div >= 0)
        {
            // get appID length
            aHeaderLengths[KAppIDFieldIndex] = ReadIntL(aReadStream);
            if (div == 0)
                break;
            mmsMap = div;
        }
        div = Divide(mmsMap, K16BitsMask);
        if (div >= 0)
        {
            // get subject length
            aHeaderLengths[KSubjectFieldIndex] = ReadIntL(aReadStream);
            if (div == 0)
                break;
            mmsMap = div;
        }
        div = Divide(mmsMap, K8BitsMask);
        if (div >= 0)
        {
            // get the from length
            aHeaderLengths[KFromFieldIndex] = ReadIntL(aReadStream);
        }
        div = Divide(mmsMap, K4BitsMask);
        if (div >= 0)
        {
            // get to length
            aHeaderLengths[KToFieldIndex] = ReadIntL(aReadStream);
            if (div == 0)
                break;
            mmsMap = div;
        }
        div = Divide(mmsMap, K2BitsMask);
        if (div >= 0)
        {
            // get bcc length
            aHeaderLengths[KBccFieldIndex] = ReadIntL(aReadStream);
            if (div == 0)
                break;
            mmsMap = div;
        }
        if (mmsMap >= 0)
        {
            // get cc length
            aHeaderLengths[KCcFieldIndex] = ReadIntL(aReadStream);
            mmsMap = div;
        }
        break;
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::ResolveHeadersLC
 * Resolves/reads the headers of the multimedia message from the stream
 * and sets them to the internal representation of the multimedia
 * message
 * -----------------------------------------------------------------------------
 */
CMMSMessageInformation& CJavaMMSMessageHandler::ResolveHeadersLC(
    RBufReadStream& aReadStream, TInt32 aHeaderLengths[])
{
    JELOG2(EWMA);
    // priority
    TInt8 prio = aReadStream.ReadUint8L();
    // date
    TInt64 date = ReadInt64L(aReadStream);
    CMMSMessageInformation* msg = CMMSMessageInformation::NewL(KNullDesC, date,
                                  KNullDesC8, NULL);
    CleanupStack::PushL(msg);
    // reply to appID
    HBufC8* replyToAppID =HBufC8::NewL(aHeaderLengths[KReplyToAppIDFieldIndex]);
    CleanupStack::PushL(replyToAppID);
    TPtr8 replyToAppIDPtr(replyToAppID->Des());
    // appID
    HBufC8* appID = HBufC8::NewL(aHeaderLengths[KAppIDFieldIndex]);
    CleanupStack::PushL(appID);
    TPtr8 appIDPtr(appID->Des());
    // subject
    HBufC8* subject = HBufC8::New(aHeaderLengths[KSubjectFieldIndex]);
    CleanupStack::PushL(subject);
    TPtr8 subjectPtr(subject->Des());
    // from
    HBufC8* from = HBufC8::NewL(aHeaderLengths[KFromFieldIndex]);
    CleanupStack::PushL(from);
    TPtr8 fromPtr(from->Des());
    // to
    HBufC8* to = HBufC8::NewL(aHeaderLengths[KToFieldIndex]);
    CleanupStack::PushL(to);
    TPtr8 toPtr(to->Des());
    // bcc
    HBufC8* bcc = HBufC8::NewL(aHeaderLengths[KBccFieldIndex]);
    CleanupStack::PushL(bcc);
    TPtr8 bccPtr(bcc->Des());
    // cc
    HBufC8* cc = HBufC8::NewL(aHeaderLengths[KCcFieldIndex]);
    CleanupStack::PushL(cc);
    TPtr8 ccPtr(cc->Des());
    for (TInt i = 0; i < KMmsMapLength; i++)
    {
        if (aHeaderLengths[i] > 0)
        {
            // read the first aHeaderLengths[i] bytes
            switch (i)
            {
            case(KReplyToAppIDFieldIndex):
                // reply to app ID
                aReadStream.ReadL(replyToAppIDPtr, aHeaderLengths[i]);
                break;
            case(KAppIDFieldIndex):
                // app ID
                aReadStream.ReadL(appIDPtr, aHeaderLengths[i]);
                break;
            case(KSubjectFieldIndex):
                // subject
                aReadStream.ReadL(subjectPtr, aHeaderLengths[i]);
                break;
            case(KFromFieldIndex):
                // from
                aReadStream.ReadL(fromPtr, aHeaderLengths[i]);
                break;
            case(KToFieldIndex):
                // to
                aReadStream.ReadL(toPtr, aHeaderLengths[i]);
                break;
            case(KBccFieldIndex):
                // bcc
                aReadStream.ReadL(bccPtr, aHeaderLengths[i]);
                break;
            case(KCcFieldIndex):
                // cc
                aReadStream.ReadL(ccPtr, aHeaderLengths[i]);
                break;
            default:
                // Nothing to do
                break;
            }
        }
    }
    // create the CMMSMessageInformation
    msg->SetApplicationIdL(appIDPtr);
    msg->SetReplyToApplicationIdL(replyToAppIDPtr);
    msg->SetMessageSubjectL(subjectPtr);
    switch (prio)
    {
    case KPriorityLow:
        //low
        msg->SetMessagePriority(EMmsPriorityLow);
        break;
    case KPriorityNormal:
        //normal
        msg->SetMessagePriority(EMmsPriorityNormal);
        break;
    case KPriorityHigh:
        //high
        msg->SetMessagePriority(EMmsPriorityHigh);
        break;
    default:
        // Nothing to do
        break;
    }
    // parse the addresses and set them to the message
    ParseAndSetAddressL(EMsvRecipientTo, toPtr, *msg);
    ParseAndSetAddressL(EMsvRecipientBcc, bccPtr, *msg);
    ParseAndSetAddressL(EMsvRecipientCc, ccPtr, *msg);
    // pop the pointers before deleting them
    CleanupStack::PopAndDestroy(cc);
    CleanupStack::PopAndDestroy(bcc);
    CleanupStack::PopAndDestroy(to);
    CleanupStack::PopAndDestroy(from);
    CleanupStack::PopAndDestroy(subject);
    CleanupStack::PopAndDestroy(appID);
    CleanupStack::PopAndDestroy(replyToAppID);
    return *msg;
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::ParseAndSetAddressL
 * Splits the concatenated addresses (";" separated string/descriptor)
 * into sepparate tokens and sets them individually to the internal
 * representation of the multimedia message.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::ParseAndSetAddressL(
    TMsvRecipientTypeValues aAddressType,
    TDes8& aAddr,
    CMMSMessageInformation& aMsg)
{
    JELOG2(EWMA);
    if (aAddr.Length() == 0)
    {
        return;
    }
    else
    {
        while (true)
        {
            TInt off = aAddr.Locate(';');
            if (off != KErrNotFound)
            {
                TPtrC8 left = aAddr.Left(off);
                aMsg.AddAddressL(aAddressType, left);
                TPtrC8 right = aAddr.Right(aAddr.Length() - off - 1);
                aAddr.Copy(right);
            }
            else
            {
                aMsg.AddAddressL(aAddressType, aAddr);
                break;
            }
        }
    }
}

/* -----------------------------------------------------------------------------
 * CJavaMMSMessageHandler::SaveDataToFileL
 * Save data to the D-drive. MMS engine assume that attachments are
 * always files. It also help us to use less RAM.
 * -----------------------------------------------------------------------------
 */
void CJavaMMSMessageHandler::SaveDataToFileL(const TDesC8& aDataArray,
        TDesC8*& aFileName,
        const TDesC8& aDataType)
{
    JELOG2(EWMA);
    //// create a dummy attached file here on the D (temp) drive
    RFile attachementFile;
    TInt err = 0;
    TBuf<KMaxFileName> fileName;
    int i = 1;
    CDocumentHandler* docHandler = CDocumentHandler::NewLC();
    do
    {
        fileName.SetLength(0);
        //fileName.Append(KTempDrive); // Add the drive name
        TChar driveInfo;
        DriveInfo::GetDefaultDrive(DriveInfo::EDefaultRam, driveInfo);
        fileName.Append(driveInfo);
        fileName.Append(KTempDriveExt);
        fileName.Append(KTempMmsAttachmentName); // Add the drive name
        fileName.AppendNum((int) this);
        fileName.AppendNum(i++);
        // we delegate the DocHandler to append the file extension
        TDataType mimeType(aDataType);
        docHandler->CheckFileNameExtension(fileName, mimeType);
        err = attachementFile.Create(mRFs, fileName, EFileWrite
                                     | EFileShareExclusive);
    }
    while (KErrAlreadyExists == err);
    CleanupStack::PopAndDestroy(docHandler);
    if (err != KErrNone)
    {
        User::LeaveIfError(err);
    }
    else
    {
        // Get the file name, so that we can delete it later
        java::util::S60CommonUtils::ConvertWiderToNarrowL(fileName,
                (TDesC8*&) aFileName);

        // add data to the newly created attached file.
        attachementFile.Write(aDataArray);
        attachementFile.Flush();
        attachementFile.Close();
    }
}

} //namespace wma
} //namespace java

