/*
 * Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description:  CS Session class
 *
 */

// INCLUDE FILES
#include <ccsconversationentry.h>
#include <ccsclientconversation.h>

#include "ccsconversationevent.h"
#include "ccsdebug.h"
#include "ccsserver.h"
#include "ccssession.h"
#include "ccsplugininterface.h"
#include "ccsconversationcache.h"
#include "ccscontactsresolver.h"
#include "ccsconversationdeletehandler.h"
#include "ccsconversationmarkreadhandler.h"

// CONSTANTS
const TInt KTinyBuffer = 256; // 256 bytes
const TInt KBigBuffer = 2048; // 2K

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

// ----------------------------------------------------------------------------
// CCsSession::NewL
// Two Phase Construction
// ----------------------------------------------------------------------------
CCsSession* CCsSession::NewL(CCsServer* aServer)
{
    PRINT ( _L("Enter CCsSession::NewL") );

    CCsSession* self = new (ELeave) CCsSession(aServer);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self); // self

    PRINT ( _L("End CCsSession::NewL") );

    return self;
}

// ----------------------------------------------------------------------------
// CCsSession::CCsSession
// Construtor
// ----------------------------------------------------------------------------
CCsSession::CCsSession(CCsServer* aServer) :
    iServer(aServer)
{
}

// ----------------------------------------------------------------------------
// CCsSession::ConstructL
// Second phase constructor
// ----------------------------------------------------------------------------
void CCsSession::ConstructL()
{
    PRINT ( _L("Enter CCsSession::ConstructL") );

    iBufferOverflow = EFalse;
    iGetConversationBufferOverflow = EFalse;
    des = NULL;

    iNotifyHandling = EFalse;
    iConversationListChangeObserver = EFalse;
    iConversationChangeObserver = EFalse;
    iCachingChangeObserver = EFalse;
    iMonitoredConversation = NULL;

    // initialize the event List
    iEventList = new (ELeave) RPointerArray<CCsConversationEvent> ();

    iReqCnt = 1; //Let's start the event ID from 1

    PRINT ( _L("End CCsSession::ConstructL") );
}

// ----------------------------------------------------------------------------
// CCsSession::CCsSession
// Destructor
// ----------------------------------------------------------------------------
CCsSession::~CCsSession()
{
    PRINT ( _L("Enter CCsSession::~CCsSession") );

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

    if (iEventList)
    {
        iEventList->ResetAndDestroy();
        iEventList->Close();
        delete iEventList;
        iEventList = NULL;
    }

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

    PRINT ( _L("End CCsSession::~CCsSession") );
}

// ----------------------------------------------------------------------------
// CCsSession::ServiceL
//
// ----------------------------------------------------------------------------
void CCsSession::ServiceL(const RMessage2& aMessage)
{
    TInt errStatus = KErrNone;

    // Do the service
    TRAP ( errStatus, DoServiceL(aMessage) );

    // Check the error status returned
    if (errStatus != KErrNone)
    {
        aMessage.Complete(errStatus);
    }
}

// ----------------------------------------------------------------------------
// CCsSession::DoServiceL
//
// ----------------------------------------------------------------------------
void CCsSession::DoServiceL(const RMessage2& aMessage)
{
    switch (aMessage.Function())
    {
        case EGetConversationList:
            PRINT ( _L("Received function EGetConversationList") )
            GetConversationListL(aMessage);
            break;
            
        case EGetConversationUnreadList:
            PRINT ( _L("Received function EGetConversationUnreadList") )
            GetConversationUnreadListL(aMessage);
            break;

        case EGetConversations:
            PRINT ( _L("Received function EGetConversations") )
            GetConversationsL(aMessage);
            break;
            
        case EGetTotalUnreadCount:
            PRINT ( _L("Received function EGetTotalUnreadCount") )
            GetTotalUnreadCountL(aMessage);
            break;

        case ERequestChangeEvent:
            PRINT ( _L("Received function ERequestChangeEvent") )
            RequestChangeEventL(aMessage);
            break;

        case ERemoveChangeEvent:
            PRINT ( _L("Received function ERemoveChangeEvent") )
            RemoveChangeEventL(aMessage);
            break;

        case ESetConversationListChangeObserver:
            PRINT ( _L("Received function ESetConversationListChangeObserver") )
            SetConversationListChangeObserverL(aMessage);
            break;

        case EResetConversationListChangeObserver:
            PRINT ( _L("Received function EResetConversationListChangeObserver") )
            ResetConversationListChangeObserverL(aMessage);
            break;

        case ESetConversationChangeObserver:
            PRINT ( _L("Received function ESetConversationChangeObserver") )
            SetConversationChangeObserverL(aMessage);
            break;

        case EResetConversationChangeObserver:
            PRINT ( _L("Received function EResetConversationChangeObserver") )
            ResetConversationChangeObserverL(aMessage);
            break;

        case ESetCachingStatusObserver:
            PRINT ( _L("Received function ESetCachingStatusObserver") )
            SetCachingStatusObserverL(aMessage);
            break;

        case EResetCachingStatusObserver:
            PRINT ( _L("Received function EResetCachingStatusObserver") )
            ResetCachingStatusObserverL(aMessage);
            break;

        case EGetCachingStatus:
            GetCachingStatusL(aMessage);
            break;

        case EShutdown:
            PRINT ( _L("Received function EShutdown") )
            ShutdownServerL(aMessage);
            break;

        case EUserDeleteConversation:
            PRINT ( _L("Received function EDeleteConversation") )
            DeleteConversationL(aMessage);
            break;

        case EGetConversationId:
            PRINT ( _L("Received function EGetConversationId") )
            GetConversationIdL(aMessage);
            break;
            
        case EGetConversationIdFromAddress:
            PRINT ( _L("Received function EGetConversationIdFromAddress") )
            GetConversationIdfromAddressL(aMessage);
            break;
			
		case EGetConversationFromMessageId:
            PRINT ( _L("Received function EGetConversationFromMessageId") )
            GetConversationFromMessageIdL(aMessage);
            break;

        case EUserMarkReadConversation:
            PRINT ( _L("Received function EUserMarkReadConversation") )
            MarkConversationReadL(aMessage);
            break;
    }

    return;
}

// ----------------------------------------------------------------------------
// CCsSession::ServiceError
//
// ----------------------------------------------------------------------------
void CCsSession::ServiceError(const RMessage2& aMessage, TInt aError)
{
    aMessage.Complete(aError);
}

// ----------------------------------------------------------------------------
// CCsSession::GetConversationEntryListL
// the function to handle the request of Listing
// of recent(latest) conversation entry and
// list of dispalyname for all stored conversation entry ID
// ----------------------------------------------------------------------------
void CCsSession::GetConversationListL(const RMessage2& aMessage)
{
    PRINT ( _L("Enter CCsSession::GetConversationListL") );
    PRINT_TIMESTAMP ("Enter CCsSession::GetConversationListL");

    if (iBufferOverflow == EFalse)
    {
        RPointerArray<CCsClientConversation>* ClientConversationList =
                new (ELeave) RPointerArray<CCsClientConversation> ();

        // get cache pointer
        CCsConversationCache* cache = iServer->ConversationCacheInterface();

        // Call cache function to get recent conversation entry list
        // with dispaly name for all stored conversation entry ID
        cache->GetConversationListL(ClientConversationList);
        CleanupStack::PushL(ClientConversationList);

        //write all list data into stream
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        TInt listCount = ClientConversationList->Count();

        if (listCount == 0)
        {
            iConversationListChangeObserver = ETrue;
        }

        // write the count first
        writeStream.WriteUint16L(listCount);

        // now go through the list and do externalize
        for (int iloop = 0; iloop < listCount; iloop++)
        {
            CCsClientConversation
                    * ClientConversation =
                            static_cast<CCsClientConversation*> ( (*ClientConversationList)[iloop]);
            //write list of ClientConversation
            ClientConversation->ExternalizeL(writeStream);
        }

        // Results are already packed in the stream
        writeStream.CommitL();

        // --------------------------------------------------------------
        // Create a heap descriptor from the buffer
        des = HBufC8::NewLC(buf->Size());
        CleanupStack::Pop(des);
        TPtr8 ptr(des->Des());
        buf->Read(0, ptr, buf->Size());

        // cleanup
        CleanupStack::PopAndDestroy(2, buf); // writestream, buf
        CleanupStack::Pop(ClientConversationList);

        // destroy objects inside list
        ClientConversationList->ResetAndDestroy();
        ClientConversationList->Close();
        delete ClientConversationList;
        ClientConversationList = NULL;
    }

    TInt rcevdBufferSize = aMessage.GetDesMaxLength(1);
    TInt reqdBufferSize = des->Size();

    PRINT1 ( _L("Received buffer size = %d"), rcevdBufferSize );
    PRINT1 ( _L("Required buffer size = %d"), reqdBufferSize );

    // If the received buffer size from Client API is less than
    // the required buffer size write the required buffer size
    // and return.
    if (rcevdBufferSize < reqdBufferSize)
    {
        PRINT ( _L("In-adequate buffer received") );
        PRINT ( _L("Packing the required buffer size in response") );

        TPckgC<TInt> bufferSizePackage(reqdBufferSize);
        aMessage.WriteL(1, bufferSizePackage);
        aMessage.Complete(EGetConversationListBufferOverflow);
        iBufferOverflow = ETrue;
    }
    else
    {
        PRINT ( _L("Adequate buffer received") );
        PRINT ( _L("Packing the results in response") )

        aMessage.Write(1, *des);
        aMessage.Complete(EGetConversationListOperationComplete);
        iBufferOverflow = EFalse;
        delete des;
        des = NULL;
    }

    PRINT_TIMESTAMP ("End CCsSession::GetConversationListL");
    PRINT ( _L("End CCsSession::GetConversationListL") );
}

// ----------------------------------------------------------------------------
// CCsSession::GetConversationUnreadListL
// the function to handle the request of Listing
// of recent(latest) unread conversation entry and
// list of dispalyname for all stored conversation entry ID
// ----------------------------------------------------------------------------
void CCsSession::GetConversationUnreadListL(const RMessage2& aMessage)
    {
    PRINT ( _L("Enter CCsSession::GetConversationUnreadListL") );
    PRINT_TIMESTAMP ("Enter CCsSession::GetConversationUnreadListL");

    if (iBufferOverflow == EFalse)
        {
        RPointerArray<CCsClientConversation>* ClientConversationList =
                new (ELeave) RPointerArray<CCsClientConversation> ();

        // get cache pointer
        CCsConversationCache* cache = iServer->ConversationCacheInterface();

        // Call cache function to get recent conversation entry list
        // with dispaly name for all stored conversation entry ID
        cache->GetConversationUnreadListL(ClientConversationList);

        CleanupStack::PushL(ClientConversationList);

        //write all list data into stream
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        TInt listCount  = ClientConversationList->Count();

        if (listCount == 0)
            {
            iConversationListChangeObserver = ETrue;
            }

        // write the count first
        writeStream.WriteUint16L(listCount);

        // now go through the list and do externalize
        for (int iloop=0 ; iloop < listCount ; iloop++)
            {
            CCsClientConversation* ClientConversation =
            static_cast<CCsClientConversation*>((*ClientConversationList)[iloop]);
            //write list of ClientConversation
            ClientConversation->ExternalizeL(writeStream);
            }

        // Results are already packed in the stream
        writeStream.CommitL();

        // --------------------------------------------------------------
        // Create a heap descriptor from the buffer
        des = HBufC8::NewLC(buf->Size());
        CleanupStack::Pop(des);
        TPtr8 ptr(des->Des());
        buf->Read(0, ptr, buf->Size());

        // cleanup
        CleanupStack::PopAndDestroy(2, buf); // writestream, buf
        CleanupStack::Pop(ClientConversationList);

        // destroy objects inside list
        ClientConversationList->ResetAndDestroy();
        ClientConversationList->Close();
        delete ClientConversationList;
        ClientConversationList = NULL;
        }

    TInt rcevdBufferSize = aMessage.GetDesMaxLength(1);
    TInt reqdBufferSize  = des->Size();

    PRINT1 ( _L("Received buffer size = %d"), rcevdBufferSize );
    PRINT1 ( _L("Required buffer size = %d"), reqdBufferSize );

    if ( rcevdBufferSize < reqdBufferSize )
        {
        PRINT ( _L("In-adequate buffer received") );
        PRINT ( _L("Packing the required buffer size in response") );

        TPckgC<TInt> overflowPackage(ETrue);
        aMessage.WriteL(0, overflowPackage);
        TPckgC<TInt> bufferSizePackage(reqdBufferSize);
        aMessage.WriteL(1, bufferSizePackage);
        aMessage.Complete(KErrNone);
        iBufferOverflow = ETrue;
        }
    else
        {
        PRINT ( _L("Adequate buffer received") );
        PRINT ( _L("Packing the results in response") )

        TPckgC<TInt> overflowPackage(EFalse);
        aMessage.WriteL(0, overflowPackage);
        aMessage.Write(1, *des);
        aMessage.Complete(KErrNone);
        iBufferOverflow = EFalse;
        delete des;
        des = NULL;
        }

    PRINT_TIMESTAMP ("End CCsSession::GetConversationUnreadListL");
    PRINT ( _L("End CCsSession::GetConversationUnreadListL") );
    }

// ----------------------------------------------------------------------------
// CCsSession::GetConversationsL
// the function to handle the request
// of conversation entry list for one conversation entry ID
// ----------------------------------------------------------------------------
void CCsSession::GetConversationsL(const RMessage2& aMessage)
{
    PRINT ( _L("Enter CCsSession::GetConversationsL") );

    if (iGetConversationBufferOverflow == EFalse)
    {
        // Read Contact from the message
        HBufC8* buffer = HBufC8::NewLC(KBigBuffer);

        TPtr8 bufferPtr(buffer->Des());
        aMessage.ReadL(0, bufferPtr);

        // Stream over the buffer
        RDesReadStream stream(bufferPtr);
        stream.PushL();

        // get cache pointer
        CCsConversationCache* cache = iServer->ConversationCacheInterface();

        // read the Client Conversation consist of Entry Id
        CCsClientConversation* ClientConversation =
                CCsClientConversation::NewL();
        CleanupStack::PushL(ClientConversation);
        ClientConversation->InternalizeL(stream);
        CleanupStack::Pop(ClientConversation);
        CleanupStack::PopAndDestroy(2, buffer);//stream, buffer

        CleanupStack::PushL(ClientConversation);
        RPointerArray<CCsConversationEntry>* conversationEntryList =
                new (ELeave) RPointerArray<CCsConversationEntry> ();
        CleanupStack::PushL(conversationEntryList);

        // get conversationlist for given ClientConversation 
        cache->GetConversationsL(ClientConversation, conversationEntryList);

        // create a new buffer for writing into stream
        // write all list data into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        TInt ItemCount = conversationEntryList->Count();
        //write  recent conversation entry list
        writeStream.WriteInt32L(ItemCount);

        // Write the conversation entry
        for (TInt iloop = 0; iloop < ItemCount; iloop++)
        {
            CCsConversationEntry
                    * entry =
                            static_cast<CCsConversationEntry*> ( (*conversationEntryList)[iloop]);
            entry->ExternalizeL(writeStream);
        }

        // Results are already packed in the stream
        writeStream.CommitL();

        // --------------------------------------------------------------
        // Create a heap descriptor from the buffer
        des = HBufC8::NewLC(buf->Size());
        CleanupStack::Pop(des);
        TPtr8 ptr(des->Des());
        buf->Read(0, ptr, buf->Size());

        CleanupStack::PopAndDestroy(2, buf); // writestream, buf
        CleanupStack::Pop(conversationEntryList);

        // Cleanup
        conversationEntryList->ResetAndDestroy();
        conversationEntryList->Close();
        delete conversationEntryList;
        conversationEntryList = NULL;
        CleanupStack::PopAndDestroy(ClientConversation);
    }

    TInt rcevdBufferSize = aMessage.GetDesMaxLength(1);
    TInt reqdBufferSize = des->Size();

    PRINT1 ( _L("Received buffer size = %d"), rcevdBufferSize );
    PRINT1 ( _L("Required buffer size = %d"), reqdBufferSize );

    // If the received buffer size from Client API is less than
    // the required buffer size write the required buffer size
    if (rcevdBufferSize < reqdBufferSize)
    {
        PRINT ( _L("In-adequate buffer received") );
        PRINT ( _L("Packing the required buffer size in response") );

        TPckgC<TInt> bufferSizePackage(reqdBufferSize);
        aMessage.WriteL(1, bufferSizePackage);
        aMessage.Complete(EGetConversationBufferOverflow);
        iGetConversationBufferOverflow = ETrue;
    }
    else
    {
        PRINT ( _L("Adequate buffer received") );
        PRINT ( _L("Packing the results in response") )

        aMessage.Write(1, *des);
        aMessage.Complete(EGetConversationOperationComplete);
        iGetConversationBufferOverflow = EFalse;
        delete des;
        des = NULL;
    }

    PRINT ( _L("End CCsSession::GetConversationsL") );
}

// ----------------------------------------------------------------------------
// CCsSession::ShutdownServerL
// Stops the scheduler
// ----------------------------------------------------------------------------
void CCsSession::ShutdownServerL(const RMessage2& aMessage)
{
    aMessage.Complete(KErrNone);
    CActiveScheduler::Stop();

    PRINT ( _L("CCsSession::ShutdownServerL - Server ShutDown") );
}

// ----------------------------------------------------------------------------
// CCsSession::HandleNewConversationListEventL
// Notify client about new conversation event
// ----------------------------------------------------------------------------
void CCsSession::HandleNewConversationListEventL(
                                                 CCsClientConversation* aClientConversation)
{
    PRINT ( _L("Enter CCsSession::HandleNewConversationListEventL") );

    if (!iConversationListChangeObserver)
        return;

    if (! (iNotifyHandling))
    {
        //append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetClientConversationL(*aClientConversation);
        conversationEvent->SetEvent(KConversationListEventNew);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        //externalize ClientConversation
        aClientConversation->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();

        // --------------------------------------------------------------
        // Create a heap descriptor from the buffer
        HBufC8* notifyDes = HBufC8::NewLC(buf->Size());
        TPtr8 ptr(notifyDes->Des());
        buf->Read(0, ptr, buf->Size());

        iAsyncReqRMessage.Write(1, *notifyDes);
        iAsyncReqRMessage.Complete(EAddConversationListEvent);
        CleanupStack::PopAndDestroy(3, buf); // notifyDes, writestream, buf 
        iNotifyHandling = EFalse;
    }

    PRINT ( _L("End CCsSession::HandleNewConversationListEventL") );
}

// ----------------------------------------------------------------------------
// CCsSession::HandleDeleteConversationListEventL
// Notify client about delete conversation event
// ----------------------------------------------------------------------------
void CCsSession::HandleDeleteConversationListEventL(
                                                    CCsClientConversation* aClientConversation)
{
    PRINT ( _L("Enter CCsSession::HandleDeleteConversationListEventL") );

    if (!iConversationListChangeObserver)
        return;

    if (! (iNotifyHandling))
    {
        //append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        conversationEvent->SetClientConversationL(*aClientConversation);
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetEvent(KConversationListEventDelete);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        //externalize ClientConversation
        aClientConversation->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();

        // --------------------------------------------------------------
        // Create a heap descriptor from the buffer
        HBufC8* notifyDes = HBufC8::NewLC(buf->Size());
        TPtr8 ptr(notifyDes->Des());
        buf->Read(0, ptr, buf->Size());

        iAsyncReqRMessage.Write(1, *notifyDes);
        iAsyncReqRMessage.Complete(EDeleteConversationListEvent);
        CleanupStack::PopAndDestroy(3, buf); // notifyDes, writestream, buf
        iNotifyHandling = EFalse;
    }

    PRINT ( _L("End CCsSession::HandleDeleteConversationListEventL") );
}

// ----------------------------------------------------------------------------
// CCsSession::HandleModifyConversationListEventL
// Notify client about update conversation event
// ----------------------------------------------------------------------------
void CCsSession::HandleModifyConversationListEventL(
                                                    CCsClientConversation* aClientConversation)
{
    PRINT ( _L("Enter CCsSession::HandleModifyConversationListEventL") );

    if (!iConversationListChangeObserver)
        return;

    if (! (iNotifyHandling))
    {
        //append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        conversationEvent->SetClientConversationL(*aClientConversation);
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetEvent(KConversationListEventUpdate);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        //externalize ClientConversation
        aClientConversation->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();
        // --------------------------------------------------------------

        // Create a heap descriptor from the buffer
        HBufC8* notifyDes = HBufC8::NewLC(buf->Size());
        TPtr8 ptr(notifyDes->Des());
        buf->Read(0, ptr, buf->Size());

        iAsyncReqRMessage.Write(1, *notifyDes);
        iAsyncReqRMessage.Complete(EModifyConversationListEvent);
        CleanupStack::PopAndDestroy(3, buf); // notifyDes, writestream, buf
        iNotifyHandling = EFalse;
    }

    PRINT ( _L("End CCsSession::HandleModifyConversationListEventL") );
}

// ----------------------------------------------------------------------------
// CCsSession::RequestChangeEventL
// the function to register for cache change event notification
// ----------------------------------------------------------------------------
void CCsSession::RequestChangeEventL(const RMessage2& aMessage)
{
    iAsyncReqRMessage = aMessage;
    iNotifyHandling = ETrue;

    if (iEventList->Count() > 0)
    {
        CCsConversationEvent* conversationEvent = (*iEventList)[0];

        // check if the reqCnt matches with the latest arrived count
        // then its not a duplicate, delete the top event
        if (iReqCnt == aMessage.Int2())
        {
            iEventList->Remove(0);
            delete conversationEvent;

            if (iEventList->Count() > 0)
            {
                // increment the count
                iReqCnt++;
                conversationEvent = (*iEventList)[0];
            }
            else
            {
                // no more pending events, simply return
                return;
            }
        }

        HBufC8* buffer = HBufC8::NewLC(4);
        TPtr8 cntPtr(buffer->Des());
        _LIT8(KFormat,"%d");
        cntPtr.Format(KFormat, iReqCnt);
        iAsyncReqRMessage.Write(0, *buffer);

        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        // Externalize ClientConversation
        if (conversationEvent->ClientConversation())
            conversationEvent->ClientConversation()->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();

        // --------------------------------------------------------------
        // Create a heap descriptor from the buffer
        HBufC8* notifyDes = HBufC8::NewLC(buf->Size());
        TPtr8 ptr(notifyDes->Des());
        buf->Read(0, ptr, buf->Size());

        iAsyncReqRMessage.Write(1, *notifyDes);

        NotifyClient(conversationEvent);

        CleanupStack::PopAndDestroy(4, buffer);
        // notifyDes, writestream, buf, buffer
        iNotifyHandling = EFalse;
    }
}

// ----------------------------------------------------------------------------
// CCsSession::RemoveChangeEventL
// Deregister the cache change event notification
// ----------------------------------------------------------------------------
void CCsSession::RemoveChangeEventL(const RMessage2& aMessage)
{
    if (! (iNotifyHandling))
    {
        // complete message with aMessage
        aMessage.Complete(KErrNone);
    }
    else
    {
        iAsyncReqRMessage.Complete(KErrCancel);
        iNotifyHandling = EFalse;
        // complete message with aMessage
        aMessage.Complete(KErrNone);
    }
}

// ----------------------------------------------------------------------------
// CCsSession::GetCachingStatusL
// the function to request conversation server
// to get caching status.
// ----------------------------------------------------------------------------
void CCsSession::GetCachingStatusL(const RMessage2& aMessage)
{
    PRINT ( _L("Enter CCsSession::GetCachingStatusL") );

    // create a new buffer for writing into stream
    CBufFlat* buf = CBufFlat::NewL(KTinyBuffer);

    CleanupStack::PushL(buf);
    RBufWriteStream writeStream(*buf);
    writeStream.PushL();

    // Externalize caching status
    writeStream.WriteUint8L(iServer->GetCachingStatus());

    // Results are already packed in the stream
    writeStream.CommitL();
    // --------------------------------------------------------------

    // Create a heap descriptor from the buffer
    HBufC8* des = HBufC8::NewLC(buf->Size());
    CleanupStack::Pop(des);
    TPtr8 ptr(des->Des());
    buf->Read(0, ptr, buf->Size());

    CleanupStack::PopAndDestroy(2, buf); // writestream, buf

    aMessage.Write(1, *des);
    aMessage.Complete(KErrNone);
    delete des;

    PRINT ( _L("End CCsSession::GetCachingStatusL") );
}

// ----------------------------------------------------------------------------
// CCsSession::GetTotalUnreadCountL
// Gets total unread conversation entries.
// ----------------------------------------------------------------------------
void CCsSession::GetTotalUnreadCountL(const RMessage2& aMessage)
    {
    PRINT ( _L("Enter CCsSession::GetTotalUnreadCountL") );

    // create a new buffer for writing into stream
    CBufFlat* buf = CBufFlat::NewL(KTinyBuffer);

    CleanupStack::PushL(buf);
    RBufWriteStream writeStream(*buf);
    writeStream.PushL();

    CCsConversationCache* cache = iServer->ConversationCacheInterface();

    // Externalize caching status
    writeStream.WriteUint32L(cache->GetTotalUnreadCount());

    // Results are already packed in the stream
    writeStream.CommitL();
    // --------------------------------------------------------------

    // Create a heap descriptor from the buffer
    HBufC8* des = HBufC8::NewLC(buf->Size());
    CleanupStack::Pop(des);
    TPtr8 ptr(des->Des());
    buf->Read(0, ptr, buf->Size());

    CleanupStack::PopAndDestroy(2, buf); // writestream, buf

    aMessage.Write(1, *des);
    aMessage.Complete(KErrNone);
    delete des;

    PRINT ( _L("End CCsSession::GetTotalUnreadCountL") );
    }

// ----------------------------------------------------------------------------
// CCsSession::SetConversationListChangeObserverL
// the function to request conversation server
// to set ConversationListChangeObserver.
// ----------------------------------------------------------------------------
void CCsSession::SetConversationListChangeObserverL(const RMessage2& aMessage)
{
    iConversationListChangeObserver = ETrue;
    aMessage.Complete(KErrNone);
}

// ----------------------------------------------------------------------------
// CCsSession::ResetConversationListChangeObserverL
// the function to request conversation server
// to reset ConversationListChangeObserver.
// ----------------------------------------------------------------------------
void CCsSession::ResetConversationListChangeObserverL(const RMessage2& aMessage)
{
    iConversationListChangeObserver = EFalse;
    aMessage.Complete(KErrNone);
}

// ----------------------------------------------------------------------------
// CCsSession::SetConversationChangeObserverL
// the function to request conversation server
// to set ConversationChangeObserver.
// ----------------------------------------------------------------------------
void CCsSession::SetConversationChangeObserverL(const RMessage2& aMessage)
{
    // Read Contact from the message
    HBufC8* buffer = HBufC8::NewLC(KBigBuffer);

    TPtr8 bufferPtr(buffer->Des());
    aMessage.ReadL(0, bufferPtr);

    // Stream over the buffer
    RDesReadStream stream(bufferPtr);
    stream.PushL();

    // Get the client conversation
    CCsClientConversation* clientConversation = CCsClientConversation::NewL();
    CleanupStack::PushL(clientConversation);
    clientConversation->InternalizeL(stream);
    CleanupStack::Pop(clientConversation);

    CleanupStack::PopAndDestroy(2, buffer);//stream, buffer

    iMonitoredConversation = clientConversation;
    iConversationChangeObserver = ETrue;

    aMessage.Complete(KErrNone);
}

// ----------------------------------------------------------------------------
// CCsSession::ResetConversationChangeObserverL
// the function to request conversation server
// to reset ConversationChangeObserver.
// ----------------------------------------------------------------------------
void CCsSession::ResetConversationChangeObserverL(const RMessage2& aMessage)
{
    // Read Contact from the message
    HBufC8* buffer = HBufC8::NewLC(KBigBuffer);

    TPtr8 bufferPtr(buffer->Des());
    aMessage.ReadL(0, bufferPtr);

    // Stream over the buffer
    RDesReadStream stream(bufferPtr);
    stream.PushL();

    // Read the client conversation
    CCsClientConversation* clientConversation = CCsClientConversation::NewL();
    CleanupStack::PushL(clientConversation);
    clientConversation->InternalizeL(stream);
    CleanupStack::Pop(clientConversation);

    CleanupStack::PopAndDestroy(2, buffer); //stream, buffer

    if (iMonitoredConversation)
    {
        delete iMonitoredConversation;
        iMonitoredConversation = NULL;
        iConversationChangeObserver = EFalse;
    }

    delete clientConversation;

    aMessage.Complete(KErrNone);
}

// ----------------------------------------------------------------------------
// CCsSession::SetCachingStatusObserverL
// the function to request conversation server
// to set caching status observer flag.
// ----------------------------------------------------------------------------
void CCsSession::SetCachingStatusObserverL(const RMessage2& aMessage)
{
    iCachingChangeObserver = ETrue;
    aMessage.Complete(KErrNone);
}

// ----------------------------------------------------------------------------
// CCsSession::ResetCachingStatusObserverL
// the function to request conversation server
// to reset caching status observer flag.
// ----------------------------------------------------------------------------
void CCsSession::ResetCachingStatusObserverL(const RMessage2& aMessage)
{
    iCachingChangeObserver = EFalse;
    aMessage.Complete(KErrNone);
}

// ----------------------------------------------------------------------------
// CCsSession::HandleNewConversationEventL
// the function to handles the new conversation event received from cache
// asynchronously
// ----------------------------------------------------------------------------
void CCsSession::HandleNewConversationEventL(
                                             CCsClientConversation* aClientConversation)
{
    PRINT ( _L("Enter CCsSession::HandleNewConversationEventL") );

    if (!iConversationChangeObserver)
        return;

    if (aClientConversation->GetConversationEntryId()
            != iMonitoredConversation->GetConversationEntryId())
        return;

    if (! (iNotifyHandling))
    {
        //append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetClientConversationL(*aClientConversation);
        conversationEvent->SetEvent(KConversationEventNew);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        //externalize ClientConversation
        aClientConversation->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();
        // --------------------------------------------------------------

        // Create a heap descriptor from the buffer
        HBufC8* des = HBufC8::NewLC(buf->Size());
        CleanupStack::Pop(des);
        TPtr8 ptr(des->Des());
        buf->Read(0, ptr, buf->Size());

        CleanupStack::PopAndDestroy(2, buf); // writestream, buf

        iAsyncReqRMessage.Write(1, *des);
        iAsyncReqRMessage.Complete(EAddConversationEvent);
        delete des;
        iNotifyHandling = EFalse;
    }

    PRINT ( _L("End CCsSession::HandleNewConversationEventL") );
}

// ----------------------------------------------------------------------------
// CCsSession::HandleDeleteConversationEventL
// the function to handles the delete conversation event received from cache
// asynchronously
// ----------------------------------------------------------------------------
void CCsSession::HandleDeleteConversationEventL(
                                                CCsClientConversation* aClientConversation)
{
    PRINT ( _L("Enter CCsSession::HandleDeleteConversationEventL") );

    if (!iConversationChangeObserver)
        return;

    if (aClientConversation->GetConversationEntryId()
            != iMonitoredConversation->GetConversationEntryId())
        return;

    if (! (iNotifyHandling))
    {
        //append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetClientConversationL(*aClientConversation);
        conversationEvent->SetEvent(KConversationEventDelete);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        //externalize ClientConversation
        aClientConversation->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();
        // --------------------------------------------------------------

        // Create a heap descriptor from the buffer
        HBufC8* des = HBufC8::NewLC(buf->Size());
        CleanupStack::Pop(des);
        TPtr8 ptr(des->Des());
        buf->Read(0, ptr, buf->Size());

        CleanupStack::PopAndDestroy(2, buf); // writestream, buf

        iAsyncReqRMessage.Write(1, *des);
        iAsyncReqRMessage.Complete(EDeleteConversationEvent);
        delete des;
        iNotifyHandling = EFalse;
    }

    PRINT ( _L("End CCsSession::HandleDeleteConversationEventL") );
}

// ----------------------------------------------------------------------------
// CCsSession::HandleModifyConversationEventL
// the function to handles the modify conversation event received from cache
// asynchronously
// ----------------------------------------------------------------------------
void CCsSession::HandleModifyConversationEventL(
                                                CCsClientConversation* aClientConversation)
{
    PRINT ( _L("Enter CCsSession::HandleModifyConversationEventL") );

    if (!iConversationChangeObserver)
        return;

    if (aClientConversation->GetConversationEntryId()
            != iMonitoredConversation->GetConversationEntryId())
        return;

    if (! (iNotifyHandling))
    {
        //append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetClientConversationL(*aClientConversation);
        conversationEvent->SetEvent(KConversationEventUpdate);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        // create a new buffer for writing into stream
        CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
        CleanupStack::PushL(buf);

        RBufWriteStream writeStream(*buf);
        writeStream.PushL();

        //externalize ClientConversation
        aClientConversation->ExternalizeL(writeStream);

        // Results are already packed in the stream
        writeStream.CommitL();
        // --------------------------------------------------------------

        // Create a heap descriptor from the buffer
        HBufC8* des = HBufC8::NewLC(buf->Size());
        CleanupStack::Pop(des);
        TPtr8 ptr(des->Des());
        buf->Read(0, ptr, buf->Size());

        CleanupStack::PopAndDestroy(2, buf); // writestream, buf

        iAsyncReqRMessage.Write(1, *des);
        iAsyncReqRMessage.Complete(EModifyConversationEvent);
        delete des;
        iNotifyHandling = EFalse;
    }

    PRINT ( _L("End CCsSession::HandleModifyConversationEventL") );
}

// ----------------------------------------------------------------------------
// CCsSession::NotifyClient
// The function to notify client for cache change event
// ----------------------------------------------------------------------------
void CCsSession::NotifyClient(CCsConversationEvent* aConversationEvent)
{
    if (aConversationEvent->IsNewConversationListEventSet())
    {
        iAsyncReqRMessage.Complete(EAddConversationListEvent);
    }
    else if (aConversationEvent->IsDeleteConversationListEventSet())
    {
        iAsyncReqRMessage.Complete(EDeleteConversationListEvent);
    }
    else if (aConversationEvent->IsUpdateConversationListEventSet())
    {
        iAsyncReqRMessage.Complete(EModifyConversationListEvent);
    }
    else if (aConversationEvent->IsNewConversationEventSet())
    {
        iAsyncReqRMessage.Complete(EAddConversationEvent);
    }
    else if (aConversationEvent->IsDeleteConversationEventSet())
    {
        iAsyncReqRMessage.Complete(EDeleteConversationEvent);
    }
    else if (aConversationEvent->IsUpdateConversationEventSet())
    {
        iAsyncReqRMessage.Complete(EModifyConversationEvent);
    }
    else if (aConversationEvent->IsRefreshConversationListEventSet())
    {
        iAsyncReqRMessage.Complete(KConversationEventListRefresh);
    }
    else if (aConversationEvent->IsRefreshConversationEventSet())
    {
        iAsyncReqRMessage.Complete(KConversationEventRefresh);
    }
}

// ----------------------------------------------------------------------------
// CCsSession::HandleChangeEventL
// The function handles cache change events
// ----------------------------------------------------------------------------
void CCsSession::HandleChangeEventL(CCsClientConversation* aConversation,
                                    TUint32 aEvent)
{
    if (aEvent & KConversationListEventNew)
    {
        HandleNewConversationListEventL(aConversation);
    }
    else if (aEvent & KConversationListEventUpdate)
    {
        HandleModifyConversationListEventL(aConversation);
    }
    else if (aEvent & KConversationListEventDelete)
    {
        HandleDeleteConversationListEventL(aConversation);
    }
    else if (aEvent & KConversationEventNew)
    {
        HandleNewConversationEventL(aConversation);
    }
    else if (aEvent & KConversationEventUpdate)
    {
        HandleModifyConversationEventL(aConversation);
    }
    else if (aEvent & KConversationEventDelete)
    {
        HandleDeleteConversationEventL(aConversation);
    }
    else if (aEvent & KConversationEventListRefresh)
    {
        HandleRefreshConversationListL();
    }
    else if (aEvent & KConversationEventRefresh)
    {
        HandleRefreshConversationL();
    }
}

// ----------------------------------------------------------------------------
// CCsSession::DeleteConversationL
// ----------------------------------------------------------------------------
void CCsSession::DeleteConversationL(const RMessage2& aMessage)
{
    PRINT ( _L("Enter CCsSession::DeleteConversationL") );

    TInt conversationId = aMessage.Int0();

    // Delete handler
    CCsConversationCache* cache = iServer->ConversationCacheInterface();
    CCsConversationDeleteHandler* deleteHandler =
            CCsConversationDeleteHandler::NewL(cache);

    deleteHandler->DeleteL(conversationId);
    aMessage.Complete(EUserDeleteConversationComplete);

    PRINT ( _L("End CCsSession::DeleteConversationL") );
}

// ----------------------------------------------------------------------------
// CCsSession::HandleRefreshConversationListL
// ----------------------------------------------------------------------------
void CCsSession::HandleRefreshConversationListL()
{
    if (!iConversationListChangeObserver)
        return;

    if (! (iNotifyHandling))
    {
        // Append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetEvent(KConversationEventListRefresh);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        iAsyncReqRMessage.Complete(ERefreshConversationListEvent);
        iNotifyHandling = EFalse;
    }
}

// ----------------------------------------------------------------------------
// CCsSession::HandleRefreshConversationL
// ----------------------------------------------------------------------------
void CCsSession::HandleRefreshConversationL()
{
    if (!iConversationChangeObserver)
        return;

    if (! (iNotifyHandling))
    {
        // Append in notify list
        CCsConversationEvent* conversationEvent = CCsConversationEvent::NewL();
        CleanupStack::PushL(conversationEvent);
        conversationEvent->SetEvent(KConversationEventRefresh);
        iEventList->AppendL(conversationEvent);
        CleanupStack::Pop(conversationEvent);
    }
    else
    {
        iAsyncReqRMessage.Complete(ERefreshConversationEvent);
        iNotifyHandling = EFalse;
    }
}

// ----------------------------------------------------------------------------
// Get conversation id
// ----------------------------------------------------------------------------
void CCsSession::GetConversationIdL(const RMessage2& aMessage)
{
    // create a new buffer for writing into stream
    CBufFlat* buf = CBufFlat::NewL(KTinyBuffer);

    CleanupStack::PushL(buf);
    RBufWriteStream writeStream(*buf);
    writeStream.PushL();

    // Get the contact id
    TInt contactId = aMessage.Int0();
    CCsConversationCache* cache = iServer->ConversationCacheInterface();
    TInt conversationId = cache->GetConversationIdL(contactId);

    // Externalize link
    writeStream.WriteInt32L(conversationId);

    // Results are already packed in the stream
    writeStream.CommitL();

    // Create a heap descriptor from the buffer
    HBufC8* des = HBufC8::NewLC(buf->Size());
    CleanupStack::Pop(des);
    TPtr8 ptr(des->Des());
    buf->Read(0, ptr, buf->Size());

    CleanupStack::PopAndDestroy(2, buf); // writestream, buf

    aMessage.Write(1, *des);
    aMessage.Complete(EGetConversationIdComplete);
    delete des;
}

// ----------------------------------------------------------------------------
// GetConversationFromMessageIdL
// ----------------------------------------------------------------------------
void CCsSession::GetConversationFromMessageIdL(const RMessage2& aMessage)
    {
    // create a new buffer for writing into stream
    CBufFlat* buf = CBufFlat::NewL(KBigBuffer);
    CleanupStack::PushL(buf);

    RBufWriteStream writeStream(*buf);
    writeStream.PushL();

    // Get the message id
    TInt messageId = aMessage.Int0();
    CCsConversationCache* cache = iServer->ConversationCacheInterface();
    CCsClientConversation* conversation = cache->GetConversationFromMessageIdL(messageId);
    
    // if no conversation exists for given message-id, 
    // create a dummy conversation and complete response
    if(conversation == NULL)
    {
        //create dummy conversation
        conversation = CCsClientConversation::NewL();
        CleanupStack::PushL(conversation);
        conversation->SetConversationEntryId(-1);
        CCsConversationEntry* entry = CCsConversationEntry::NewL();
		CleanupStack::PushL(entry);
        entry->SetEntryId(-1);
        conversation->SetConversationEntryL(entry); // clone
		CleanupStack::PopAndDestroy(entry);
    }
    else
    {
        CleanupStack::PushL(conversation);
    }

    // Externalize 
    conversation->ExternalizeL(writeStream);
    
    // Results are already packed in the stream
    writeStream.CommitL();
    
    // Create a heap descriptor from the buffer
    HBufC8* des = HBufC8::NewLC(buf->Size());
    CleanupStack::Pop(des);
    TPtr8 ptr(des->Des());
    buf->Read(0, ptr, buf->Size());
    
    CleanupStack::PopAndDestroy(3, buf); // conversation, writestream, buf
    
    aMessage.Write(1, *des);
    aMessage.Complete(EGetConversationFromMessageIdComplete);
    delete des;
    }

// ----------------------------------------------------------------------------
// CCsSession::GetConversationIdfromAddressL
// ----------------------------------------------------------------------------
void CCsSession::GetConversationIdfromAddressL(const RMessage2& aMessage)
{
    TInt deslen = aMessage.GetDesLength(0);
    
    // Copy the data into a buffer
    RBuf buffer;
    buffer.CreateL(deslen);
    buffer.CleanupClosePushL();
    aMessage.ReadL(0,buffer,0);


    // Get the contact id
    CCsConversationCache* cache = iServer->ConversationCacheInterface();
    TInt conversationId = cache->GetConversationIdFromAddressL(buffer);

    // create a new buffer for writing into stream
    CBufFlat* buf = CBufFlat::NewL(KTinyBuffer);
    CleanupStack::PushL(buf);

    RBufWriteStream writeStream(*buf);
    writeStream.PushL();
    // Externalize link
    writeStream.WriteInt32L(conversationId);

    // Results are already packed in the stream
    writeStream.CommitL();

    // Create a heap descriptor from the buffer
    HBufC8* des = HBufC8::NewLC(buf->Size());
    CleanupStack::Pop(des);
    TPtr8 ptr(des->Des());
    buf->Read(0, ptr, buf->Size());
 
    CleanupStack::PopAndDestroy(2, buf); // writestream, buf
    CleanupStack::PopAndDestroy(&buffer);

    aMessage.Write(1, *des);
    aMessage.Complete(EGetConversationIdFromAddressComplete);
    delete des;
}

// ----------------------------------------------------------------------------
// CCsSession::MarkConversationReadL
// ----------------------------------------------------------------------------
void CCsSession::MarkConversationReadL(const RMessage2& aMessage)
{
    PRINT ( _L("Enter CCsSession::MarkConversationReadL") );

    TInt conversationId = aMessage.Int0();

    // Mark read handler
    CCsConversationCache* cache = iServer->ConversationCacheInterface();
    CCsConversationMarkReadHandler* markHandler = CCsConversationMarkReadHandler::NewL(cache);

    markHandler->MarkReadL(conversationId);
    
    aMessage.Complete(EUserMarkReadConversationComplete);

    PRINT ( _L("End CCsSession::MarkConversationReadL") );
}
//EOF
