/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
*      Implementation of class CConnection.   
*      
*
*/


// INCLUDE FILES

#include "Connection.h"
#include "CodPanic.h"
#include "CodUtil.h"
#include "CodError.h"
#include "CodLogger.h"
#include <CommDbConnPref.h>
#include <cdblen.h>
#include <es_enum.h>

// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------
// CConnection::NewL()
// ---------------------------------------------------------
//
CConnection* CConnection::NewL()
    {
    CConnection* conn = new (ELeave) CConnection();
    CleanupStack::PushL( conn );
    conn->ConstructL();
    CleanupStack::Pop( conn );
    return conn;
    }

// ---------------------------------------------------------
// CConnection::~CConnection()
// ---------------------------------------------------------
//
CConnection::~CConnection()
    {
    Cancel();
    DoClose();
    }

// ---------------------------------------------------------
// CConnection::ConnectL()
// ---------------------------------------------------------
//
void CConnection::ConnectL( TUint32 aIap, TRequestStatus* aStatus )
    {
    CLOG(( EConn, 2, _L("-> CConnection::ConnectL iap(%d)"), aIap ));
    // Misuse asserts.
    __ASSERT_ALWAYS( aStatus, CodPanic( ECodInvalidArguments ) );
    // Internal asserts.
    __ASSERT_DEBUG( !iParentStatus, CodPanic( ECodInternal ) );

    iStatus = KErrCodCannotConnect;
    if ( iState == EConnected )
        {
        // Already connected; do nothing.
        CLOG(( EConn, 4, _L("  already connected") ));
        iStatus = KErrNone;
        }
    else if ( iState == EInit )
        {
        // Not connected. Create connection.
        CLOG(( EConn, 4, _L("  starting connection") ));
        // Make this part atomic by pushing closes on the stack.
        User::LeaveIfError( iSockServ.Connect() );
        CleanupClosePushL<RSocketServ>( iSockServ );
        User::LeaveIfError( iConn.Open( iSockServ ) );
        CleanupClosePushL<RConnection>( iConn );
        iConnPref.SetDirection( ECommDbConnectionDirectionOutgoing );
        if ( aIap )
            {
            // Use specified IAP.
            iConnPref.SetIapId( aIap );
            iConnPref.SetDialogPreference( ECommDbDialogPrefDoNotPrompt );
            }
        else
            {
            // aIap == 0 -> user select.
            iConnPref.SetDialogPreference( ECommDbDialogPrefPrompt );
            }
        iConn.Start( iConnPref, iStatus );
        iState = EConnecting;
        SetActive();
        CleanupStack::Pop( 2 ); // closing iConn and iSockServ
        // End of atomic part.
        }
    else
        {
        // Not expecting this to be called in other states.
        CLOG(( EConn, 0, _L("CConnection::ConnectL: unexpected state (%d)"), \
                                                                    iState ));
        __ASSERT_ALWAYS( EFalse, CodPanic( ECodInternal ) );
        }

    iParentStatus = aStatus;
    *iParentStatus = KRequestPending;

    if ( !IsActive() )
        {
        // Unless we have an outstanding connect request (iConn.Start),
        // we are done.
        Done();
        }

    CLOG(( EConn, 2, _L("<- CConnection::ConnectL") ));
    }

// ---------------------------------------------------------
// CConnection::AttachL()
// ---------------------------------------------------------
//
void CConnection::AttachL( TUint32 aIap )
    {
    CLOG(( EConn, 2, _L("-> CConnection::AttachL iap(%d)"), aIap ));

    iStatus = KErrCodCannotConnect;
    if ( iState == EConnected )
        {
        // Already connected; do nothing.
        CLOG(( EConn, 4, _L("  already connected") ));
        iStatus = KErrNone;
        }
    else if ( iState == EInit )
        {
        // Not connected. Create connection, or attach to existing.
        CLOG(( EConn, 4, _L("  not connected") ));
        if ( aIap )
            {
            // Make this part atomic by pushing closes on the stack.
            User::LeaveIfError( iSockServ.Connect() );
            CleanupClosePushL<RSocketServ>( iSockServ );
            User::LeaveIfError( iConn.Open( iSockServ ) );
            CleanupClosePushL<RConnection>( iConn );
            TConnectionInfoBuf connInfo;
            TUint count;
            User::LeaveIfError( iConn.EnumerateConnections( count ) );
            CLOG(( EConn, 4, _L("  %d existing connections"), count ));
            TUint i;
            for ( i = count; i; i-- )
                {
                // Note: GetConnectionInfo expects 1-based index.
                User::LeaveIfError( iConn.GetConnectionInfo( i, connInfo ) );
                CLOG(( EConn, 4, _L("  conn(%d) iap(%d)"), \
                                                i, connInfo().iIapId ));
                if ( connInfo().iIapId == aIap )
                    {
                    CLOG(( EConn, 4, _L("  attach"), i, count ));
                    User::LeaveIfError( iConn.Attach
                        ( connInfo, RConnection::EAttachTypeNormal ) );
                    iState = EConnected;
                    iStatus = KErrNone;
                    break;
                    }
                }
            CleanupStack::Pop( 2 ); // closing iConn and iSockServ
            // End of atomic part.
            }
        if ( iState == EInit )
            {
            CLOG(( EConn, 4, _L("  ended unconnected") ));
            iConn.Close();
            iSockServ.Close();
            }
        }
    else
        {
        // Not expecting this to be called in other states.
        CLOG(( EConn, 0, _L("CConnection::AttachL: unexpected state (%d)"), \
                                                                    iState ));
        __ASSERT_ALWAYS( EFalse, CodPanic( ECodInternal ) );
        }
    User::LeaveIfError( iStatus.Int() );

    CLOG(( EConn, 2, _L("<- CConnection::AttachL") ));
    }

// ---------------------------------------------------------
// CConnection::Close()
// ---------------------------------------------------------
//
void CConnection::Close()
    {
    CLOG(( EConn, 2, _L("-> CConnection::Close") ));
    Cancel();
    DoClose();
    CLOG(( EConn, 2, _L("<- CConnection::Close") ));
    }

// ---------------------------------------------------------
// CConnection::IsConnected()
// ---------------------------------------------------------
//
TBool CConnection::IsConnected( TUint32& aIap )
    {
    TBool connected( EFalse );
    if( iState == EConnected )
        {
        TBuf<KCommsDbSvrMaxColumnNameLength * 2 + 1> iapId;
        _LIT( KFormatIapId, "%S\\%S" );
        TPtrC iap( IAP );
        TPtrC id( COMMDB_ID );
        iapId.Format( KFormatIapId, &iap, &id );
        TInt err = iConn.GetIntSetting( iapId, aIap );
        connected = err ? EFalse : ETrue;
        }
    return connected;
    }

// ---------------------------------------------------------
// CConnection::CConnection()
// ---------------------------------------------------------
//
CConnection::CConnection()
: CActive( CActive::EPriorityStandard ),
  iState( EInit )
    {
    CActiveScheduler::Add( this );
    }

// ---------------------------------------------------------
// CConnection::ConstructL()
// ---------------------------------------------------------
//
void CConnection::ConstructL()
    {
    if ( !CodUtil::ApCountL() )
        {
        // It's futile to try making a connection without an AP, so leave if
        // we don't have any.
        User::Leave( KErrCodNoAccessPoint );
        }
    }

// ---------------------------------------------------------
// CConnection::DoCancel()
// ---------------------------------------------------------
//
void CConnection::DoCancel()
    {
    CLOG(( EConn, 2, _L("-> CConnection::DoCancel") ));
    __ASSERT_DEBUG( iState == EConnecting, CodPanic( ECodInternal ) );
    // RConnection has no cancel method matching the async RConnection::Start.
    // The only way to cancel is to close the RConnection.
    // (Our request will complete with KErrCancel, as expected.)
    DoClose();
    Done();
    CLOG(( EConn, 2, _L("<- CConnection::DoCancel") ));
    }

// ---------------------------------------------------------
// CConnection::RunL()
// ---------------------------------------------------------
//
void CConnection::RunL()
    {
    CLOG(( EConn, 2, _L("-> CConnection::RunL iStatus(%d)"), \
        iStatus.Int() ));
    __ASSERT_DEBUG( iState == EConnecting, CodPanic( ECodInternal ) );
    User::LeaveIfError( iStatus.Int() );    // Handle errors in RunError().
    iState = EConnected;
    Done();
    CLOG(( EConn, 2, _L("<- CConnection::RunL") ));
    }

// ---------------------------------------------------------
// CConnection::RunError()
// ---------------------------------------------------------
//
TInt CConnection::RunError( TInt LOG_ONLY( aError ) )
    {
    CLOG(( EConn, 2, _L("-> CConnection::RunError aError(%d)"), aError ));
    __ASSERT_DEBUG( iState == EConnecting, CodPanic( ECodInternal ) );
    DoClose();
    Done();
    CLOG(( EConn, 2, _L("<- CConnection::RunError") ));
    return KErrNone;
    }

// ---------------------------------------------------------
// CConnection::DoClose()
// ---------------------------------------------------------
//
void CConnection::DoClose()
    {
    CLOG(( EConn, 2, _L("CConnection::DoClose") ));
    iConn.Close();
    iSockServ.Close();
    iState = EInit;
    }

// ---------------------------------------------------------
// CConnection::Done()
// ---------------------------------------------------------
//
void CConnection::Done()
    {
    CLOG(( EConn, 2, _L("CConnection::Done iStatus(%d)"), iStatus.Int() ));
    __ASSERT_DEBUG( iParentStatus, CodPanic( ECodInternal ) );
    User::RequestComplete( iParentStatus, iStatus.Int() );
    iParentStatus = NULL;
    }
