/*
* 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 FILES
#include <cmsvattachment.h>
#include <cmsvmimeheaders.h>
#include <mmsvattachmentmanager.h>

#include "logger.h"
#include "fs_methodcall.h"
#include "s60commonutils.h"
#include "cmmsmessageattachment.h"
#include "cmmsplatformservices60impl.h"

//constants
const TInt KMaxAppIDLength = 34;

using namespace java::push;
using namespace java::util;

namespace java
{
namespace wma
{
/* ================= MEMBER FUNCTIONS =======================
 * constructor
 *------------------------------------------------------------------------------
 * private CMMSPlatformServiceS60Impl
 *
 *------------------------------------------------------------------------------
 */
CMMSPlatformServiceS60Impl::CMMSPlatformServiceS60Impl(
    const bool aServerConnection,
    HBufC* aServerApplicationId,
    ConnectionListener* aListener)
        :CActive(EPriorityNormal), java::util::FunctionServer("CMMSPlatformService"),
        mIsSending(false), mServerConnection(aServerConnection), mNewMessages(0),
        mServerApplicationId(aServerApplicationId)
{
    JELOG2(EWMA);
    //Creates a new thread and starts active scheduler
    //This results in call to doServerSideInit()
    createServerToNewThread();
    mSendMsgArray = NULL;
    mMessageListener = aListener;
}
/**
 * Gets the instance of OS Specific class which implements the interface
 * CMMSPlatformService.
 * @param aJni - jni interface pointer
 * @param aPeer - java side peer reference.
 * @param aServerConnection - boolean value to identify client/server
 *                            connection.
 * @param aUri - uri on which the connection to be opened.
 * @param aListener - listener to be notified about incoming messages.
 */
MMSPlatformService* MMSPlatformService::getInstance(JNIEnv& aJni,
        jobject aPeer,const bool aServerConnection,const std::wstring& aUri,
        ConnectionListener* aListener)
{
    return CMMSPlatformServiceS60Impl::New(aJni,aPeer,aServerConnection,aUri,
                                           aListener);
}


CMMSPlatformServiceS60Impl* CMMSPlatformServiceS60Impl::New(JNIEnv& aJni,
        jobject aPeer,const bool aServerConnection,const std::wstring& aUri,
        ConnectionListener* aListener)
{
    JELOG2(EWMA);
    HBufC* serverApplicationId = NULL;
    int error = KErrNone;
    if (aServerConnection)
    {
        serverApplicationId = S60CommonUtils::wstringToDes(
                                  (aUri.substr(7)).c_str());
    }
    CMMSPlatformServiceS60Impl * self = new CMMSPlatformServiceS60Impl(
        aServerConnection, serverApplicationId, aListener);
    error = self->getError();
    if (KErrNone == error)
    {
        //Attaches the function server thread to VM, to make callbacks to java
        //This results in call to vmAttached()
        self->attachToVm(aJni, aPeer);
        LOG(EWMA, EInfo, "CMMSPlatformServiceS60Impl Successfully attached to VM");
        TRAP(error, self->ConstructL());
    }
    if (error != KErrNone)
    {
        ELOG1(EWMA, "CMMSPlatformServiceS60Impl : Error while opening %d", error);
        // If there is any error while opening close the connection
        self->closeConnection(aJni,aPeer);
        delete self;
        throw error;
    }
    return self;
}

/**------------------------------------------------------------------------------
 * EPOC default constructor can leave.
 * private ConstructL
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::ConstructL()
{
    JELOG2(EWMA);
    User::LeaveIfError(mRFs.Connect());
    User::LeaveIfError(mRFs.ShareProtected());
    CreateSendMessageArrayL(); // create send array
    mMessageDeliveryDate = TTime(0);
    mMmsMessagePriority = EMmsPriorityHigh;
    mMessageHandler = CJavaMMSMessageHandler::NewL(mRFs);
    // If server connection then check for messages already available in MMS
    // message store.
    if (mServerConnection)
    {
        LOG(EWMA, EInfo , "CMMSPlatformServiceS60Impl::ConstructL() Check for"
            "any messages availble in message store");
        CallMethodL(this, &CMMSPlatformServiceS60Impl::NotifyReceivedMessageL,
                    this);
    }
}


void CMMSPlatformServiceS60Impl::vmAttached()
{
    JELOG2(EWMA);
    jclass sessionClass = mJniEnv->FindClass(
                              "com/nokia/mj/impl/mms/MMSConnectionImpl");
    mSendCallBackMethodId = mJniEnv->GetMethodID(sessionClass,
                            "messageSentCallback", "(I)V");

}
/**
 * Returns total number of messages that are arrived
 * from opening the connection
 */
int CMMSPlatformServiceS60Impl::getNumberOfMessages()
{
    JELOG2(EWMA);
    return mNewMessages;
}
/**
 * Does the MMS Engine side initializations .Registers with MMS Engine
 * for notification about incoming message in case of server connection.
 */
void CMMSPlatformServiceS60Impl::doServerSideInit()
{
    JELOG2(EWMA);
    FunctionServer::doServerSideInit();
    CActiveScheduler::Add(this);
    TRAPD(error,
    {
        mSession = CMsvSession::OpenSyncL(*this);
        mClientMtmRegistry = CClientMtmRegistry::NewL(*mSession);
        mMmsClient = (CMmsClientMtm *) mClientMtmRegistry->NewMtmL(
                         KUidMsgTypeMultimedia);
        mSendTMsvIdArray = RArray<TMsvId>(KArrayGranularity);
        mMmsApplicationAdapter = CMmsApplicationAdapter::NewL();
        if (mServerConnection)
        {
            mServerMmsMtm = (CMmsClientMtm *) mClientMtmRegistry->NewMtmL(
                                KUidMsgTypeMultimedia);
            mReceivedMsgArray = new(ELeave) CArrayPtrSeg
            <CMMSMessageInformation>(KArrayGranularity);
            TBool isRegister = EFalse;
            // If launched by push the application would have already registered
            // with mms engine . check before registering.
            LOG(EWMA, EInfo , "Checking whether application is already"
                "registered or not");
            isRegister = mMmsApplicationAdapter->RegisteredL
                         (*mServerApplicationId);
            // If not registered , register now.
            if (!isRegister)
            {
                TMsvId folderId;
                LOG(EWMA, EInfo , "Registering the AppId with MMS engine");
                mMmsApplicationAdapter->RegisterL(*mServerApplicationId,folderId);
                isRegister = true;
            }

            TMsvId tmsvId = mMmsApplicationAdapter->FolderIdL(
                                *mServerApplicationId);
            if (0 != tmsvId)
            {
                mRegisterCMsvEntry = mSession->GetEntryL(tmsvId);
                // Add listener to the created entry for that application Id
                mRegisterCMsvEntry->AddObserverL(*this);
            }

        }
    });
    mError = error;
}

/* -----------------------------------------------------------------------------
 * CMMSPlatformServiceS60Impl::Send
 * Sends a multimedia message to its recipients.
 * -----------------------------------------------------------------------------
 */
int CMMSPlatformServiceS60Impl::send(JNIEnv& aJniEnv,
                                     jbyteArray aBytes, int aOffset, int aLength, jstring /*aDestination*/)
{
    JELOG2(EWMA);
    // put the jbyteArray into the mSendBuffer
    delete mSendBuf; // avoid the memory leak
    mSendBuf = NULL;
    TRAPD(error,
    {
        mSendBuf = CBufFlat::NewL(aLength);

        // Extends the size of the buffer by the required size
        mSendBuf->ExpandL(aOffset, aLength);

        // Transfer the data from java
        TPtr8 sendBufPtr = mSendBuf->Ptr(0);
        S60CommonUtils::CopyToNative(aJniEnv, aBytes, aOffset, aLength,sendBufPtr);
        // If the object is active then it is currently sending a message
        // it will also send the newly added message.
        iStatus = KRequestPending;
        SetActive();
        CallMethodL(this, &CMMSPlatformServiceS60Impl::mmsRequestComplete,
                    KErrNone,this);
    });
    ELOG1(EWMA, "CMMSPlatformServiceS60Impl : status of send %d",error);
    return error;
}

/*------------------------------------------------------------------------------
 * protected SetObjectActive locally
 * .
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::mmsRequestComplete(int result)
{
    JELOG2(EWMA);
    TRequestStatus *status = &iStatus;
    User::RequestComplete(status, result);
}

/*------------------------------------------------------------------------------
 * Notifies java side about success or failure of sending MMS message
 * .
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::NotifySend(JNIEnv& aJni,jobject aPeer,
        int aError)
{
    JELOG2(EWMA);
    aJni.CallVoidMethod(aPeer, mSendCallBackMethodId, aError);
    mIsSending = false;
}

/*------------------------------------------------------------------------------
 * RunL from CActive
 * .
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::RunL()
{
    JELOG2(EWMA);
    if (iStatus.Int() ==KErrNone)
    {
        mIsSending = true;
        SendMultiPartMessageL();
    }
}

/*------------------------------------------------------------------------------
 * protected DoCancel
 * Cancel the sending of the message. If the message is in the creation phase
 * we can cancel the sending of the message. If the deviec is actually sending
 * the message we cannot cancel it.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::DoCancel()
{
    // clean up the entry
    JELOG2(EWMA);
}
/*------------------------------------------------------------------------------
 * protected RunError
 * .
 *------------------------------------------------------------------------------
 */
TInt CMMSPlatformServiceS60Impl::RunError(TInt aError)
{
    JELOG2(EWMA);
    ELOG1(EWMA, "+ In runerror %d this %d", aError);
    NotifySend(*mJniEnv, mJavaPeerObject, aError);
    return KErrNone;
}
/* -----------------------------------------------------------------------------
 * CMMSPlatformServiceS60Impl::RetrieveMessageL
 * Retrieves the first received message from the queue. After delivering the
 * message to the caller, the message is removed from the queue.
 * -----------------------------------------------------------------------------
 */
jbyteArray CMMSPlatformServiceS60Impl::retrieveMessage(JNIEnv& aJni)
{
    JELOG2(EWMA);
    // check if there is something in the queue
    if (mReceivedMMSIDs.Count() <= 0)
    {
        return NULL;
    }
    // retrieve the first message whose ID is in the queue
    TMsvId id = mReceivedMMSIDs[0];
    mReceivedMMSIDs.Remove(0);

    CMMSMessageInformation* mms = ReceivedMessage(id);
    if (!mms)
    {
        return NULL;
    }
    CleanupStack::PushL(mms);

    // serialize the received MMS
    LOG(EWMA, EInfo , "Serializing the message before giving to java");
    CBufFlat& receiveBuffer = mMessageHandler->SerializeLC(*mms);
    // receiveBuffer is on the cleanup stack
    TPtr8 receiveBufferPtr = receiveBuffer.Ptr(0);
    //get the jbyteArray
    TInt dataLength = receiveBuffer.Capacity();
    jbyteArray array = aJni.NewByteArray(dataLength);
    S60CommonUtils::CopyToJava(aJni, receiveBufferPtr, array, 0,dataLength);
    CleanupStack::PopAndDestroy(&receiveBuffer); // receiveBuffer
    CleanupStack::PopAndDestroy(mms);
    return array;
}

/*------------------------------------------------------------------------------
 * public CreateMessageL
 * create an empty messsage in draft folder. Later we will add the information
 * (attachments addresses, subject etc)
 *------------------------------------------------------------------------------
 */
TMsvId CMMSPlatformServiceS60Impl::CreateMessageL()
{
    JELOG2(EWMA);
    TMsvId serviceId = mMmsClient->DefaultServiceL();
    // Context must be set to parent folder for CreateMessageL
    mMmsClient->SwitchCurrentEntryL(KMsvDraftEntryId);
    mMmsClient->CreateMessageL(serviceId);
    CMsvEntry& cmsvEntry = mMmsClient->Entry();
    TMsvId msvId = cmsvEntry.EntryId();
    return msvId;
}

/*------------------------------------------------------------------------------
 * private DeleteSentMessage
 * When the message is gone from the device. Delete the message from
 * the sent folder.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::DeleteSentMessageL(TMsvId aMsvId)
{
    JELOG2(EWMA);
    TInt index = mSendTMsvIdArray.Find(aMsvId);
    if (KErrNotFound == index)
    {
        return;
    }
    mSendTMsvIdArray.Remove(index);
    DeleteMmsMessageL(aMsvId);
}

/*------------------------------------------------------------------------------
 * public DeleteMessage
 * Delete the message from message store. We are deleting two type of
 * multimedia messages.Messages that we send from Java side. When they
 * are gone from the device. We delete the message from the sent folder.
 * Second is when we receive the message we delete it after
 * reading it from Java side.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::DeleteMmsMessageL(TMsvId aMsvId)
{
    JELOG2(EWMA);
    LoadMessageL(aMsvId);
    CMsvEntry& cmsvEntry = mMmsClient->Entry();
    TMsvEntry tmsvEntry(cmsvEntry.Entry());

    if ((tmsvEntry.iMtm != KUidMsgTypeMultimedia)||(tmsvEntry.InPreparation()))
    {
        // It is not MMS  or the entry is not ready
        return;
    }
    //Taking a handle to the Sent folder...
    TMsvSelectionOrdering sort = cmsvEntry.SortType();
    sort.SetShowInvisibleEntries(ETrue);
    // we want to handle also the invisible entries
    // Take a handle to the parent entry
    CMsvEntry* parentEntry = CMsvEntry::NewL(mMmsClient->Session(),
                             tmsvEntry.Parent(), sort);
    CleanupStack::PushL(parentEntry); //
    TRAPD(err, parentEntry->DeleteL(aMsvId));
    if (err != KErrNone)
    {
        mSession->RemoveEntry(aMsvId);
    }
    CleanupStack::PopAndDestroy(parentEntry);
}
/*------------------------------------------------------------------------------
 * public LoadMessage
 * laod the message  from the given TMsvId,
 * After this call message will be current message
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::LoadMessageL(TMsvId aMessageEntry)
{
    JELOG2(EWMA);
    mMmsClient->SwitchCurrentEntryL(aMessageEntry);
    mMmsClient->LoadMessageL();
}
/*------------------------------------------------------------------------------
 * AddAttachments
 * add attachment, location, contentId, and set the message root here
 *
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::AddAttachmentsL(
    CMMSMessageInformation* aMidMessageInformation)
{
    JELOG2(EWMA);
    // Add attachments also set the started ID
    CMsvStore* store = mMmsClient->Entry().EditStoreL();
    CleanupStack::PushL(store);
    TInt count = aMidMessageInformation->AttachmentCount();
    for (int i = 0; i < count; i++)
    {
        CMMSMessageAttachment& messageAttachment =
            aMidMessageInformation->Attachment(i);
        TDesC& fileName = messageAttachment.AttachmentName();
        TDesC8& mimeType8 = messageAttachment.AttachmentType();
        TDesC8& contentLocation8 = messageAttachment.ContentLocationL();
        TDesC8& contentId8 = messageAttachment.ContentIdL();
        TInt charEncoding = messageAttachment.Encoding();
        TDesC* contentLocation = NULL;
        S60CommonUtils::ConvertNarrowToWiderLC(contentLocation8,contentLocation);

        CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewLC();
        // set the attachment mime type in the CMsvMimeHeaders
        if (&mimeType8)
        {
            TInt position = mimeType8.Find(KSlash);
            if (position > 0 && position < mimeType8.Length() - 1)
            {
                mimeHeaders->SetContentTypeL(mimeType8.Left(position));
                mimeHeaders->SetContentSubTypeL(mimeType8.Mid(position + 1));
            }
            else
            {
                mimeHeaders->SetContentTypeL(mimeType8);
            }
        }
        CMsvAttachment* attachment = CMsvAttachment::NewL(
                                         CMsvAttachment::EMsvFile);
        if (contentLocation)
        {
            mimeHeaders->SetContentLocationL(*contentLocation);
        }
        // contentId8 can be NULL
        if (&contentId8)
        {
            mimeHeaders->SetContentIdL(contentId8);
        }
        // set Charset in attachment
        if (charEncoding >= 0)
        {
            mimeHeaders->SetMimeCharset(charEncoding);
        }
        RFile attaFile;
        User::LeaveIfError(attaFile.Open(mRFs, fileName,
                                         EFileShareReadersOnly | EFileRead));
        CleanupClosePushL(attaFile);
        TMsvAttachmentId attaId = 0;
        // attachment ownerhip will be transferred to Attachment Manager
        // It must not be pushed onto the cleanupStack before calling
        // CreateAttachment2L
        HBufC8* emptyMime = NULL;
        if (&mimeType8)
        {
            mMmsClient->CreateAttachment2L(*store, // edit store
                                           attaFile, // open file handle
                                           mimeType8,
                                           // combination type like image/jpeg
                                           *mimeHeaders, attachment, attaId);
        }
        else
        {
            emptyMime = HBufC8::NewLC(0);
            mMmsClient->CreateAttachment2L(*store, // edit store
                                           attaFile, // open file handle
                                           *emptyMime,
                                           // combination type like image/jpeg
                                           *mimeHeaders, attachment, attaId);
        }
        // Now Attachment Manager owns the attachment object, so assign it NULL.
        attachment = NULL;
        // check also isStarted Id
        if (messageAttachment.IsStarterContentId())
        {
            mMmsClient->SetMessageRootL(attaId);
        }
        if (emptyMime)
        {
            CleanupStack::PopAndDestroy(emptyMime);
        }
        CleanupStack::PopAndDestroy(&attaFile); // attaFile.Close()
        CleanupStack::PopAndDestroy(mimeHeaders); // mimeHeaders
        CleanupStack::PopAndDestroy(contentLocation); // contentLocation
    } // end of add attachment for loop

    store->CommitL();
    CleanupStack::PopAndDestroy(store); // store
}
/*------------------------------------------------------------------------------
 * public SetSubject
 * LoadMessage is called before this method call other wise subject can be set
 * to wrong TMsvId
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SetSubjectL(const TDesC& aSubject)
{
    JELOG2(EWMA);
    mMmsClient->SetSubjectL(aSubject);
}
/*------------------------------------------------------------------------------
 * public AddRecipient
 * LoadMessage is called before this method call other wise address can be set
 * to wrong TMsvId
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::AddRecipientL(const TDesC& aAddress,
        TMmsRecipients aAddressType)
{
    JELOG2(EWMA);
    mMmsClient->AddAddresseeL(aAddressType, aAddress);
}
/*------------------------------------------------------------------------------
 * public SetApplicationId
 * LoadMessage is called before this method call, other wise applicationId can
 * be set to wrong TMsvId.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SetApplicationIdL(const TDesC& aApplicationId)
{
    JELOG2(EWMA);
    if (aApplicationId.Length() > 0)
    {
        TMsvId mvsEntry = mMmsClient->Entry().EntryId();
        mMmsApplicationAdapter->SetApplicationIdL(mvsEntry,aApplicationId);
    }
}
/*------------------------------------------------------------------------------
 * SetReplyToApplicationId
 * LoadMessage is called before this method call, other wise
 * ReplyToApplicationId can be set to wrong TMsvId.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SetReplyToApplicationIdL(
    const TDesC& aReplyToApplicationId)
{
    JELOG2(EWMA);
    if (aReplyToApplicationId.Length() > 0)
    {
        TMsvId mvsEntry = mMmsClient->Entry().EntryId();
        mMmsApplicationAdapter->SetReplyToApplicationIdL(mvsEntry,
                aReplyToApplicationId);
    }
}
/*------------------------------------------------------------------------------
 * public SendMultiPartMessageL
 * ASyn method call to send the message.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SendMultiPartMessageL()
{
    JELOG2(EWMA);
    // convert the byte array into a native multimedia message representation
    LOG(EWMA, EInfo , "Converting the byte array into a native multimedia"
        "message representation");
    CMMSMessageInformation& mms = mMessageHandler->DeSerializeL(*mSendBuf);
    // send it towards the MMS engine
    mSendMsgArray->AppendL(&mms);
    while (mSendMsgArray->Count() > 0)
    {
        CMMSMessageInformation* midMessageInformation =
            mSendMsgArray->At(0);
        CleanupStack::PushL(midMessageInformation);
        SendMmsMessageL(midMessageInformation);
        mSendMsgArray->Delete(0);
        CleanupStack::Pop(midMessageInformation); // midMessageInformation
        delete midMessageInformation;
        midMessageInformation = NULL;
    }
}
/*------------------------------------------------------------------------------
 * public SendMultiMediaMessageL
 * Message is send in the following steps
 * 1) Create an empty message in the draft folder
 * 2) Get the multimeida messsage information from the CMMSMessageInformation
 *    object and set that information to the newly created multimedia message
 *    in the following order
 *        i) Set the subject of the message (Optional)
 *        ii)Add the destination addresses (to, cc, bcc)
 *        iii)Add attachments to the message. Attachments are saved in the
 *          message store.
 *        vi)Set message setting (Date, time, expiry time, priority, visibility
 *         of the sender)
 * 3) Save the formed message in the message store
 * 4) Send the message by using mmsMTMClient.
 * 5) When the message is gone from the device delete the message from the sent
 *    folder.
 *-----------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SendMmsMessageL(
    CMMSMessageInformation* aMidMessageInformation)
{
    JELOG2(EWMA);
    TMsvId msvId = CreateMessageL();
    // Add subject
    TDesC8& str8 = aMidMessageInformation->MessageSubjectL();
    TDesC* str = NULL;
    if (NULL != &str8)
    {
        S60CommonUtils::ConvertNarrowToWiderL(str8, str);
        SetSubjectL(*str);
        delete str;
        str = NULL;
    }
    // Add EMsvRecipientTo addresses
    TInt i = 0;
    TInt count = aMidMessageInformation->AddressesCount(EMsvRecipientTo);
    for (i = 0; i < count; i++)
    {
        AddRecipientL(aMidMessageInformation->MessageAddressL(
                          EMsvRecipientTo, i), EMsvRecipientTo);
    }
    // Add EMsvRecipientCc addresses
    count = aMidMessageInformation->AddressesCount(EMsvRecipientCc);
    for (i = 0; i < count; i++)
    {
        AddRecipientL(aMidMessageInformation->MessageAddressL(
                          EMsvRecipientCc, i), EMsvRecipientCc);
    }
    // Add EMsvRecipientBcc addresses
    count = aMidMessageInformation->AddressesCount(EMsvRecipientBcc);
    for (i = 0; i < count; i++)
    {
        AddRecipientL(aMidMessageInformation->MessageAddressL(
                          EMsvRecipientBcc, i), EMsvRecipientBcc);
    }
    // Add attachments also set the started ID
    AddAttachmentsL(aMidMessageInformation);
    // set date and time
    TInt64 time = aMidMessageInformation->MessageTime();
    SetMessageSendDate(time);
    mMmsMessagePriority = aMidMessageInformation->MessagePriority();
    // set priorty, delivery time, message class, ExpiryInterval,
    // sender visibility and delivery report
    SetMessageSettingL();

    // set application-id and reply-to-application-id
    TDesC8& id8 = aMidMessageInformation->ApplicationIdL();
    TDesC* id = NULL;
    if (NULL != &id8)
    {
        S60CommonUtils::ConvertNarrowToWiderL(id8, id);
        SetApplicationIdL(*id);
        delete id;
        id = NULL;
    }
    // Set reply-to-application-Id
    TDesC8& rId8 = aMidMessageInformation->ReplyToApplicationIdL();
    if (NULL != &rId8)
    { // add reply-to-application-id to currently loaded message
        S60CommonUtils::ConvertNarrowToWiderL(rId8, id);
        SetReplyToApplicationIdL(*id);
        delete id;
        id = NULL;
    }
    SendMmsMessageL(msvId); // send it now
    LOG(EWMA, EInfo, "CMMSPlatformServiceS60Impl::SendMmsMessageL Notifying"
        "java about the message send status");
    NotifySend(*mJniEnv, mJavaPeerObject, KErrNone);
}
/*-----------------------------------------------------------------------------
 * private SendMmsMessageL
 * Send the message from the device. This method is called when the
 * message is ready to send. All the necessary fields are filled.
 * message is send by using mmsClient mtm.
 *-----------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SendMmsMessageL(TMsvId aSendMmsMsgId)
{
    JELOG2(EWMA);
    TMsvEntry tmvsEntry = mMmsClient->Entry().Entry();
    tmvsEntry.SetInPreparation(EFalse);
    tmvsEntry.SetVisible(ETrue);
    mMmsClient->Entry().ChangeL(tmvsEntry);
    // Save the message here to save the last changes made above.
    mMmsClient->SaveMessageL();
    // left on CleanupStack
    CMsvOperationWait* wait = CMsvOperationWait::NewLC();
    wait->iStatus = KRequestPending;
    CMsvOperation* op = NULL;
    op = mMmsClient->SendL(wait->iStatus);
    wait->Start();
    CleanupStack::PushL(op);
    CActiveScheduler::Start();
    mSendTMsvIdArray.AppendL(aSendMmsMsgId);
    // The following is to ignore the completion of other active objects.
    // It is not needed if the app has a command absorbing control.
    while (wait->iStatus.Int() == KRequestPending)
    {
        CActiveScheduler::Start();
    }
    CleanupStack::PopAndDestroy(op);
    CleanupStack::PopAndDestroy(wait);
}
/*------------------------------------------------------------------------------
 * protected Set Message Setting
 * Set the class, ExpiryInterval, delivery time interval, priority and
 * visibility of the sender.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SetMessageSettingL()
{
    JELOG2(EWMA);
    mMmsClient->RestoreSettingsL();

    mMmsClient->SetMessageClass(EMmsClassPersonal);
    // X-Mms-Delivery-Time
    mMmsClient->SetDeliveryDate(mMessageDeliveryDate);

    // X-Mms-Priority
    mMmsClient->SetMessagePriority(mMmsMessagePriority);
    mMmsClient->SetSenderVisibility(EMmsSenderVisibilityShow);
    mMmsClient->SetDeliveryReport(EMmsDeliveryReportYes);
}
/*------------------------------------------------------------------------------
 * public SetMessageSendDate
 * Set message time in Java format
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::SetMessageSendDate(TInt64& aLong)
{
    JELOG2(EWMA);
    if (aLong != 0)
    {
        mMessageDeliveryDate = S60CommonUtils::JavaTimeToTTime(aLong);
    }
    else
    {
        mMessageDeliveryDate = TTime(0);
    }
}
/*------------------------------------------------------------------------------
 * public HandleSessionEventL
 * EMsvEntriesMoved,
 *
 *    One or more entries have been moved.
 *
 *    aArg1 is a CMsvEntrySelection containing the IDs of the moved entries.
 *    aArg2 is the TMsvId of the new parent.
 *    aArg3 is the TMsvId of the old parent entry.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::HandleSessionEventL(
    TMsvSessionEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* /*aArg3*/)
{
    JELOG2(EWMA);
    switch (aEvent)
    {
    case EMsvEntriesMoved:
    {
        TMsvId parentId = KMsvNullIndexEntryId;
        parentId = *(TMsvId*) aArg2;

        if (parentId == KMsvSentEntryId)
        {
            // We take the created entries into a selection
            CMsvEntrySelection* entries =
                static_cast<CMsvEntrySelection*>(aArg1);
            TInt count = entries->Count();
            LOG1(EWMA, EInfo, "CMMSPlatformServiceS60Impl::HandleSessionEventL"
                 "Count = %d ", count);
            for (TInt i = 0; i < count; i++)
            {
                LOG1(EWMA, EInfo, "CMMSPlatformServiceS60Impl::"
                     "HandleSessionEventL number = %d ", i);
                TRAP_IGNORE(DeleteSentMessageL(entries->At(i)));
            }// end of for loop
        } // end of parentid if
        break;
    }
    default:
        break;
    }
}
/*------------------------------------------------------------------------------
 * private CreateSendMessageArray
 * Create the send message TMsvId array. It will be later used to delete the
 * messages. We should NOT be deleting the messages send by other application.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::CreateSendMessageArrayL()
{
    JELOG2(EWMA);
    ClearAndDestroySendMessageArray();
    mSendMsgArray = new(ELeave) CArrayPtrSeg<CMMSMessageInformation>
    (KArrayGranularity);
}
/*------------------------------------------------------------------------------
 * private ClearAndDestroySendMessageArray
 *
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::ClearAndDestroySendMessageArray()
{
    JELOG2(EWMA);
    if (mSendMsgArray)
    {
        mSendMsgArray->ResetAndDestroy();
    }
    delete mSendMsgArray;
    mSendMsgArray = NULL;
}

/*------------------------------------------------------------------------------
 * public ClearSendMessageArray
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::ClearSendMessageArray()
{
    JELOG2(EWMA);
    if (mSendMsgArray)
    {
        mSendMsgArray->Reset();
    }
}

/*------------------------------------------------------------------------------
 * public NotifyReceivedMessage
 * Message arrive in the device. User was prompted for the MMS message.
 * But user do not want to start the midlet. Later (after some time)
 * user start the midlet.
 * MIDlet will receive all the messages. Following method will notify
 * the listener for the received messages.
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::NotifyReceivedMessageL()
{
    JELOG2(EWMA);
    TMsvId folderId = KMsvNullIndexEntryId;
    folderId = mMmsApplicationAdapter->FolderIdL(*mServerApplicationId);

    if (KMsvNullIndexEntryId != folderId)
    {
        CMsvEntry* cEntry = mSession->GetEntryL(folderId);
        // Show invisible entries
        // Sort the entries according to date;
        cEntry->SetSortTypeL(TMsvSelectionOrdering(KMsvNoGrouping,
                             EMsvSortByDate, ETrue));
        //get the selection array of the messages in sorted order by date
        CMsvEntrySelection* msvEntrySelection =
            cEntry->ChildrenWithTypeL(KUidMsvMessageEntry);

        //Process each entry, one at a time.
        TInt count = msvEntrySelection->Count();
        for (TInt i = 0; i < count; i++)
        {
            // this checks the entry and handles it if it is targeted
            // to midp app we are in startup we do not want to leave here
            TRAP_IGNORE(DoReceiveMessageL(msvEntrySelection->At(i)));
        }// end of for loop
        delete msvEntrySelection;
        msvEntrySelection = NULL;
        delete cEntry;
        cEntry = NULL;
    }
} // end of the method NotifyReceivedMessageL

/*------------------------------------------------------------------------------
 * public HandleEntryEventL
 * observer the entry for the specific application Id.
 * if a new message is arrived in this folder notify the listener
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::HandleEntryEventL(
    TMsvEntryEvent aEvent, TAny* aArg1, TAny*, TAny*)
{
    JELOG2(EWMA);
    switch (aEvent)
    {
    case MMsvSessionObserver::EMsvEntriesChanged:
    {
        // We take the created entries into a selection
        CMsvEntrySelection* entries =
            static_cast<CMsvEntrySelection*>(aArg1);
        //Process each created entry, one at a time.
        for (TInt i = 0; i < entries->Count(); i++)
        { // this checks the entry and handles it if it is targeted
            // to midp app
            TRAP_IGNORE(DoReceiveMessageL(entries->At(i)));
        } // end of for loop
    }
    break;
    }
}

/*------------------------------------------------------------------------------
 * private DoReceiveMessageL
 * ReceiveMessageL MUST be call from DoReceiveMessageL
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::DoReceiveMessageL(TMsvId aMsvId)
{
    JELOG2(EWMA);
    ReceiveMessageL(aMsvId);
}
/*------------------------------------------------------------------------------
 * protected ReceiveMessageL
 *
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::ReceiveMessageL(TMsvId aMsvId)
{
    JELOG2(EWMA);
    CMsvEntry* csvEntry = mSession->GetEntryL(aMsvId);
    TMsvEntry msvEntry = csvEntry->Entry();

    if (msvEntry.iMtm != KUidMsgTypeMultimedia)
    { // It is not MMS, free up the memory of the CMsvEntry
        delete csvEntry;
        csvEntry = NULL;
        return;
    }

    if (msvEntry.InPreparation() > 0)
    { // message is not ready free up the memory of the CMsvEntry
        delete csvEntry;
        csvEntry = NULL;
        return; // entry is not ready to read
    }

    mServerMmsMtm->SwitchCurrentEntryL(aMsvId);
    mServerMmsMtm->LoadMessageL();

    // Check application ID
    TPtrC applicationId = mMmsApplicationAdapter->ApplicationIdL(aMsvId);
    // Local Copy of the Application ID
    TDesC *localCopy_AppId = NULL;
    S60CommonUtils::CopyWiderL(applicationId, (TDesC16*&) localCopy_AppId);
    // AppId and replyToAppId is not working yet so cannot be tested.
    if (mServerApplicationId->Compare(*localCopy_AppId) != 0)
    { // received message is not for this connector
        delete csvEntry;
        csvEntry = NULL; // free the memory
        return;
    }
    TPtrC replyToappId = mMmsApplicationAdapter->ReplyToApplicationIdL(aMsvId);
    // Local Copy of reply-to-Application-Id  //TDesC16*&
    TDesC *localCopy_Reply_To_AppId = NULL;
    S60CommonUtils::CopyWiderL(replyToappId,(TDesC16*&)localCopy_Reply_To_AppId);

    // receive message date
    TInt64 dateTime;
    if (mServerMmsMtm->SendingDate() != TTime(0))
    {
        dateTime = S60CommonUtils::TTimeToJavaTime(
                       mServerMmsMtm->SendingDate());
    }
    else
    {
        dateTime = S60CommonUtils::TTimeToJavaTime(msvEntry.iDate);
    }
    TDesC *localCopySender = NULL;
    // only for emulator in local mode to pass the test on the emulator
#if(defined (__WINS__) || defined(__WINSCW__))
    {
        const CMsvRecipientList& recipientList =mServerMmsMtm->AddresseeList();
        TInt recipientCount = recipientList.Count();
        for (TInt j=0; j < recipientCount; j++)
        {
            const TDesC& address = recipientList[j];
            TMsvRecipientType addressType = recipientList.Type(j);
            if (EMsvRecipientTo == addressType)
            {
                TInt len = S60CommonUtils::CopyWiderL(address,(TDesC16*&)
                                                      localCopySender);
                break;
            }
        }
    }
#else
    {
        const TPtrC sender = mServerMmsMtm->Sender();
        S60CommonUtils::CopyWiderL(sender, (TDesC16*&) localCopySender);
    }
#endif

    TInt size = mServerMmsMtm->MessageSize();
    TDesC8* applicationId8 = NULL;
    CMMSMessageInformation* receiveMsgInformation =
        CMMSMessageInformation::NewL(*localCopySender, dateTime,
                                     *applicationId8, aMsvId);
    CleanupStack::PushL(receiveMsgInformation);

    const CMsvRecipientList& recipientList = mServerMmsMtm->AddresseeList();
    TInt recipientCount = recipientList.Count();
    TDesC16* messageAddress;
    for (TInt j = 0; j < recipientCount; j++)
    {
        messageAddress = NULL;
        const TDesC& address = recipientList[j];
        TMsvRecipientType addressType = recipientList.Type(j);
        S60CommonUtils::CopyWiderL(address, messageAddress);
        receiveMsgInformation->AddAddressL((TMsvRecipientTypeValues)addressType,
                                           messageAddress);
    }

    receiveMsgInformation->SetApplicationIdL(*localCopy_AppId);
    receiveMsgInformation->SetReplyToApplicationIdL(*localCopy_Reply_To_AppId);
    receiveMsgInformation->SetMessageSize(size);
    TPtrC subject = mServerMmsMtm->SubjectL();
    receiveMsgInformation->SetMessageSubjectL(subject);
    TMmsMessagePriority priority =
        (TMmsMessagePriority) mServerMmsMtm->MessagePriority();
    receiveMsgInformation->SetMessagePriority(priority);
    ReadAttachmentsL(*receiveMsgInformation);

    mReceivedMsgArray->AppendL(receiveMsgInformation);
    CleanupStack::Pop(receiveMsgInformation);

    delete csvEntry;
    csvEntry = NULL;
    delete localCopy_Reply_To_AppId;
    localCopy_Reply_To_AppId = NULL;
    delete localCopy_AppId;
    localCopy_AppId = NULL;
    delete localCopySender;
    localCopySender = NULL;

    HBufC* buf = HBufC::NewLC(KMaxAppIDLength);
    TPtr ptr = buf->Des();
    ptr.SetLength(0); // make sure that there is no garbage
    ptr.AppendNum(aMsvId);
    NotifyJava(*buf, 1);
    CleanupStack::PopAndDestroy(buf); //buf
    TRAP_IGNORE(DeleteMmsMessageL(aMsvId));
}

/*------------------------------------------------------------------------------
 * Notifies the message listener about the incoming message
 * .
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::NotifyJava(const TDesC& aDescription,
        int aMessages)
{
    JELOG2(EWMA);
    TLex lex(aDescription);
    TMsvId ID;
    lex.Val(ID);
    TRAP_IGNORE(mReceivedMMSIDs.AppendL(ID));
    mNewMessages += aMessages;
    mMessageListener->msgArrived();
}
/*-------------------------------------------------------------------------------
 * void ReadAttachmentsL(CMMSMessageInformation& aMidMessageInformation)
 *
 *-------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::ReadAttachmentsL(
    CMMSMessageInformation& aMidMessageInformation)
{
    JELOG2(EWMA);
    // Find root attachment
    TMsvAttachmentId rootId = mServerMmsMtm->MessageRootAttachment();
    CMsvStore* store = mServerMmsMtm->Entry().ReadStoreL();
    CleanupStack::PushL(store);
    MMsvAttachmentManager& attaMan = store->AttachmentManagerL();
    CMsvAttachment* msvAttachment = NULL;
    TInt attaCount = attaMan.AttachmentCount();

    for (TInt i = 0; i < attaCount; i++)
    {
        msvAttachment = attaMan.GetAttachmentInfoL(i);
        CleanupStack::PushL(msvAttachment);
        // open other message part
        // get mime header
        CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewLC();
        mimeHeaders->RestoreL(*msvAttachment);

        TMsvAttachmentId attachmentId = msvAttachment->Id();
        RFile attaFile = attaMan.GetAttachmentFileL(attachmentId);
        TDesC8* dataArray8 = NULL;
        ReadDataFromAttachFileL(attaFile, dataArray8);
        CleanupStack::PushL(dataArray8);
        attaFile.Close();
        // mimetype and encoding from mime header
        const TDesC8& attachmentType8 = mimeHeaders->ContentType();
        const TDesC8& attachmentSubType8 = mimeHeaders->ContentSubType();
        HBufC8* attachmentTypeBuf8 = HBufC8::NewLC(attachmentType8.Length() +
                                     attachmentSubType8.Length()
                                     + KSlashLength +
                                     KSlashLength);
        TPtr8 ptr = attachmentTypeBuf8->Des();
        ptr.SetLength(0);
        ptr.Append(attachmentType8);
        ptr.Append(KSlash);
        ptr.Append(attachmentSubType8);
        // Get the values of content-id, content-location
        const TDesC8& contentId8 = mimeHeaders->ContentId();
        const TDesC& location = mimeHeaders->ContentLocation();
        TDesC8* location8 = NULL;
        // Check for the content location if we have the same string that
        // we use for sending remove the content location.
        if ((&location) && location.Find(KTempMmsAttachmentName)== KErrNotFound)
        {
            S60CommonUtils::ConvertWiderToNarrowL(location, location8);
        }
        TInt size = msvAttachment->Size();

        CMMSMessageAttachment* midAttachment =
            CMMSMessageAttachment::NewL(mRFs, *dataArray8,
                                        *attachmentTypeBuf8, contentId8, *location8,
                                        false);

        CleanupStack::PushL(midAttachment);

        TUint charEncoding = mimeHeaders->MimeCharset();
        midAttachment->SetEncoding(charEncoding);

        if (attachmentId == rootId)
        {
            midAttachment->SetStarterContentId(true);
        }
        aMidMessageInformation.InsertAttachmentL(midAttachment);
        CleanupStack::Pop(midAttachment); // midAttachment

        delete location8;
        location8 = NULL; // location8
        // attachmentTypeBuf8
        CleanupStack::PopAndDestroy(attachmentTypeBuf8);
        CleanupStack::Pop(dataArray8); // dataArray8
        // mimeHeaders, attaInfo
        CleanupStack::PopAndDestroy(mimeHeaders);
        // mimeHeaders, attaInfo
        CleanupStack::PopAndDestroy(msvAttachment);
    }
    CleanupStack::PopAndDestroy(store); // store
}
/*-------------------------------------------------------------------------------
 *
 *-------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::ReadDataFromAttachFileL(RFile& aRFile,
        TDesC8*& aDataArray)
{
    JELOG2(EWMA);
    TInt size = 0;
    TInt error = KErrNotFound;
    error = aRFile.Size(size);
    if (error == KErrNone)
    {
        HBufC8* buffer = HBufC8::NewL(size);
        if (buffer)
        {
            TPtr8 ptr = buffer->Des();
            error = aRFile.Read(ptr, size); //read the data in the file
        }
        aDataArray = buffer;
    }
}

/*------------------------------------------------------------------------------
 * public ReceivedMessage(TMsvId aMessageId)
 *
 *------------------------------------------------------------------------------
 */
CMMSMessageInformation* CMMSPlatformServiceS60Impl::ReceivedMessage(
    TMsvId aMessageId)
{
    JELOG2(EWMA);
    CMMSMessageInformation* msgInfo = NULL;
    TInt count = mReceivedMsgArray->Count();
    for (TInt i = 0; i < count; i++)
    {
        CMMSMessageInformation* mi =(CMMSMessageInformation*)
                                    mReceivedMsgArray->At(i);
        if (aMessageId == mi->MessageMsvId()) //check for the message Id
        {
            msgInfo = mi; // that is the message we are looking for.
            mReceivedMsgArray->Delete(i);
            break;
        }
    }
    return msgInfo;
}

/*------------------------------------------------------------------------------
 * public destructor
 *
 *------------------------------------------------------------------------------
 */
CMMSPlatformServiceS60Impl::~CMMSPlatformServiceS60Impl()
{
    JELOG2(EWMA);
    detachFromVm();
    stopServer();
}
void CMMSPlatformServiceS60Impl::closeConnection(JNIEnv& aJni, jobject aPeer)
{
    JELOG2(EWMA);
    // If sending of a message is being carried out unblock the java side with
    // KErrCancel.
    if (mIsSending)
    {
        NotifySend(aJni, aPeer, KErrCancel);
    }
    // Close() Needs to be called in function server thread
    TRAP_IGNORE(CallMethodL(this, &CMMSPlatformServiceS60Impl::Close, this));
}
/*------------------------------------------------------------------------------
 * private close
 *
 *------------------------------------------------------------------------------
 */
void CMMSPlatformServiceS60Impl::Close()
{
    JELOG2(EWMA);
    if (mServerConnection)
    {
        mReceivedMMSIDs.Close();
        if (mRegisterCMsvEntry)
        {
            mRegisterCMsvEntry->RemoveObserver(*this);
            delete mRegisterCMsvEntry;
            mRegisterCMsvEntry = NULL;
        }
        if (mServerMmsMtm)
        {
            delete mServerMmsMtm;
            mServerMmsMtm = NULL;
        }
        if (mReceivedMsgArray)
        {
            mReceivedMsgArray->ResetAndDestroy();
            delete mReceivedMsgArray;
            mReceivedMsgArray = NULL;
        }
        // Unregistering from MMS Engine.
        LOG(EWMA, EInfo , "CMMSPlatformServiceS60Impl::Close() Unregistering"
            "from MMS Engine");
        mMmsApplicationAdapter->UnregisterL(*mServerApplicationId);
        if (mServerApplicationId)
        {
            delete mServerApplicationId;
            mServerApplicationId = NULL;
        }
    }
    if (mMmsClient)
    {
        delete mMmsClient;
        mMmsClient = NULL;
    }

    if (mClientMtmRegistry)
    {
        delete mClientMtmRegistry;
        mClientMtmRegistry = NULL;
    }

    if (mSession)
    {
        delete mSession;
        mSession = NULL;
    }

    if (mMmsApplicationAdapter)
    {
        delete mMmsApplicationAdapter;
        mMmsApplicationAdapter = NULL;
    }
    mSendTMsvIdArray.Close();

    if (mSendMsgArray)
    {
        mSendMsgArray->ResetAndDestroy();
        delete mSendMsgArray;
        mSendMsgArray = NULL;
    }
    if (mMessageHandler)
    {
        delete mMessageHandler;
        mMessageHandler = NULL;
    }
}
// End of File
} //namespace wma
} //namespace java

