/*
* 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: IpcClient is the client side implementation
*
*/
#include <memory>
#include <sstream>
#include <stdlib.h>

#include "logger.h"
#include "javasymbianoslayer.h"

#include "ipcclient.h"
#include "rcomms.h"
#include "creceiver.h"
#include "common.h"

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

IpcClient::IpcClient(IpcListener* aListener) : CActive(EPriorityStandard),
        mAddress(0), mRunning(false), mReceiver(0), mListener(aListener)
{
}

IpcClient::~IpcClient()
{
}

int IpcClient::connect(int aAddr)
{
    ScopedLock lock(mMutex);

    if (mRunning) return 0;

    mAddress = aAddr;
    // create unique thread name
    std::stringstream name;
    name << "JavaCommsClient-" << mAddress << "-" << this << "-" << rand();
    std::auto_ptr<HBufC> threadName(stringToDes(name.str().c_str()));

    TInt rc = mThread.Create(
                  threadName->Des(),
                  reinterpret_cast<TThreadFunction>(messageLoop),
                  KCommsStackSize,
                  NULL,
                  this);

    if (rc == KErrNone)
    {
        // make sure that thread has been started ok before we continue.
        TRequestStatus rendezvousStatus;
        mThread.Rendezvous(rendezvousStatus);
        mThread.Resume();
        User::WaitForRequest(rendezvousStatus);
        rc = rendezvousStatus.Int();
        if (rc == KErrNone)
        {
            mRunning = true;
        }
    }

    if (rc != KErrNone)
    {
        mThread.Close();
        ELOG3(EJavaComms, "%s failed to java-comms-%d, err = %d", __PRETTY_FUNCTION__, aAddr, rc);
    }

    return rc;
}

void IpcClient::disconnect()
{
    ScopedLock lock(mMutex);

    if (!mRunning) return;

    TRequestStatus status;
    mThread.Logon(status);

    TRequestStatus* istatus = &iStatus;
    mThread.RequestComplete(istatus, KErrNone);

    User::WaitForRequest(status);
    mThread.Close();
    LOG1(EJavaComms, EInfo, "IpcClient disconnected from java-comms-%d", mAddress);
}

void IpcClient::error(TInt aError)
{
    ELOG2(EJavaComms, "%s, err = %d", __PRETTY_FUNCTION__, aError);
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, aError);
}


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

    int rc = KErrNotReady;
    if (mRunning)
    {
        std::auto_ptr<HBufC8> msg(messageToDes(*aMsg));
        rc = mComms.Send(msg->Des());
    }

    if (rc)
    {
        ELOG2(EJavaComms, "%s failed, err = %d", __PRETTY_FUNCTION__, rc);
    }
    return rc;
}

void IpcClient::RunL()
{
//    JELOG2(EJavaComms);
    mRunning = false; // don't allow sends while exiting

    mReceiver->Cancel();
    mComms.Disconnect();
    CActiveScheduler::Stop();
}

void IpcClient::DoCancel()
{
    ELOG1(EJavaComms, "%s", __PRETTY_FUNCTION__);
}

TInt IpcClient::RunError(TInt aError)
{
    ELOG2(EJavaComms, "%s, err = %d", __PRETTY_FUNCTION__, aError);
    return KErrNone;
}

void IpcClient::messageLoop(TAny* aArgs)
{
    std::auto_ptr<CTrapCleanup> cleanup(CTrapCleanup::New());
    TInt r = KErrNoMemory;
    if (cleanup.get())
    {
        IpcClient* me = reinterpret_cast<IpcClient*>(aArgs);
        TRAP(r,me->doMainL());
        me->mListener->onExit();
        me->mRunning = false;
    }
    // disconnect blocks until this notification
    RThread::Rendezvous(r);
}

void IpcClient::doMainL()
{
    CActiveScheduler* s = new(ELeave) CActiveScheduler;
    CleanupStack::PushL(s);
    CActiveScheduler::Install(s);

    std::stringstream address;
    address << "java-comms-" << mAddress;

    std::auto_ptr<HBufC> serverName(stringToDes(address.str().c_str()));

    User::LeaveIfError(mComms.Connect(serverName->Des()));
    LOG1(EJavaComms, EInfo, "IpcClient connected to %s", address.str().c_str());

    mReceiver = new(ELeave) CReceiver(*this, *mListener, mComms);
    CleanupStack::PushL(mReceiver);
    mReceiver->Receive();

    CActiveScheduler::Add(this);
    iStatus = KRequestPending;
    SetActive();

    // connect blocks until this notification
    RThread::Rendezvous(KErrNone);
    mListener->onStart();

    CActiveScheduler::Start();

    CleanupStack::PopAndDestroy(mReceiver);
    CleanupStack::PopAndDestroy(s);
}

} // namespace comms
} // namespace java
