/*
* Copyright (c) 2002-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:  Subconnection class for session of ASYComms
*
*/


// INCLUDE FILES
#include "AccSrvSubAsyComms.h"
#include "AccSrvMainSession.h"
#include "AccClientServerMessages.h"
#include "acc_debug.h"
#include "AccSrvServerModel.h"
#include "AccSrvASYProxyHandler.h"
#include <AccessoryTypes.h>

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES

// CONSTANTS
//Main service implementation uid of BT-ASY stub for MT
const TUid KBTAsyMainServUidStub = {0x10200c7c};
//Main service implementation uid of BT ASY
const TUid KBTAsyMainServUid = {0x1020897c};

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

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

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

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::CAccSrvSubAsyComms
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CAccSrvSubAsyComms::CAccSrvSubAsyComms(
            CASYProxyAPI* aProxy,
            CAccSrvConnectionController* aConnectionController,
            CAccSrvServerModel* aServerModel,
            TUint aASYThreadID,
            TUid aMainServiceUid )
    : CAccSrvSubBase( aProxy, aConnectionController,aServerModel ),
      iOutstandingTrId( 0 ),
      iTimeOutTrId ( 0 ),
      iOutstandingProcessCmdId( ECmdBadFunction ),
      iASYThreadID( aASYThreadID ),
      iMainServiceUid( aMainServiceUid )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CAccSrvSubAsyComms()" );
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CAccSrvSubAsyComms" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CAccSrvSubAsyComms::ConstructL()
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ConstructL()" );

    iInitCommandTimer = CAccSrvProcessCommandTimer::NewL( this, KInitCmdTimeOut );
    iServerModel->AddASYThreadId( iASYThreadID );

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ConstructL - return void" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CAccSrvSubAsyComms* CAccSrvSubAsyComms::NewL(
                    CASYProxyAPI* aProxy,
                    CAccSrvConnectionController* aConnectionController,
                    CAccSrvServerModel* aServerModel,
                    TUint aASYThreadID,
                    TUid aMainServiceUid )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::NewL()" );
    if ( !aProxy )
        {
        User::Leave( KErrArgument );
        }

    CAccSrvSubAsyComms* self = new ( ELeave ) CAccSrvSubAsyComms(
                                                aProxy,
                                                aConnectionController,
                                                aServerModel,
                                                aASYThreadID,
                                                aMainServiceUid );

    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );

    COM_TRACE_1( "[AccFW:AccServer] CAccSrvSubAsyComms::NewL - return 0x%x", self );
    return self;
    }

// Destructor
CAccSrvSubAsyComms::~CAccSrvSubAsyComms()
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::~CAccSrvSubAsyComms()" );

    if ( iInitCommandTimer )
        {
        delete iInitCommandTimer;
        }

    if ( iProcessCommandTimer )
        {
        delete iProcessCommandTimer;
        }
        
    if( iObjectBuf )
        {
        delete iObjectBuf;
        }

    CancelNotifyProcessCommand( iMessage );

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::~CAccSrvSubAsyComms" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::DispatchMessageL
// Handles client request
// -----------------------------------------------------------------------------
//
void CAccSrvSubAsyComms::DispatchMessageL( const RMessage2& aMessage )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::DispatchMessageL()" );

    TInt function = aMessage.Function();

    switch ( function )
        {
        case EAccSrvNotifyProcessCommand:
            {
            NotifyProcessCommand( aMessage );
            break;
            }
        case EAccSrvCancelNotifyProcessCommand:
            {
            CancelNotifyProcessCommand( aMessage );
            break;
            }
        case EAccSrvProcessResponseTBool:
            {
            ProcessResponseL( aMessage, EProcessResponseTBool );
            break;
            }
        case EAccSrvProcessResponseTInt:
            {
            ProcessResponseL( aMessage, EProcessResponseTInt );
            break;
            }
        case EAccSrvProcessResponseTDes:
            {
            ProcessResponseL( aMessage, EProcessResponseTDes );
            break;
            }        
        case EAccSrvProcessResponseObject:
            {
            ProcessResponseL( aMessage, EProcessResponseObject );
            break;
            }
         
        case EAccSrvGetObjectBuffer:
            {
            TPtr8 ptrBuf( iObjectBuf->Ptr( 0 ) );
            aMessage.WriteL( KAccServParamPosFirst, ptrBuf );
            delete iObjectBuf;
            iObjectBuf = NULL;
            break;
            }
        default:
            {
            TRACE_ASSERT_ALWAYS;
            COM_TRACE_1( "[AccFW:AccServer] CAccSrvSubAsyComms::DispatchMessageL - unhandled function %d", function );
            User::Leave( KErrNotSupported );
            break;
            }
        } // switch( function )

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::DispatchMessageL - return void" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::NotifyProcessCommand
// Sets the NotifyProcessCommand to a handling queue
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CAccSrvSubAsyComms::NotifyProcessCommand( const RMessage2& aMessage )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::NotifyProcessCommand()" );

    // Add session to queue.
    // Queue will call CAccSrvSubAsyComms::CompleteProcessCommandL() when client
    // has sent request to ASY.

    // Exception: In initialization case asy-proxy-server makes
    //  the completition instead of client ( Loading complete )

    iConnectionController->NotificationQueue().RegisterControlMessage(
                                                    ENotifyProcessCommand,
                                                    this );

    // Copy client message for completion when client has sent request to ASY.
    // Completion is done in CAccSrvSubAsyComms::CompleteProcessCommandL()

    // Exception: In initialization case asy-proxy-server makes
    //  the completition instead of client ( Loading complete )

    iMessage = aMessage;

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::NotifyProcessCommand - return void" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::CancelNotifyProcessCommand
// -----------------------------------------------------------------------------
//
void CAccSrvSubAsyComms::CancelNotifyProcessCommand( const RMessage2& /*aMessage*/ )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CancelNotifyProcessCommand()" );

    // Is command pending
    if ( !iMessage.IsNull() )
        {
        // Remove NotifyProcessCommand from queue.
        iConnectionController->NotificationQueue().CancelControlMessage(
                                                        ENotifyProcessCommand,
                                                        this );
        iMessage.Complete( KErrCancel );
        }

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CancelNotifyProcessCommand - return void" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::ProcessResponseL
// Handles responses of NotifyProcessCommand
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CAccSrvSubAsyComms::ProcessResponseL( const RMessage2& aMessage,
                                           TProcessResponseType aResponseType,
                                           TBool aTimeOut )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ProcessResponseL()" );

    if ( iProcessCommandTimer )
        {
        iProcessCommandTimer->Cancel();
        delete iProcessCommandTimer;
        iProcessCommandTimer = NULL;
        }

    TMsgID                       messageId( EUnknown );
    TPckgBuf<TInt>               tridPckgBuf;
    TPckgBuf<TInt>               errPckgBuf;    
    TAccSrvValueTypeTBoolPckgBuf valueTBoolPckgBuf;// TBool    
    TPckgBuf<TAccValueTypeTInt>  valueTIntPckgBuf;// TInt    
    TASYBTAccInfo                btInfo;// TDes
    TPckg<TASYBTAccInfo>         valueBTPckg( btInfo );
    TInt asyDataSize ( 0 );
    HBufC8 *asyData = NULL;    
    CBufFlat* objectBuf = NULL;    
    
    //
    // Allocated the buffer which is really needed!
    // 
    if ( EProcessResponseTDes == aResponseType && 
         ECmdGetSupportedBTProfiles != iOutstandingProcessCmdId )
        {
    	asyDataSize = aMessage.GetDesLengthL( KAccServParamPosSecond );
        asyData = HBufC8::NewL( asyDataSize );
        CleanupStack::PushL( asyData );        
        }
	else if ( EProcessResponseObject == aResponseType )
		{
        objectBuf = CBufFlat::NewL( KAccSrvObjectBaseStreamBufGranularity );
	    CleanupStack::PushL( objectBuf );
	    TInt bufLength( aMessage.GetDesLength( KAccServParamPosSecond ) );
	    objectBuf->ResizeL( bufLength );	      
		}
	else
	   {
	   COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ProcessResponseL() - Buffer is not needed!" );
	   }
		    		    
    if ( !aTimeOut )
        {
        aMessage.ReadL( KAccServParamPosFirst, tridPckgBuf );
        if ( iOutstandingTrId != tridPckgBuf() )
            {
            // There are no correcponding SetValueL()/GetValueL() pending
            User::Leave( KErrNotFound );
            }

        switch ( aResponseType )
            {
            case EProcessResponseTBool :
                aMessage.ReadL( KAccServParamPosSecond, valueTBoolPckgBuf );
                aMessage.ReadL( KAccServParamPosThird, errPckgBuf );
                break;
            case EProcessResponseTInt :
                aMessage.ReadL( KAccServParamPosSecond, valueTIntPckgBuf );
                aMessage.ReadL( KAccServParamPosThird, errPckgBuf );
                break;
            case EProcessResponseTDes :
                if( ECmdGetSupportedBTProfiles == iOutstandingProcessCmdId )
                    {
                    aMessage.ReadL( KAccServParamPosSecond, valueBTPckg ); 
                    }
                else
                    {             
                    TPtr8 asyDataPtr = asyData->Des();	        
                    aMessage.ReadL( KAccServParamPosSecond, asyDataPtr );       
                    }
                aMessage.ReadL( KAccServParamPosThird, errPckgBuf );         
                break;
            case EProcessResponseObject :   
                {  
                // In set case do not read object buf ptr.	                      
                if( ECmdGetObjectValue == iOutstandingProcessCmdId ) 	
                   {
                   TPtr8 objectBufPtr( objectBuf->Ptr(0) );			    		
                   aMessage.ReadL( KAccServParamPosSecond, objectBufPtr );
                   aMessage.ReadL( KAccServParamPosThird, errPckgBuf );
                   }
                else
                   {                   	
                   aMessage.ReadL( KAccServParamPosThird, errPckgBuf );
                   }
                }
                break;
                
            default:
                {
                TRACE_ASSERT_ALWAYS;
                User::Leave( KErrGeneral );
                }
                break;
            }
        }
    else // Timed out
        {
        tridPckgBuf = iOutstandingTrId;
        errPckgBuf = KErrTimedOut;
        iTimeOutTrId = iOutstandingTrId;
        }
        
    // This ASY command is handled so no more new requests can not be linked to this one
    // ->Transaction filter can be removed
    iConnectionController->ASYProxyHandler().RemoveTransactionID( iOutstandingTrId );
        
    switch ( iOutstandingProcessCmdId )
        {
        case ECmdSetValueBool:
        case ECmdSetValueTInt:
        case ECmdAccessoryUpdated:
        case ECmdSetObjectValue:
            {
            messageId = ESetValue;
            }
            break;
        case ECmdGetValueBool:
        case ECmdGetValueTInt:
        case ECmdGetValueTDes8:
        case ECmdGetSupportedBTProfiles:
        case ECmdGetObjectValue:
            {
            messageId = EGetValue;
            }
            break;
        case ECmdProcessCommandInit:
            {
            if ( iInitCommandTimer->IsActive() )
                {
                iInitCommandTimer->Cancel();
                CompleteProcessCommandInitL();
                }
            }
            break;
        default:
            {
            TRACE_ASSERT_ALWAYS;
            User::Leave( KErrGeneral );
            }
            break;
        }

    if ( iOutstandingProcessCmdId != ECmdProcessCommandInit )
        {
        switch ( aResponseType )
            {
            case EProcessResponseTBool :
                iConnectionController->NotificationQueue().CompleteValueMessageL(
                                                   messageId,
                                                   valueTBoolPckgBuf(),
                                                   errPckgBuf(),
                                                   iOutstandingTrId );
                break;
            case EProcessResponseTInt :
                iConnectionController->NotificationQueue().CompleteValueMessageL(
                                                   messageId,
                                                   valueTIntPckgBuf(),
                                                   errPckgBuf(),
                                                   iOutstandingTrId );
                break;
            case EProcessResponseTDes :
                if( ECmdGetSupportedBTProfiles == iOutstandingProcessCmdId )
                    {
                    iConnectionController->NotificationQueue().CompleteValueMessageL(
                                                   messageId,
                                                   valueBTPckg,
                                                   errPckgBuf(),
                                                   iOutstandingTrId );
                    }
                else
                    {
                    TPtr8 asyDataPtr = asyData->Des();		
                    iConnectionController->NotificationQueue().CompleteValueMessageL(
                                                   messageId,
                                                   asyDataPtr,
                                                   errPckgBuf(),
                                                   iOutstandingTrId );                    
                    CleanupStack::PopAndDestroy( asyData );                               
                    }
                break;
            case EProcessResponseObject :
                {
                TPtr8 objectBufPtr( objectBuf->Ptr(0) );
                iConnectionController->NotificationQueue().CompleteValueMessageL( 
                                                   messageId,
                                                   iOutstandingTrId,
                                                   &objectBufPtr,
                                                   errPckgBuf() );
                CleanupStack::PopAndDestroy( objectBuf );
                }                                                   
            break;
                
            default:
                {
                TRACE_ASSERT_ALWAYS;
                User::Leave( KErrGeneral );
                }
                break;
            }

        }

    iOutstandingProcessCmdId = ECmdBadFunction;
    iOutstandingTrId = 0;
    
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ProcessResponseL - return void" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::CompleteMessageL
//
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CAccSrvSubAsyComms::CompleteMessageL( TMsgID /*aMsgID*/,
                                            TInt /*aErrorCode*/,
                                            TInt /*aUniqueID*/ )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CompleteMessageL()" );

    TRACE_ASSERT_ALWAYS;

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CompleteMessageL - return False" );

    return EFalse; //message was not consumed
    }
// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::CompleteProcessCommandL
// Completes queued message
// Preconditions:
// 1. ECmdProcessCommandInit command is allways handled
// 2. Request must be meant for this subsession
// -----------------------------------------------------------------------------
//
TBool CAccSrvSubAsyComms::CompleteProcessCommandL(
    TProcessCmdId aCmdn,
    TInt aTrId,
    TASYCommandParamRecord& aASYCommandParamRecord,
    TPtr8* aPtrBuf )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CompleteProcessCommandL()" );

    TBool messageConsumption( EFalse );// Return value tells should this subsession be removed from the queue

    if (  ECmdProcessCommandInit == aCmdn )
        {
        messageConsumption = HandleCommandL( aCmdn, aTrId, aASYCommandParamRecord, aPtrBuf );
        }
    else
        {
        if ( iServerModel->IsMyRequest( 
                iASYThreadID, aASYCommandParamRecord.iGenericID.UniqueID() ) )
            {
            messageConsumption = HandleCommandL( aCmdn, aTrId, aASYCommandParamRecord, aPtrBuf );
            }
        else
            {
            //Not for us
            //Special handling for BT-ASY
            if ( ( KBTAsyMainServUidStub.iUid == iMainServiceUid.iUid ) || 
                 ( KBTAsyMainServUid.iUid == iMainServiceUid.iUid ) )
                {
                if ( aASYCommandParamRecord.iGenericID.PhysicalConnectionCaps( KPCBluetooth ) )//BT
                    {
                    messageConsumption = HandleCommandL( aCmdn, aTrId, aASYCommandParamRecord, aPtrBuf );
                    }
                }
            else
                {
                //This subsession is not serving BT-ASY
                }
            }
        }

    COM_TRACE_1( "[AccFW:AccServer] CAccSrvSubAsyComms::CompleteProcessCommandL - return %d", messageConsumption );
    return messageConsumption;
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::HandleCommandL
//
// (other items were commented in a header).
// -----------------------------------------------------------------------------
TBool CAccSrvSubAsyComms::HandleCommandL( TProcessCmdId aCmdn,
                                          TInt aTrId,
                                          TASYCommandParamRecord& aASYCommandParamRecord,
                                          TPtr8* aPtrBuf )
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::HandleCommandL()" );
    TInt err( KErrNone );

    iOutstandingProcessCmdId = aCmdn;
    iOutstandingTrId = aTrId;

    TProxyCommandPckgBuf params;
    params().iCmdId        = aCmdn;
    params().iTrId         = aTrId;
    params().iTimeOutTrId  = iTimeOutTrId;
    params().iAsyCmdParams = aASYCommandParamRecord;
    iMessage.WriteL( KAccServParamPosFirst, params ); 


    if( ECmdProcessCommandInit !=  aCmdn )
        {    
        err = HandleObjectBufferWriteL( aPtrBuf );
        iProcessCommandTimer = CAccSrvProcessCommandTimer::NewL( this );
        iProcessCommandTimer->StartTimer();
        }
    else
        {
        iInitCommandTimer->StartTimer();
        }

    iMessage.Complete( err );

    iTimeOutTrId = 0;
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::HandleCommandL - return ETrue" );
    return ETrue;
    }
    
// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::HandleObjectBufferWriteL()
// -----------------------------------------------------------------------------
TInt CAccSrvSubAsyComms::HandleObjectBufferWriteL( TPtr8* aPtrBuf )
    {
    COM_TRACE_1( "[AccFW:AccServer] CAccSrvSubAsyComms::HandleObjectBufferWriteL(0x%x)", aPtrBuf );
    TInt err( KErrNone );
    
    if( !aPtrBuf )
        {
        // There are no buffer to handle
        COM_TRACE_1( "[AccFW:AccServer] CAccSrvSubAsyComms::HandleObjectBufferWriteL - no buffer, return %d", err );
        return err;
        }
    
    TInt size=iMessage.GetDesMaxLength( KAccServParamPosSecond );    
    
    if( aPtrBuf->Size() <=  size )
        {
        iMessage.WriteL( KAccServParamPosSecond, *aPtrBuf );
        }
    else
        {
        //Return a new buffer size to client
        err = KErrOverflow;
        CBufFlat*  buf = CBufFlat::NewL( sizeof( KMaxTUint ) ); 
        CleanupStack::PushL( buf );  
        buf->ResizeL( sizeof(KMaxTUint) );
            
        RBufWriteStream wStrm;    
        (void) wStrm.Open( *buf );
        CleanupClosePushL( wStrm );    
        wStrm.WriteInt32L( aPtrBuf->Size() );//new buffer size
        wStrm.CommitL();         
        CleanupStack::PopAndDestroy( &wStrm );
         
        TPtr8 ptr( buf->Ptr( 0 ) );
        iMessage.WriteL( KAccServParamPosSecond, ptr );        
        CleanupStack::PopAndDestroy( buf );           
         
        //Store current buffer for later retrival        
        iObjectBuf = CBufFlat::NewL( KAccSrvObjectBaseStreamBufGranularity );
        iObjectBuf->ResizeL( aPtrBuf->Size() );
        iObjectBuf->Write( 0, *aPtrBuf );     
        
        }
        
        
    COM_TRACE_1( "[AccFW:AccServer] CAccSrvSubAsyComms::HandleObjectBufferWriteL - return %d", err );
    return err;
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::CompleteProcessCommandInitL
//
// (other items were commented in a header).
// -----------------------------------------------------------------------------
void CAccSrvSubAsyComms::CompleteProcessCommandInitL()
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CompleteProcessCommandInitL()" );

    // Note, Must NOT leave before next call
    iServerModel->SetASYsLoadCompleted();

    iConnectionController->InitializeConnectionStatusL();

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::CompleteProcessCommandInitL" );
    }

// -----------------------------------------------------------------------------
// CAccSrvSubAsyComms::ProcessCommandTimeOutCompleteL
//
// (other items were commented in a header).
// -----------------------------------------------------------------------------
void CAccSrvSubAsyComms::ProcessCommandTimeOutCompleteL()
    {
    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ProcessCommandTimeOutCompleteL()" );

    switch( iOutstandingProcessCmdId )
        {
        case ECmdSetValueBool:
        case ECmdGetValueBool:
            ProcessResponseL( iMessage, EProcessResponseTBool, ETrue );
            break;
        case ECmdSetValueTInt:
        case ECmdGetValueTInt:
            ProcessResponseL( iMessage, EProcessResponseTInt, ETrue );
            break;
        case ECmdGetValueTDes8:
        case ECmdGetSupportedBTProfiles:
            ProcessResponseL( iMessage, EProcessResponseTDes, ETrue );
            break;
        case ECmdSetObjectValue:
        case ECmdGetObjectValue:
            ProcessResponseL( iMessage, EProcessResponseObject, ETrue );
            break;    
        default:
            break;
        }

    COM_TRACE_( "[AccFW:AccServer] CAccSrvSubAsyComms::ProcessCommandTimeOutCompleteL" );
    }

// ========================== OTHER EXPORTED FUNCTIONS =========================

// End of File
