/*
* Copyright (c) 2006 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:  Client-side implementation of BTEng
*
*/



#include <e32base.h>
#include <btmanclient.h>

#include "btengclient.h"
#include "debug.h"

/**  Number of retries for connecting to BTEng server side */
const TInt KBTEngConnectAttempts = 3;


// ======== LOCAL FUNCTIONS ========

// ---------------------------------------------------------------------------
// ?description
// ---------------------------------------------------------------------------
//
TInt ClientStart()
    {
    TRACE_FUNC_ENTRY
    const TUidType serverUid( KNullUid, KNullUid, KBTEngServerUid3 );
        // Create a new server process. Simultaneous launching of two processes 
        // should be detected when the second one attempts to create the server 
        // object, failing with KErrAlreadyExists.
    RProcess server;
    TInt err = server.Create( KBTEngServerName, KNullDesC, serverUid );
    if( err != KErrNone )
        {
        TRACE_INFO( ( _L( "[BTEng]\t Server process creation failed %d" ), err ) );
        return err;
        }
    TRequestStatus status;
    server.Rendezvous( status );
    if( status != KRequestPending )
        {
        TRACE_INFO( ( _L( "[BTEng]\t wrong status (%d), kill process"), 
                            status.Int() ) )
        server.Kill( KErrCancel );  // Abort startup
        }
    else
        {
        TRACE_INFO( ( _L( "[BTEng]\t Server process launched") ) )
        server.Resume();
        }
    User::WaitForRequest( status );     // Wait for start or death
    err = status.Int();
    if( server.ExitType() == EExitPanic )
        {
        // The server actually panicked; inform the client.
        err = KErrDied;
        }
    server.Close();
    TRACE_FUNC_RES( ( _L( "result %d" ), err ) )
    return err;
    }


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

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
RBTEng::RBTEng()
:   RSessionBase()
    {
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::Connect()
    {
    TRACE_FUNC_ENTRY
    TInt err = KErrNone;
    for( TInt i = 0; i < KBTEngConnectAttempts; i++ )
        {
        err = CreateSession( KBTEngServerName, Version() );
        if( err == KErrNone )
            {
            break;  // Success
            }
        else if( err == KErrNotFound || err == KErrServerTerminated )
            {
                // Session could not be created, 
                // first the server needs to be started.
            err = ClientStart();
            }
        if( err != KErrNone && err != KErrAlreadyExists )
            {
                // If the server cannot be started for other reasons than 
                // KErrAlreadyExists, give up...
            break;
            }
        }
    TRACE_FUNC_RES( ( _L("result %d"), err ) )
    return err;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TVersion RBTEng::Version()
    {
    return TVersion( KBTEngServerVersionMajor, KBTEngServerVersionMinor, 
                      KBTEngServerVersionBuild );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::SetPowerState( const TBTPowerStateValue aState, const TBool aTemp )
    {
    return SendReceive( EBTEngSetPowerState, TIpcArgs( aState, aTemp ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::SetVisibilityMode( const TBTVisibilityMode aMode, const TInt aTime )
    {
    return SendReceive( EBTEngSetVisibilityMode, TIpcArgs( aMode, aTime ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::ConnectDevice( const TBTDevAddr& aAddr, 
    const TBTDeviceClass& aDeviceClass  )
    {
    TRACE_BDADDR(aAddr);
    TBTDevAddrPckgBuf addrPkg( aAddr );
    TBTEngDevClassPkg codPkg( aDeviceClass.DeviceClass() );
    return SendReceive( EBTEngConnectDevice, TIpcArgs( &addrPkg, &codPkg ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::CancelConnectDevice( const TBTDevAddr& aAddr )
    {
    TRACE_BDADDR(aAddr);
    TBTDevAddrPckgBuf addrPkg( aAddr );
    return SendReceive( EBTEngCancelConnectDevice, TIpcArgs( &addrPkg ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::DisconnectDevice( const TBTDevAddr& aAddr, 
    TBTDisconnectType aDiscType )
    {
    TRACE_BDADDR(aAddr);
    TBTDevAddrPckgBuf addrPkg( aAddr );
    TBTEngParamPkg discPkg( aDiscType );
    return SendReceive( EBTEngDisconnectDevice, TIpcArgs( &addrPkg, &discPkg ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::IsDeviceConnected( const TBTDevAddr& aAddr, 
    TBTEngConnectionStatus& aConnected )
    {
    TRACE_BDADDR(aAddr);
    TBTDevAddrPckgBuf addrPkg( aAddr );
    TBTEngParamPkg connPkg( aConnected );
    TInt err = SendReceive( EBTEngIsDeviceConnected, TIpcArgs( &addrPkg, &connPkg ) );
    aConnected = (TBTEngConnectionStatus) connPkg();
    return err;
    }

// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::IsDeviceConnectable( const TBTDevAddr& aAddr,
    const TBTDeviceClass& aDeviceClass, TBool& aConnectable )
    {
    TRACE_BDADDR(aAddr);
    TBTDevAddrPckgBuf addrPkg( aAddr );
    TBTEngDevClassPkg codPkg( aDeviceClass.DeviceClass() );
    TBTEngParamPkg connPkg( aConnectable );
    TInt err = SendReceive( EBTEngIsDeviceConnectable, TIpcArgs( &addrPkg, &codPkg, &connPkg ) );
    aConnectable = connPkg();
    return err;
    }

// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::GetConnectedAddresses( TDes8& aArrayPkg, TBTEngParamPkg& aProfilePkg )
    {
    return SendReceive( EBTEngGetConnectedAddresses, 
                         TIpcArgs( &aArrayPkg, &aProfilePkg ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::NotifyConnectionEvents( TDes8& aEvent, TRequestStatus& aStatus )
    {
        // Use the third slot for the event package.
    SendReceive( EBTEngNotifyConnectionEvents, 
                  TIpcArgs( NULL, NULL, &aEvent ), aStatus );
    return KErrNone;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::CancelNotifyConnectionEvents()
    {
    return SendReceive( EBTEngCancelEventNotifier, TIpcArgs() );
    }


// ---------------------------------------------------------------------------
// Set a pairing observer in BTEngine.
// ---------------------------------------------------------------------------
//
TInt RBTEng::SetPairingObserver( const TBTDevAddr& aAddr, TBool aActivate )
    {
    TBTDevAddrPckgBuf addrPkg( aAddr );
    return SendReceive( EBTEngSetPairingObserver, TIpcArgs( &addrPkg, aActivate ) );
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt RBTEng::PrepareDiscovery()
    {
    return SendReceive( EBTEngPrepareDiscovery, TIpcArgs() );
    }

// ---------------------------------------------------------------------------
// Start to pair a device in BTEngine.
// ---------------------------------------------------------------------------
//
void RBTEng::PairDevice( const TBTDevAddrPckgBuf& aAddr, 
        const TUint32& aDeviceClass, TRequestStatus& aStatus  )
    {
    SendReceive( EBTEngPairDevice, TIpcArgs( &aAddr, aDeviceClass ), aStatus );    
    }

// ---------------------------------------------------------------------------
// cancel pairing request
// ---------------------------------------------------------------------------
//
void RBTEng::CancelPairDevice( )
    {
    (void) SendReceive( EBTEngCancelPairDevice);        
    }
