/*
* 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: CCommsSession handles communication with client side
*
*/

#include <memory>

#include "logger.h"

#include "common.h"
#include "commssession.h"
#include "ipcserver.h"

namespace java
{
namespace comms
{
using java::util::ScopedLock;

CCommsSession::CCommsSession(IpcListener& aListener, int aPermissions)
        : mListener(aListener), mClientPermissions(aPermissions)
{
}

CCommsSession::~CCommsSession()
{
    while (!mMessageQueue.empty())
    {
        std::auto_ptr<HBufC8> tmp(mMessageQueue.front());
        mMessageQueue.pop();
    }
}

void CCommsSession::ServiceL(const RMessage2& aMessage)
{
    TInt function = aMessage.Function();
    switch (function)
    {
    case ESend:
        handleSendL(aMessage);
        break;

    case EReceive:
        handleReceiveL(aMessage);
        break;

    case ECancelReceive:
        handleCancelReceive(aMessage);
        break;

    default:
        aMessage.Complete(KErrNotSupported);
        break;
    }
}

int CCommsSession::send(ipcMessage_t* aMsg)
{
    ScopedLock lock(mMutex);

    // messages from server are considered to be always trusted
    aMsg->ipcHeader.permissions = -1;
    HBufC8* msg = messageToDes(*aMsg);
    mMessageQueue.push(msg);

    if (isReceivePending())
    {
        HBufC8* msg = mMessageQueue.front();

        TInt rc = doSend(mReceiveMessage, *msg);
        if (rc == KErrNone)
        {
            mMessageQueue.pop();
            delete msg;
        }
        else
        {
            WLOG2(EJavaComms, "%s failed, err = %d", __PRETTY_FUNCTION__, rc);
        }
    }
    return 0;
}

void CCommsSession::handleSendL(const RMessage2& aMessage)
{
    TInt len = aMessage.GetDesLengthL(0);
    int rc = KErrNone;
    if (len < sizeof(ipcHeader_t))
    {
        WLOG2(EJavaComms, "%s invalid message size, len = %d", __PRETTY_FUNCTION__, len);
        rc = KErrArgument;
    }
    else
    {
        RBuf8 buffer;

        buffer.CleanupClosePushL();
        buffer.CreateL(len);

        aMessage.ReadL(0,buffer);

        char* buf = desToMessage(buffer);
        ipcMessage_t* msg = reinterpret_cast<ipcMessage_t*>(buf);
        if (msg->ipcHeader.length == len)
        {
            msg->ipcHeader.sender = getId();
            msg->ipcHeader.permissions = mClientPermissions;
            mListener.processMessage(msg);
        }
        else
        {
            WLOG3(EJavaComms, "%s message size does not match to header, len = %d header = %d",
                  __PRETTY_FUNCTION__, len, msg->ipcHeader.length);
            rc = KErrArgument;
        }

        delete[] buf;
        CleanupStack::PopAndDestroy();
    }
    aMessage.Complete(rc);
}

void CCommsSession::handleReceiveL(const RMessage2& aMessage)
{
    ScopedLock lock(mMutex);

    if (mMessageQueue.empty())
    {
        mReceiveMessage = aMessage;
    }
    else
    {
        HBufC8* msg = mMessageQueue.front();
        TInt rc = doSend(aMessage, *msg);
        if (rc == KErrNone)
        {
            mMessageQueue.pop();
            delete msg;
        }
        else
        {
            WLOG2(EJavaComms, "%s failed, err = %d", __PRETTY_FUNCTION__, rc);
        }
    }
}

void CCommsSession::handleCancelReceive(const RMessage2& aMessage)
{
    ScopedLock lock(mMutex);

    if (isReceivePending())
    {
        mReceiveMessage.Complete(KErrCancel);
    }
    aMessage.Complete(KErrNone);
}

bool CCommsSession::isReceivePending() const
{
    return !mReceiveMessage.IsNull();
}

int CCommsSession::getId() const
{
    // to be used as unique id to identify session - not as pointer
    return (int)this;
}

TInt CCommsSession::doSend(const RMessagePtr2& aMessage, HBufC8& aData)
{
    TInt allocatedLength = aMessage.GetDesMaxLength(0);
    TInt neededLength = aData.Length();

    TPckgBuf<TInt> lengthPckg(neededLength);
    TInt rc = aMessage.Write(1, lengthPckg);

    if (rc == KErrNone && allocatedLength >= neededLength)
    {
        rc = aMessage.Write(0, aData.Des());
    }
    else
    {
        rc = KErrOverflow;
        WLOG3(EJavaComms, "%s: allocated length for message is too small (required size=%d, allocated size=%d)",
              __PRETTY_FUNCTION__, neededLength, allocatedLength);
    }

    aMessage.Complete(rc);
    return rc;
}

} // namespace comms
} // namespace java
