/*
* 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:  Connection Manager
*                Provides Native conection management for all Java Connections:
                 - Network Access selection
*
*/


#include <memory>
#include <agentdialog.h>                    // RGenConAgentDialogServer
#include <commsdattypesv1_1.h>              // TMDBElementId
#include <logger.h>
#include <commdb.h>
#include <cmmanager.h>
#include <cmdefconnvalues.h>
#include <cmmanagerdef.h>
#include <javastorageentry.h>
#include <javastorage.h>
#include <javastoragenames.h>
#include <javasymbianoslayer.h>
#include <cmdestination.h>
#include "connectionmanager.h"
#include "javacommonutils.h"
#include "s60commonutils.h"

using namespace java::storage;
using namespace CMManager;

_LIT8(KAskAlwaysId, "A");
_LIT8(KAskOnceId, "O");
_LIT8(KDefaultConn, "F");
_LIT8(KSnapId, "D");
_LIT8(KIapId, "I");


enum JavaNetworkAccessPointType
{
    ENapNone = 0,               ///< Not defined
    ENapAskAlways,              ///< Always ask
    ENapDefault,                ///< Default Conn
    ENapSnap,                   ///< SNAP ID is selected
    ENapIap,                    ///< IAP ID is selected
};


// Byte shift
const TInt KShift8 = 8;

// ---------------------------------------------------------------------------
// ConnectionManager::isIapDefault
// ---------------------------------------------------------------------------
//

EXPORT_C bool ConnectionManager::isIapDefault(TUint32 aMatchIapId, TUint32 aDestId, bool aDefault)
{
    LOG(ESOCKET,EInfo,"+ConnectionManager::getDefualtId");
    TUint32 id = aDestId;
    TUint32 tmpapId;
    TCmDefConnType type;

    RCmManager * mgr = new(ELeave) RCmManager();
    mgr->OpenL();

    if (aDefault)
    {
        TCmDefConnValue obj;
        mgr->ReadDefConnL(obj);
        id = obj.iId;
        type = obj.iType;
        if ((type ==ECmDefConnConnectionMethod) && (type == aMatchIapId))
        {
            return true;
        }
        else if (type != ECmDefConnDestination)
        {
            return false;
        }
    }

    RCmDestination tmpdst =     mgr->DestinationL(id);
    ILOG1(ESOCKET,"Is connected return value = %d ", tmpdst.IsConnectedL());
    if (!(tmpdst.IsConnectedL()))
    {
        return false;   // no access point within this destination are active
    }
    for (TInt j = 0; j < tmpdst.ConnectionMethodCount(); j++)
    {
        tmpapId = tmpdst.ConnectionMethodL(j).GetIntAttributeL(ECmIapId);
        ILOG1(ESOCKET,"tmpai id = %d   ",tmpapId);
        if (tmpapId == aMatchIapId)
        {
            return true;
        }
    }
    return false;

}

// ---------------------------------------------------------------------------
// JavaNetworkAccessRepository::GetDeviceDefaultAccessPointL
// ---------------------------------------------------------------------------
//
void ConnectionManager
::GetDeviceDefaultAccessPointL(char * access)
{
    int id;
    TCmDefConnType type;
    LOG(ESOCKET,EInfo,"+GetDeviceDefaultAccessPointL");

    RCmManager * mgr = new(ELeave) RCmManager();
    mgr->OpenLC();
    TCmDefConnValue obj;
    HBufC8 * des;


    mgr->ReadDefConnL(obj);
    id = obj.iId;
    type = obj.iType;
    LOG1(ESOCKET,EInfo,"readDefConnL returned %d",id);
    LOG1(ESOCKET,EInfo,"type is %d",type);
    CleanupStack::PopAndDestroy();


    if (type == ECmDefConnAskOnce || type == ECmDefConnConnectionMethod || type == ECmDefConnDestination)
    {
        des = CreateDescriptorL(type,id);
    }
    else
    {
        des = PromptUserSelectNetworkAccessPointL();
        LOG(ESOCKET,EInfo," after PromptUserSelectNetworkAccessPointL ");
    }
    TInt len = des->Length();
    LOG1(ESOCKET,EInfo,"+des length =  %d",len);
    LOG(ESOCKET,EInfo,"after lenght");
    memcpy(access, des->Des().PtrZ(),len+1);
    LOG(ESOCKET,EInfo,"after memcpy");
    LOG1(ESOCKET,EInfo,"+apnInfo =  %s",access);
    LOG1(ESOCKET,EInfo,"+apnInfo =  %s",access);
    CleanupStack::PopAndDestroy(des);
}

void ConnectionManager
::PromptUserL(char * access)
{
    HBufC8 * des;
    des = PromptUserSelectNetworkAccessPointL();
    TInt len = des->Length();
    memcpy(access, des->Des().PtrZ(),len+1);
    CleanupStack::PopAndDestroy(des);
}

// ---------------------------------------------------------------------------
// JavaNetworkAccessRepository::PromptUserSelectNetworkAccessL
// ---------------------------------------------------------------------------
//
HBufC8 *ConnectionManager
::PromptUserSelectNetworkAccessPointL()
{
    LOG(ESOCKET,EInfo,"+PromptUserSelectNetworkAccessPointL");
    TUint32 id((TUint32)KJavaNetworkAccessNotSpecified);
    TUint32 snapOrIapId(0);

    TConnectionPrefs prefs;
    prefs.iRank = 1;
    prefs.iDirection = ECommDbConnectionDirectionOutgoing;
    prefs.iBearerSet = ECommDbBearerCSD | ECommDbBearerWcdma
                       | ECommDbBearerLAN | ECommDbBearerVirtual
#ifdef RD_JAVA_SUPPORT_CDMA
                       | ECommDbBearerCdma2000
#endif
                       | ECommDbBearerPSD
                       | ECommDbBearerPAN | ECommDbBearerWLAN;

    TUint32 dummy(0);

    RGenConAgentDialogServer dlgSv;
    CleanupClosePushL(dlgSv);
    User::LeaveIfError(dlgSv.Connect());


    TRequestStatus status(KRequestPending);
    LOG(ESOCKET,EInfo,"prompting by regenconagent ");
    dlgSv.AccessPointConnection(dummy, dummy, snapOrIapId, prefs.iBearerSet, status);
    User::WaitForRequest(status);
    CleanupStack::PopAndDestroy(&dlgSv);
    User::LeaveIfError(status.Int());


    TMDBElementId tableId = snapOrIapId & KCDMaskShowRecordType;
    if (tableId == KCDTIdNetworkRecord)
    {
        // User selects SNAP
        LOG(ESOCKET,EInfo,"user selected SNAP");
        id = (snapOrIapId & KCDMaskShowRecordId) >> KShift8;
        return CreateDescriptorL(2, id);
    }
    else if (tableId == KCDTIdIAPRecord)
    {
        // User selects IAP
        LOG(ESOCKET,EInfo,"user selected IAP");
        id = (snapOrIapId & KCDMaskShowRecordId) >> KShift8;
        return CreateDescriptorL(3, id);
    }
    else
    {
        // Uknown selection
    }
    return 0;
}

// ---------------------------------------------------------------------------
// NetworkAccessRepository::DescriptorLC
// ---------------------------------------------------------------------------
//
HBufC8 * ConnectionManager::CreateDescriptorL(const int aType, const int id)
{
    LOG(ESOCKET,EInfo,"+ConnectionManager::CreateDescriptorL");
    HBufC8* des = HBufC8::NewLC(256);
    TPtr8 ptr(des->Des());
    switch (aType)
    {
    case ECmDefConnAlwaysAsk :
        ptr = KAskAlwaysId();
        break;
    case ECmDefConnAskOnce :
        ptr = KAskOnceId();
        break;

    case ECmDefConnDestination :
        if (id == KDefaultConnection)
        {
            // case of defaultconnection set in app settings
            // store 'F' into java storage
            ptr = KDefaultConn();
        }
        else
        {
            ptr = KSnapId();
            ptr.AppendNum(id);
        }
        break;
    case ECmDefConnConnectionMethod :
        LOG(ESOCKET,EInfo,"ECmDefConnConnectionMethod case");
        ptr = KIapId();
        ptr.AppendNum(id);
        break;
    default :
        User::Leave(KErrNotSupported);
        break;
    }
    // CleanupStack::PopAndDestroy();
    LOG(ESOCKET,EInfo,"-ConnectionManager::CreateDescriptorL");
    return des;
}

EXPORT_C unsigned int ConnectionManager::getDestinationNetworkIdL(Uid aAppSuiteUid)
{
    JavaStorageApplicationEntry_t entries;
    std::wstring temp;
    JavaStorageEntry attribute;
    int snapid = KJavaNetworkAccessNotSpecified;
    std::auto_ptr<JavaStorage> js(JavaStorage::createInstance());

    js->open(JAVA_DATABASE_NAME);


    // Read all attributes of one application specified by UID.
    js->read(APPLICATION_PACKAGE_TABLE, aAppSuiteUid, entries);
    js->close();

    // set the entry as ACCESS_POINT
    attribute.setEntry(ACCESS_POINT, L"");

    // Get value of the attribute from read attributes.
    while (!entries.empty())
    {
        JavaStorageApplicationEntry_t::const_iterator findIterator = entries.find(attribute);

        if (findIterator != entries.end())
        {
            JavaStorageEntry sourceEntry = (*findIterator);
            temp.append(sourceEntry.entryValue().c_str());
            HBufC * value = S60CommonUtils::wstringToDes(temp.c_str());
            HBufC8 * temp1 = HBufC8::NewL(value->Des().Length());
            temp1->Des().Copy(value->Des());
            TRAP_IGNORE(snapid = ParseNetworkAccessPointL(*temp1));
        }
        else
        {
            LOG(ESOCKET,EInfo,"Name attribute does not exists.");
        }
        entries.clear();
    }
    return snapid;
}

EXPORT_C unsigned int ConnectionManager::getApnIdL(Uid aAppSuiteUid)
{
    int apnid = KJavaNetworkAccessNotSpecified;
    JavaStorageApplicationEntry_t entries;
    std::auto_ptr<JavaStorage> js(JavaStorage::createInstance());

    try
    {
        js->open();
        js->read(APPLICATION_PACKAGE_TABLE, aAppSuiteUid, entries);
        js->close();
    }
    catch (JavaStorageException& aJse)
    {
        LOG1(ESOCKET, EInfo,"APN read error: %s", aJse.toString().c_str());
        return apnid;
    }

    JavaStorageEntry attribute;
    attribute.setEntry(ACCESS_POINT, L"");

    JavaStorageApplicationEntry_t::const_iterator findIterator
    = entries.find(attribute);

    if (findIterator != entries.end())
    {
        std::auto_ptr<HBufC>value(S60CommonUtils::wstringToDes(
                                      (*findIterator).entryValue().c_str()));
        std::auto_ptr<HBufC8> temp1(HBufC8::NewL(value->Des().Length()));
        temp1->Des().Copy(value->Des());
        TRAP_IGNORE(apnid = ParseNetworkAccessPointL(*temp1));
    }
    else
    {
        WLOG1WSTR(ESOCKET, "APN ID not found for the Application: %s",
                  aAppSuiteUid.toString());
    }
    return apnid;
}


EXPORT_C void ConnectionManager::setApnIdL(Uid aAppSuiteUid,int aApnId)
{
    // only IAP ids supported
    std::auto_ptr<HBufC8>des(CreateDescriptorL(ECmDefConnConnectionMethod, aApnId));
    CleanupStack::Pop(); // Pop CreateDescriptorL added HBufC from cleanupstack.

    std::auto_ptr<HBufC> temp1(HBufC::NewL(des->Des().Length() + 1));
    temp1->Des().Copy(des->Des());

    TPtr ptr = temp1->Des();
    std::wstring apnString(desToWstring(ptr));

    LOG1(ESOCKET,EInfo,"ConnectionManager::setApnIdL() - len of apnString = %d",
         apnString.size());

    LOG1WSTR(ESOCKET,
             EInfo,
             "COnnectionManager::setApnIdL() - storing %s into javastorage",
             apnString);

    JavaStorageApplicationEntry_t matchEntries;
    std::auto_ptr<JavaStorage> js(JavaStorage::createInstance());

    try
    {
        js->open();
        // Check application really exists there. Otherwise orphaned column is
        // written to storage.
        js->read(APPLICATION_PACKAGE_TABLE, aAppSuiteUid, matchEntries);
        if (matchEntries.size() > 0)
        {
            JavaStorageApplicationEntry_t updateEntries;
            JavaStorageEntry attribute;

            attribute.setEntry(ACCESS_POINT, apnString);
            updateEntries.insert(attribute);

            matchEntries.clear();
            attribute.setEntry(ID, aAppSuiteUid.toString());
            matchEntries.insert(attribute);

            js->update(APPLICATION_PACKAGE_TABLE, updateEntries, matchEntries);
        }
    }
    catch (JavaStorageException& aJse)
    {
        WLOG1(ESOCKET, "Connection manager failed to store access point: %s", aJse.toString().c_str());
    }

    js->close();
}
EXPORT_C void ConnectionManager::setDestinationNetworkIdL(Uid aAppSuiteUid,int aSnapId)
{
    std::auto_ptr<JavaStorage> js(JavaStorage::createInstance());
    JavaStorageApplicationEntry_t updateEntries;
    JavaStorageApplicationEntry_t matchEntries;
    JavaStorageEntry attribute;
    HBufC8* des = 0;
    try
    {
        js->open(JAVA_DATABASE_NAME);
    }
    catch (JavaStorageException jse)
    {
        LOG(ESOCKET,EInfo,"opening javastorage db failed");
    }

    // only SNAP ids supported
    if (aSnapId == KAlwaysAskId)
    {
        des = CreateDescriptorL(ECmDefConnAlwaysAsk,KAlwaysAskId);
    }
    else
    {
        if (aSnapId == KDefaultConnection)
        {
            des = CreateDescriptorL(ECmDefConnDestination,KDefaultConnection);
        }
        else
        {
            des = CreateDescriptorL(ECmDefConnDestination,aSnapId);
        }
    }

    HBufC* temp1 = HBufC::NewL(des->Des().Length()+1);
    temp1->Des().Copy(des->Des());

    int len1 = des->Length();
    int len2 = temp1->Length();
    LOG2(ESOCKET,EInfo,"ConnectionManager::setApnIdL() - len1= %d, len2 = %d",len1, len2);
    wchar_t* apnString = new wchar_t[len2+1];

    TPtr ptr = temp1->Des();
    TPtr ptr16((TUint16*)apnString,len2+1);

    ptr16.Copy(ptr);
    ptr16.SetLength(len2);
    ptr16.ZeroTerminate();

    LOG1(ESOCKET,EInfo,"ConnectionManager::setApnIdL() - len of apnString = %d", wcslen(apnString));

    std::wstring tmpstring(apnString);
    char *dirName = JavaCommonUtils::wstringToUtf8(tmpstring);
    LOG1(ESOCKET,EInfo,"COnnectionManager::setApnIdL() - storing %s into javastorage",dirName);


    js->startTransaction();

    attribute.setEntry(ACCESS_POINT, apnString);
    updateEntries.insert(attribute);

    attribute.setEntry(ID, aAppSuiteUid.toString());
    matchEntries.insert(attribute);


    LOG(ESOCKET,EInfo,"ConnectionManager::setApnIdL() - before js update");
    try
    {
        js->update(APPLICATION_PACKAGE_TABLE, updateEntries, matchEntries);
    }
    catch (JavaStorageException jse)
    {
        ELOG1(ESOCKET,"Updating the javastorage db failed %S",jse.toString().c_str());
        js->close();
        CleanupStack::PopAndDestroy(des);
        return;
    }

    try
    {
        js->commitTransaction();
    }
    catch (JavaStorageException jse)
    {
        ELOG(ESOCKET,"Commit transaction in javastorage db failed");
        js->close();
        CleanupStack::PopAndDestroy(des);
        return;
    }

    js->close();
    updateEntries.clear();
    matchEntries.clear();
    CleanupStack::PopAndDestroy(des);
}

unsigned int ConnectionManager::ParseNetworkAccessPointL(const TDesC8& aDes)
{
    JavaNetworkAccessPointType type(ENapNone);
    TUint32 id = (TUint32)KJavaNetworkAccessNotSpecified;

    TLex8 charLexer(aDes);
    charLexer.Mark();
    // First character in descriptor MUST indicates Network Access type <(A)|(D|I)ID>
    if (!(charLexer.Peek()).IsAlpha())
    {
        // First character in descriptor IS NOT alpha
        User::Leave(KErrCorrupt);
    }

    // First character in descriptor IS alpha
    charLexer.Inc();
    TPtrC8 parsedText(charLexer.MarkedToken());

    // Parse what Network Access type is indicated
    if (parsedText.Compare(KAskAlwaysId) == 0)
    {
        type = ENapAskAlways;
        id = (TUint32) KAlwaysAskId;
    }
    // In case of default connection, equal to'F' case
    // return KDefaultConnection to app mgr
    else if (parsedText.Compare(KDefaultConn) == 0)
    {
        type = ENapDefault;
        id = (TUint32) KDefaultConnection;
    }
    else if (parsedText.Compare(KSnapId) == 0)
    {
        type = ENapSnap;
    }
    else if (parsedText.Compare(KIapId) == 0)
    {
        type = ENapIap;
    }
    else
    {
        // First character IS NOT pre-defined indicator
        User::Leave(KErrCorrupt);
    }

    if (type == ENapAskAlways || type == ENapDefault)
    {
        // Descriptor MUST contain only indicator
        if (!charLexer.Eos())
        {
            // Descriptor contains more than indicator
            User::Leave(KErrArgument);
        }
        // Success go to end
    }
    else
    {
        // Descriptor MUST contain only indicator and Network Access Point ID
        if (charLexer.Eos())
        {
            // Descriptor contains only indicator
            User::Leave(KErrCorrupt);
        }

        charLexer.Mark();
        charLexer.Inc();
        // Parse Network Access Point ID
        while ((charLexer.Peek()).IsDigit())
        {
            charLexer.Inc();    // move to next char
        }

        // get token
        TPtrC8 parsedNumber = charLexer.MarkedToken();
        TLex8 numLexer(parsedNumber);
        // try to convert token to number
        if (numLexer.Val(id, EDecimal) != KErrNone)
        {
            // Converting fails
            User::Leave(KErrCorrupt);
        }

        // Descriptor MUST contain only indicator and Network Access Point ID
        if (!charLexer.Eos())
        {
            // Descriptor contains more than indicator and Network Access ID
            User::Leave(KErrCorrupt);
        }
        // Success go to end
    }
    return id;
}
