/*
* 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 <stdlib.h>
#include <stdio.h>
#include <usbman.h>
#include <acminterface.h>
#include "logger.h"
#include "javacommonutils.h"
#include "s60commonutils.h"
#include "com_nokia_mj_impl_comm_CommConnectionImpl.h"
#include "nativecommconnection.h"
#include "fs_methodcall.h"


#if defined (__WINS__)
_LIT(PDD_NAME,"ECDRV");
_LIT(LDD_NAME,"ECOMM");
#else
_LIT(PDD_NAME, "EUART1");
_LIT(LDD_NAME, "ECOMM");
#endif

_LIT(KSymbianIRComm1, "IRComm");
_LIT(KSymbianIRComm2, "IRCOMM");
_LIT(KSymbianComm,"COMM");
_LIT(KSymbianAcm,"ACM");
_LIT(KSymbianBTComm1,"BTComm");
_LIT(KSymbianBTComm2,"BTCOMM");
_LIT(KSymbianECUART,"ECUART");

enum TCOMMOPTIONSINDEX
{
    ECommBaudRateIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_BAUDRATE_INDEX,

    ECommBitsPerCharIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_BITSPERCHAR_INDEX,

    ECommStopBitsIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_STOPBITS_INDEX,

    ECommParityIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_PARITY_INDEX,

    ECommBlockingIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_BLOCKING_INDEX,

    ECommAutoRtsIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_AUTORTS_INDEX,

    ECommAutoCtsIndex = com_nokia_mj_impl_comm_CommConnectionImpl_COMM_AUTOCTS_INDEX
};

using namespace java::util;
using namespace java::comm;

NativeCommConnection::NativeCommConnection(FunctionServer *aFuncServ) :  mConnectionIsOpen(false),mInternalBuf(NULL),mInternalBuflength(0)
{
    JELOG2(ESOCKET);
    iFuncServ = aFuncServ;
    mPortNames.insert(TPair("IR", "IRComm"));
    mPortNames.insert(TPair("USB", "ACM"));
    mPortNames.insert(TPair("COM", "COMM"));
    mPortNames.insert(TPair("BT", "BTComm"));
    mReadInProgress = false;
    mWriteInProgress = false;
}

NativeCommConnection::~NativeCommConnection()
{
    JELOG2(ESOCKET);

}

int NativeCommConnection::doGetBaudRate()
{
    JELOG2(ESOCKET);
    if (mConnectionIsOpen == false)
        return -1;
    return TBpsToTInt(mCommConfig.iRate);

}
int NativeCommConnection::getBaudRate()
{
    JELOG2(ESOCKET);
    int rate = 0;
    CallMethod(rate,this, &NativeCommConnection::doGetBaudRate,iFuncServ);
    ILOG1(ESOCKET, "NativeCommConnection::getBaudRate(), returning %d",rate);
    return rate;

}

int NativeCommConnection::doSetBaudRate(const int aBaudRate)
{
    JELOG2(ESOCKET);
    int oldvalue = doGetBaudRate();
    TBps tmpRate = mCommConfig.iRate;       // store the old rate, in case the setConfig fails, restore back this old one

    mCommConfig.iRate = TIntToTBps(aBaudRate);
    TCommConfig configBuf;
    TCommConfigV01& configRef = configBuf();
    configRef = mCommConfig;
    int ret = mComm.SetConfig(configBuf);
    if (ret < 0)
    {
        ELOG1(ESOCKET, "NativeCommConnection::doSetBaudRate(), setConfig failed for input %d",aBaudRate);
        /* value provided in aBaudRate is not supported, so restore back the old one */
        mCommConfig.iRate = tmpRate;
    }
    return oldvalue;
}

int NativeCommConnection::setBaudRate(const int aBaudRate)
{
    int oldRate = 0;
    CallMethod(oldRate,this, &NativeCommConnection::doSetBaudRate,aBaudRate,iFuncServ);
    return oldRate;
}

int NativeCommConnection::writeBytes(JNIEnv& aJni, jbyteArray aJavaBuffer,
                                     int aOffset, int aLength)
{
    JELOG2(ESOCKET);
    TBuf8<512> writeBuffer;
    int ret = S60CommonUtils::CopyToNative(aJni, aJavaBuffer, aOffset, aLength,
                                           writeBuffer);
    ILOG(ESOCKET, "NativeCommConnection::writeBytes(), copy to native completed");

    //TDesC8 tmp = writeBuffer.
    int handle = reinterpret_cast<int>(this);
    int bufhandle = reinterpret_cast<int>(&writeBuffer);


    //TRequestStatus status;
    //  mComm.Write(status, writeBuffer);
    //  User::WaitForRequest(status);

    int retValue = 0;
    CallMethod(retValue,this, &NativeCommConnection::doWrite,handle,bufhandle,iFuncServ);

    if (retValue != KErrNone)
    {
        ELOG1(ESOCKET, "NativeCommConnection::writeBytes() failed, status = %d",retValue);
        return retValue;
    }
    else
    {
        int tmp = writeBuffer.Length();
        return aLength;
    }

}


int NativeCommConnection::doWrite(int aNativeCommConnectionHandle, int aBufferHandle)
{
    JELOG2(ESOCKET);
    mWriteInProgress = true;
    NativeCommConnection *nativeObj = reinterpret_cast<NativeCommConnection*>(aNativeCommConnectionHandle);
    //TBuf8<512> writeBuffer = reinterpret_cast<TBuf8<512>>(aBufferHandle);
    TDesC8* writeBuffer = reinterpret_cast<TDesC8*>(aBufferHandle);
    TBuf8<512> tmp;

    TRequestStatus status;
    nativeObj->mComm.Write(status,*writeBuffer);
    User::WaitForRequest(status);
    ILOG1(ESOCKET, "NativeCommConnection::doWrite(), status = %d",status.Int());
    return status.Int();
}

int NativeCommConnection::doRead(JNIEnv& aJni, jbyteArray aJavaBuffer)
{
    JELOG2(ESOCKET);

    if (mInternalBuf!=NULL)
    {
        TPtrC8 tmpPtr(mInternalBuf->Des());
        /* available was called before read, so copy the data already read, which is store in internal buffer */
        S60CommonUtils::CopyToJava(aJni, tmpPtr, aJavaBuffer, 0,
                                   mInternalBuflength);
        mInternalBuf = NULL;
        ILOG1(ESOCKET, "--NativeCommConnection::doRead(), copied from internal buffer ,len = %d",mInternalBuflength);
        return mInternalBuflength;
    }

    mReadInProgress = true;
    ILOG(ESOCKET, "NativeCommConnection::doRead(), continuing");
    TRequestStatus status;
    TBuf8<512> readBuffer;
    mComm.ReadOneOrMore(status, readBuffer); // block until at least 1 byte of data is read
    User::WaitForRequest(status);
    int length = readBuffer.Length();
    TPtrC8 receiveBufferPtr = readBuffer.Ptr();


    if (status.Int() == KErrNone)
    {

        S60CommonUtils::CopyToJava(aJni, readBuffer, aJavaBuffer, 0,
                                   length);
    }
    if (status.Int() != KErrNone)
    {
        return status.Int(); // error code
    }
    else
    {
        ILOG1(ESOCKET, "NativeCommConnection::doRead(), read successfully, length = %d",length);
        return length;
    }

}

int NativeCommConnection::readBytes(JNIEnv& aJni, jbyteArray aJavaBuffer)
{
    JELOG2(ESOCKET);
    int ret = 0;
    CallMethod(ret,this,&NativeCommConnection::doRead,aJni,aJavaBuffer,iFuncServ);
    return ret;
}

void NativeCommConnection::initializePortSettings(int *aNumericOptions)
{
    JELOG2(ESOCKET);
    // mCommConfig holds the current configuration, update it with new values
    TParity parity;
    if (aNumericOptions[ECommParityIndex] == 1)
    {
        parity = EParityOdd;
        mCommConfig.iParity = parity;
    }
    else if (aNumericOptions[ECommParityIndex] == 2)
    {
        parity = EParityEven;
        mCommConfig.iParity = parity;
    }

    if (aNumericOptions[ECommAutoCtsIndex] == 1)
        mCommConfig.iHandshake |= KConfigObeyCTS; // option: autocts
    else
        mCommConfig.iHandshake &= ~KConfigObeyCTS;

    if (aNumericOptions[ECommAutoRtsIndex] == 1)
        mCommConfig.iHandshake |= KConfigFreeRTS; // option: autorts
    else
        mCommConfig.iHandshake &= ~KConfigFreeRTS;

    if (aNumericOptions[ECommBaudRateIndex] != -1)
        doSetBaudRate(aNumericOptions[ECommBaudRateIndex]); // option: baudrate

    if (aNumericOptions[ECommBitsPerCharIndex] == 7)
        mCommConfig.iDataBits = EData7; //  option: bitsperchar
    else
        mCommConfig.iDataBits = EData8;

    if (aNumericOptions[ECommStopBitsIndex] == 1)
        mCommConfig.iStopBits = EStop1; // option: stopbits
    else
        mCommConfig.iStopBits = EStop2;

    TCommConfig configBuf1;
    TCommConfigV01& configRef = configBuf1();
    configRef = mCommConfig;
    mComm.SetConfig(configBuf1); // set the new configuration

}

std::basic_string<char> NativeCommConnection::mapToSymbianPortName(
    const std::string aJavaPortName)
{
    JELOG2(ESOCKET);
    std::map<std::string, std::string>::iterator itr = mPortNames.find(
                aJavaPortName);
    if (itr != mPortNames.end())
    {
        return itr->second; // take the value string <name,value>
    }
    return NULL;
}

int NativeCommConnection::openPortConnection(std::string aJavaPortName, int aPortNumber, int *aNumericOptions)
{
    int ret = 0;
    CallMethod(ret,this,&NativeCommConnection::openCommPortConnection,aJavaPortName,aPortNumber,aNumericOptions,iFuncServ);
    return ret;
}

int NativeCommConnection::openCommPortConnection(
    const std::string aJavaPortName, int aPortNumber, int *aNumericOptions)
{
    JELOG2(ESOCKET);
    mNativePortName = mapToSymbianPortName(aJavaPortName);
    int ret = connectToCommServer();
    if (ret != KErrNone)
    {
        return ret; // error occurred
    }

    // RComm.Open() takes the argument port id in the form "IR::0" (or) "ACM::1" ,...
    // The java port number always starts from 1( MIDP spec).
    // So convert this java port number to actual native port number using the base index stored previously.

    std::basic_string<char> prtname;
    char portNumberString[50];
    sprintf(portNumberString, "%d", (aPortNumber + mPortLowIndex) - 1);
    prtname = mNativePortName + "::" + portNumberString;

    /* convert from string to TBuf16 */
    TBuf8<50> portNameUTF;
    TBuf16<50> portname;
    TPtrC8 portNamePtr((TText8*) prtname.c_str());
    portNameUTF.Copy(portNamePtr);
    portname.Copy(portNameUTF); // convert from TBuf8 to TBuf16, because RCommServer.open() accepts only TDesc16 type

    char* t5 = (char *)prtname.c_str();

    ret = mComm.Open(mCommServer, portname, ECommExclusive);
    if (ret == KErrNone)
    {
        ILOG(ESOCKET, "openCommPortConnection() success");
        mConnectionIsOpen = true;
        TCommConfig cBuf;
        TCommConfigV01& c = cBuf();
        mComm.Config(cBuf);
        mCommConfig = c;
        initializePortSettings(aNumericOptions);
    }
    ELOG1(ESOCKET, "--NativeCommConnection::openCommPortConnection()",2);
    return ret;
}

void NativeCommConnection::doOpenConnnection(int portNameHandle)
{
    JELOG2(ESOCKET);
    char *t2 = reinterpret_cast<char*>(portNameHandle);
    TBuf8<50> portNameUTF;
    TBuf16<50> portname;
    TPtrC8 portNamePtr((TText8*) t2);
    portNameUTF.Copy(portNamePtr);
    portname.Copy(portNameUTF);


    int rc = mComm.Open(mCommServer, portname, ECommExclusive);
    if (rc != KErrNone)
    {
        ELOG1(ESOCKET, "NativeCommConnection open failed: %d", rc);
    }
}

void NativeCommConnection::doStopReading()
{
    mComm.ReadCancel();     // cancel the read
    mReadInProgress = false;
    if (!mWriteInProgress)
    {
        ILOG(ESOCKET, "++NativeCommConnection::doStopReading(), releasing the connection");
        /* if there is no write going on, then release the comm port */
        mComm.Close();
        mCommServer.Close();
        mConnectionIsOpen = false;
    }
}

void NativeCommConnection::stopReading()
{
    JELOG2(ESOCKET);
    CallMethod(this, &NativeCommConnection::doStopReading,iFuncServ);
    // cancel any pending read operation
}

void NativeCommConnection::doStopWriting()
{
    mComm.WriteCancel();
    mWriteInProgress = false;
}

void NativeCommConnection::stopWriting()
{
    // cancel any pending write operation
    JELOG2(ESOCKET);
    CallMethod(this, &NativeCommConnection::doStopWriting,iFuncServ);
}

int NativeCommConnection::doAvailable()
{
    TRequestStatus status;
    TBuf8<512> readBuffer;
    mComm.ReadOneOrMore(status, readBuffer); // block until at least 1 byte of data is read
    User::WaitForRequest(status);
    int length = readBuffer.Length();
    TPtrC8 receiveBufferPtr = readBuffer.Ptr();
    if (status.Int() == KErrNone)
    {
        TRAPD(err,mInternalBuf = HBufC8::NewL(length));
        if (err < 0)
        {
            return err;
        }
        TPtr8 tmpPtr(mInternalBuf->Des());
        tmpPtr.Copy(readBuffer);
        mInternalBuflength = length;
        return length;
    }
    else
        return 0;

}

int NativeCommConnection::available()
{
    JELOG2(ESOCKET);
    int ret;
    CallMethod(ret,this, &NativeCommConnection::doAvailable,iFuncServ);
    ILOG1(ESOCKET, "NativeCommConnection::available(), returning %d",ret);
    return ret;
}

int NativeCommConnection::connectToCommServer()
{
    JELOG2(ESOCKET);
    int ret = 0;
    User::LoadPhysicalDevice(PDD_NAME); // load physical device drivers
    User::LoadLogicalDevice(LDD_NAME); // load logical device drivers

    ret = mCommServer.Connect();
    if (ret != KErrNone)
        return -ret;

    /* convert from std::string * to TBuf16 */
    TBuf8<50> portNameUTF;
    TBuf16<50> portname;
    TPtrC8 portNamePtr((TText8*) mNativePortName.c_str()); // covert c-style char* to symbian specific TPtr8
    portNameUTF.Copy(portNamePtr);
    portname.Copy(portNameUTF);

    /* Load the comm module corresponding to the name */
    if (portname.Match(KSymbianIRComm1) != KErrNotFound)
        ret = mCommServer.LoadCommModule(KSymbianIRComm2);
    else if (portname.Match(KSymbianComm) != KErrNotFound)
        ret = mCommServer.LoadCommModule(KSymbianECUART);
    else if (portname.Match(KSymbianBTComm1) != KErrNotFound)
        ret = mCommServer.LoadCommModule(KSymbianBTComm2);
    else if (portname.Match(KSymbianAcm) != KErrNotFound)
        ret = mCommServer.LoadCommModule(KAcmCsyName);

    if (ret != KErrNone)
        return ret;

    // Get base offsets of specified port.
    TSerialInfo info;
    ret = mCommServer.GetPortInfo(portname, info);
    if (ret == KErrNone)
        mPortLowIndex = info.iLowUnit;

    ILOG1(ESOCKET, "NativeCommConnection::Lowunit  = %d", mPortLowIndex);

    return ret;
}

void NativeCommConnection::doClose()
{
    JELOG2(ESOCKET);
    if (mConnectionIsOpen != false)
    {
        mComm.Close();
        mCommServer.Close();
        mConnectionIsOpen = false;
    }
}

void NativeCommConnection::close()
{
    CallMethod(this, &NativeCommConnection::doClose,iFuncServ);

}

// convert a baud rate passed as a TBps into an integer,
int NativeCommConnection::TBpsToTInt(TBps aRate)
{
    JELOG2(ESOCKET);
    switch (aRate)
    {
    case EBps50:
        return 50;
    case EBps75:
        return 75;
    case EBps110:
        return 110;
    case EBps134:
        return 134;
    case EBps150:
        return 150;
    case EBps300:
        return 300;
    case EBps600:
        return 600;
    case EBps1200:
        return 1200;
    case EBps1800:
        return 1800;
    case EBps2000:
        return 2000;
    case EBps2400:
        return 2400;
    case EBps3600:
        return 3600;
    case EBps4800:
        return 4800;
    case EBps7200:
        return 7200;
    case EBps9600:
        return 9600;
    case EBps19200:
        return 19200;
    case EBps38400:
        return 38400;
    case EBps57600:
        return 57600;
    case EBps115200:
        return 115200;
    case EBps230400:
        return 230400;
    case EBps460800:
        return 460800;
    case EBps576000:
        return 576000;
    case EBps1152000:
        return 1152000;
    case EBps4000000:
        return 4000000;
    case EBps921600:
        return 921600;
    default:
        return -1;
    }
}

// convert a baud rate passed as an int into a TBps enum value,
// rates are rounded down to the next rate, anything less than 75 is set to 50
TBps NativeCommConnection::TIntToTBps(const TInt aRate)
{
    JELOG2(ESOCKET);
    if (aRate > 4000000)
        return EBpsSpecial;
    if (aRate == 4000000)
        return EBps4000000;
    if (aRate >= 1152000)
        return EBps1152000;
    if (aRate >= 921600)
        return EBps921600;
    if (aRate >= 576000)
        return EBps576000;
    if (aRate >= 460800)
        return EBps460800;
    if (aRate >= 230400)
        return EBps230400;
    if (aRate >= 115200)
        return EBps115200;
    if (aRate >= 57600)
        return EBps57600;
    if (aRate >= 38400)
        return EBps38400;
    if (aRate >= 19200)
        return EBps19200;
    if (aRate >= 9600)
        return EBps9600;
    if (aRate >= 7200)
        return EBps7200;
    if (aRate >= 4800)
        return EBps4800;
    if (aRate >= 3600)
        return EBps3600;
    if (aRate >= 2400)
        return EBps2400;
    if (aRate >= 2000)
        return EBps2000;
    if (aRate >= 1800)
        return EBps1800;
    if (aRate >= 1200)
        return EBps1200;
    if (aRate >= 600)
        return EBps600;
    if (aRate >= 300)
        return EBps300;
    if (aRate >= 150)
        return EBps150;
    if (aRate >= 134)
        return EBps134;
    if (aRate >= 110)
        return EBps110;
    if (aRate >= 75)
        return EBps75;
    return EBps50;
}

