/*
* Copyright (c) 2002-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:  Class to handle OutputStream operations.
*
*/

#include <logger.h>

#include <JniEnvWrapper.h>
#include "cmmaoutputstream.h"
#include "mmmaeventposter.h"
#include "cmmaoutputstreamevent.h"

// CONSTANTS
const TInt KMMACommit = -10000; // indicates native started commit

CMMAOutputStream* CMMAOutputStream::NewL(JNIEnv* aJNIEnv,
        MMMAEventPoster* aEventPoster,
        jobject aJavaOutputStreamWriter
                                        )
{
    CMMAOutputStream* self = CMMAOutputStream::NewLC(aJNIEnv,
                             aEventPoster,
                             aJavaOutputStreamWriter
                                                    );
    CleanupStack::Pop();
    return self;
}


CMMAOutputStream* CMMAOutputStream::NewLC(JNIEnv* aJNIEnv,
        MMMAEventPoster* aEventPoster,
        jobject aJavaOutputStreamWriter
                                         )
{
    CMMAOutputStream* self = new(ELeave) CMMAOutputStream(aEventPoster);
    CleanupStack::PushL(self);
    self->ConstructL(aJNIEnv, aJavaOutputStreamWriter);
    return self;
}


void CMMAOutputStream::CreateL(CMMAOutputStream** aOutputStream,
                               MMAFunctionServer* aEventServer,
                               JNIEnv* aJniEnv,
                               MMMAEventPoster* aEventPoster,
                               jobject aJavaOutputStreamWriter)
{

    // JNI interface pointer can't be passed to different thread, so
    // it is needed to get valid JNI interface pointer for Event Server thread
    aJniEnv = aEventServer->getValidJniEnv();

    *aOutputStream = NewL(aJniEnv, aEventPoster, aJavaOutputStreamWriter);
}


CMMAOutputStream::~CMMAOutputStream()
{
    LOG(EJavaMMAPI, EInfo, "MMA::CMMAOutputStream::~");

    // If write event is in the event server, it cannot be deleted.
    // Thus set the event to be deleted when event dispatch is called.
    if (iWriteEvent &&
            iWriteEvent->State() == CMMAOutputStreamEvent::EMMAEventActive)
    {
        iWriteEvent->SetState(CMMAOutputStreamEvent::EMMADeleteEvent);
    }
    else
    {
        delete iWriteEvent;
    }

    delete iData;
    LOG(EJavaMMAPI, EInfo, "MMA::CMMAOutputStream::~ OK");
}


// Default constructor
CMMAOutputStream::CMMAOutputStream(MMMAEventPoster* aEventPoster)
        : iEventSource(aEventPoster),
        iPtr(NULL, 0)
{
    LOG(EJavaMMAPI, EInfo, "MMA::CMMAOutputStream constructed");
}


void CMMAOutputStream::ConstructL(JNIEnv* aJNIEnv,
                                  jobject aJavaOutputStreamWriter)
{
    LOG(EJavaMMAPI, EInfo, "CMMAOutputStream::ConstructL()");

    // void write( int aLength, int aStatus ) method in OutputStreamWriter
    jmethodID classMethodID =
        aJNIEnv->GetMethodID(
            aJNIEnv->GetObjectClass(aJavaOutputStreamWriter),
            "write",
            "(II)V");
    if (!classMethodID)
    {
        LOG(EJavaMMAPI, EInfo, "CMMAOutputStream::ConstructL: Cannot find java method");
        User::Panic(_L("Java method write(II)V not found"), KErrGeneral);
    }

    iWriteEvent = new(ELeave) CMMAOutputStreamEvent(classMethodID,
            aJavaOutputStreamWriter);
}

void CMMAOutputStream::ReadDataL(TUint8* aOutputData,
                                 TInt* aBufferSize,
                                 TInt* aReadStatus)
{
    if (!iData)
    {
        User::Leave(KErrNotReady);
    }

    // Status code to be returned
    TInt status = KErrNone;

    TPtr8 buffer(aOutputData, *aBufferSize);
    LOG1(EJavaMMAPI, EInfo, " MMA::CMMAOutputStream::ReadDataL SIZE BEFORE READL %d", buffer.Length());
    LOG1(EJavaMMAPI, EInfo, " MMA::CMMAOutputStream::ReadDataL BYTES AVAILABLE %d", iPtr.Length());

    // Bytes available in native buffer
    TInt bytesAvailable = iPtr.Length();

    // Maximum size that can be written to Java buffer
    TInt outputMaxLength = buffer.MaxLength();

    // Length that can be written
    TInt writeLength = 0;

    if (outputMaxLength < bytesAvailable)
    {
        // Not all bytes can be written Java buffer
        writeLength = outputMaxLength;

        // Java need to read more data
        status = EMoreData;
    }
    else
    {
        // All bytes can be written to Java buffer
        writeLength = bytesAvailable;

        // All data is copied
        status = ECompleted;
    }

    // Copy maximum number of bytes to Java buffer
    buffer.Copy(iPtr.Left(writeLength));
    *aBufferSize = buffer.Length();

    // Move pointer to next read position.
    iPtr = iPtr.Mid(writeLength);

    LOG1(EJavaMMAPI, EInfo, " MMA::CMMAOutputStream::ReadDataL SIZE AFTER READL %d", buffer.Length());
    LOG1(EJavaMMAPI, EInfo, " MMA::CMMAOutputStream::ReadDataL RETURN %d", status);
    LOG1(EJavaMMAPI, EInfo, " MMA::CMMAOutputStream::ReadDataL dataLeft %d", iPtr.Length());

    if (iPtr.Length() == 0)
    {
        // All data is read and buffer is not needed anymore.
        delete iData;
        iData = NULL;
    }
    *aReadStatus = status;
}

void CMMAOutputStream::WriteL(const TDesC8& aData)
{
    LOG1(EJavaMMAPI, EInfo, "CMMAOutputStream::WriteL data size = %d", aData.Size());
    if (iData)
    {
        // Previous data was not readed from the stream.
        User::Leave(KErrNotReady);
    }

    if (aData.Length() > 0)
    {
        // Take a copy of new data.
        HBufC8* data = aData.AllocL();
        delete iData;
        iData = data;
        iPtr.Set(iData->Des());

        // Set java event
        LOG1(EJavaMMAPI, EInfo, "CMMAOutputStream::WriteL: available data: %d", iData->Length());
        iWriteEvent->SetLength(iData->Length());
        iWriteEvent->SetStatus(EMoreData);
    }
    else
    {
        LOG(EJavaMMAPI, EInfo, "CMMAOutputStream::WriteL Zero length data");
        iWriteEvent->SetLength(0);
        iWriteEvent->SetStatus(ECompleted);
    }
    iEventSource->PostEvent(iWriteEvent, CMMAEvent::ENotifyPriority);
}

void CMMAOutputStream::Commit()
{
    iWriteEvent->SetLength(0);
    iWriteEvent->SetStatus(KMMACommit);
    iEventSource->PostEvent(iWriteEvent, CMMAEvent::ENotifyPriority);
}

//  END OF FILE

