/*
* 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 "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:  SyncML Obex client
*
*/


#include "NSmlObexClient.h"
#include "NSmlObexServiceSearcher.h"
#include "ObexSearcherBase.h"
#include "Obexsearcherfactory.h"
#include "ExtBTSearcherObserver.h"
#include <nsmlconstants.h>
#include <nsmldebug.h>

#include "nsmlerror.h"
#include "wbxml2xmlconverter.h"

//============================================================
// CNsmlObexClient definition
//============================================================


//------------------------------------------------------------
// CNsmlObexClient::NewL()
//------------------------------------------------------------
CNsmlObexClient* CNsmlObexClient::NewL()
    {
	CNsmlObexClient* self = new (ELeave) CNsmlObexClient();
	CleanupStack::PushL( self );
    self->ConstructL( ObexName );
	CleanupStack::Pop(); //self
    return self;
    }

//------------------------------------------------------------
// CNsmlObexClient::CNsmlObexClient()
//------------------------------------------------------------
CNsmlObexClient::CNsmlObexClient(): CActive(CActive::EPriorityStandard),
  iState(EDisconnected), iDataPtr( 0, 0 )
    {
    CActiveScheduler::Add( this );
    }

//------------------------------------------------------------
// CNsmlObexClient* CreateCNsmlObexClientL()
//------------------------------------------------------------
EXPORT_C CNsmlObexClient* CreateCNsmlObexClientL()
	{
	return CNsmlObexClient::NewL();
	}

//------------------------------------------------------------
// CNsmlObexClient::ConstructL( const TDesC8& /*aName*/ )
//------------------------------------------------------------
void CNsmlObexClient::ConstructL( const TDesC8& /*aName*/ )
    {
	iDataBuf = CBufFlat::NewL( KNSmlObexClientGranularity );
    iCurrObject = CObexBufObject::NewL( iDataBuf );
    }

//------------------------------------------------------------
// CNsmlObexClient::~CNsmlObexClient()
//------------------------------------------------------------
CNsmlObexClient::~CNsmlObexClient()
    {
	if( iClient )
		{
		iClient->Disconnect(iStatus);	
	 	User::After( 500000 );
		delete iClient;
		User::WaitForRequest( iStatus );
		}
 
	Cancel();
	delete iDataBuf;
	delete iMimeType;
    delete iCurrObject;
	delete iObexSearcher;
    }

//------------------------------------------------------------
// CNsmlObexClient::DoCancel()
//------------------------------------------------------------
void CNsmlObexClient::DoCancel()
    {
	TRequestStatus* status = iAgentStatus; 

	delete iClient;
	iClient = NULL;

	if( *status == KRequestPending )
		{
		User::RequestComplete( status, KErrCancel );
		}
    }

//------------------------------------------------------------
// CNsmlObexClient::RunL()
//------------------------------------------------------------
void CNsmlObexClient::RunL()
    {
    TInt error( iStatus.Int() );    
	TRequestStatus* status = iAgentStatus;
    
    DBG_FILE_CODE( iStatus.Int(), _S8("CNsmlObexClient::RunL \
	                Before error conversion : ") );
    ErrorConversion( iStatus.Int(), error );
            
    if ( iStatus != KErrNone )
        {
		if ( iState == EGettingConnection && 
			 iStatus == KErrIrObexClientPeerDoesNotHaveObex )
			{
			iState = EConnectionFailed;
			ConnectToServerL();
			}
		else if ( iState == EDisconnecting && iStatus == KErrDisconnected )
			{
			iState = EDisconnected;
			User::RequestComplete( status, error );
			}
		else
			{
			iState = EDisconnected;
			User::RequestComplete( status, KErrCancel );
			}
		}
    else 
        {
        switch ( iState )
            {
            case EGettingConnection:
			case EConnectionFailed:
                iState = EWaitingToSend;
				User::RequestComplete( status, error );			
                break;

            case EWaitingToSend:
				iState = EWaitingToReceive;
				User::RequestComplete( status, error );
                break;

           case EWaitingToReceive:
				iDataPtr = this->iDataBuf->Ptr( 0 );				
				iState = EWaitingToSend;
				User::RequestComplete( status, error );
DBG_DUMP((void*)iDataPtr.Ptr(), iDataPtr.Length(), _S8("ReceiveDataL (WBXML)"));
#ifdef __NSML_DEBUG__
_DBG_FILE("CNsmlObexClient::RunL: CWbxml2XmlConverter::ConvertL() begin");
CWbxml2XmlConverter* c;
c = CWbxml2XmlConverter::NewLC();
c->ConvertL(iDataPtr.Ptr(), iDataPtr.Length());
DBG_DUMP((void*)c->Document().Ptr(), c->Document().Length(), _S8("ReceiveDataL (XML)") );
CleanupStack::PopAndDestroy(); // c
_DBG_FILE("CNsmlObexClient::RunL: CWbxml2XmlConverter::ConvertL() end");
#endif // __NSML_DEBUG__
                break;

		   case EDisconnecting:
                iState = EDisconnected;
				User::RequestComplete( iAgentStatus, KErrNone );
                break;

            default:
                Panic( EBTObjectExchangeSdpRecordDelete );
                break;
            };
        }
    }

//------------------------------------------------------------
// CNsmlObexClient::ConnectL( TNSmlObexTransport aTransport, TBool /*aServerAlerted*/, TDesC8& aMimeType, TRequestStatus &aStatus )
//------------------------------------------------------------
void CNsmlObexClient::ConnectL( 
								TNSmlObexTransport aTransport,
								TBool /*aServerAlerted*/,
								TDesC8& aMimeType,
								TRequestStatus &aStatus )
    {    
	iAgentStatus = &aStatus;
	// agent
	*iAgentStatus = KRequestPending;

	delete iMimeType;
	iMimeType = NULL;
	iMimeType = HBufC8::NewL( aMimeType.Length() );
	TPtr8 iMIMEptr( iMimeType->Des() );
	iMIMEptr.Copy( aMimeType );

    if ( iState == EDisconnected && !IsActive() )
        {

		//Delete old and create new device/service searcher
		delete iObexSearcher;
		iObexSearcher = NULL;

		//Handle bluetooth as a special case:
		if ( aTransport == EObexBt )
			{
			iObexSearcher = CObexSearcherFactory::CreateBTSearcherL( iBTConnInfo );
			iObexSearcher->SetObserver( this );
			iObexSearcher->SetExtObserver( iExtObserver );

			//Search device first if the device address is undefined
			if ( iBTConnInfo.iDevAddr == TBTDevAddr() )
				{
				iObexSearcher->SearchDeviceL();
				}
			else
				{
				//else skip device search and jump straight to the service search
				iObexSearcher->SearchServiceL();
				}
			}
		else
			{
			//Other cases, only IrDA so far
			iObexSearcher = CObexSearcherFactory::CreateObexSearcherL( aTransport );
			iObexSearcher->SetObserver( this );
			iObexSearcher->SetExtObserver( iExtObserver );
			iObexSearcher->SearchDeviceL();
			}

        }
    else
        {
        //debug  here
        User::Leave( KErrInUse );
        }    
    }

//------------------------------------------------------------
// CNsmlObexClient::ConnectToServerL()
//------------------------------------------------------------
void CNsmlObexClient::ConnectToServerL()
    {    
    if (iClient)
        {
        delete iClient;
        iClient = NULL;
        }

	//Let the obex searcher create the obex client
	iClient = iObexSearcher->CreateObexClientL();
    
	iCurrObject->SetTargetL( KClientTargetHeader ); 
    iClient->Connect( *iCurrObject, iStatus );    
    SetActive();
    }

//------------------------------------------------------------
// CNsmlObexClient::SendDataL( TDesC8& aStartPtr, TBool /*aFinalPacket*/, TRequestStatus &aStatus )
//------------------------------------------------------------
void CNsmlObexClient::SendDataL( TDesC8& aStartPtr, TBool /*aFinalPacket*/, TRequestStatus &aStatus )
    {
	iAgentStatus = &aStatus;
	// agent
	*iAgentStatus = KRequestPending;

	if ( iState == EWaitingToReceive )
		{
		iClient->Abort();
		iState = EWaitingToSend;
		}
    if ( iState != EWaitingToSend )
        {
        User::Leave( KErrDisconnected );
        }
    else if ( IsActive() ) 
        {
        User::Leave( KErrInUse );
        }

    iCurrObject->Reset();
	
	DBG_DUMP((void*)aStartPtr.Ptr(), aStartPtr.Length(), 
	        _S8("SendDataL (WBXML)") );
#ifdef __NSML_DEBUG__
	_DBG_FILE("CNsmlObexClient::SendDataL: CWbxml2XmlConverter::ConvertL() begin");
	CWbxml2XmlConverter* c = CWbxml2XmlConverter::NewLC();
	c->ConvertL(aStartPtr.Ptr(), aStartPtr.Length());
	DBG_DUMP((void*)c->Document().Ptr(), c->Document().Length(), 
	        _S8("SendDataL (XML)") );
	CleanupStack::PopAndDestroy(); // c
	_DBG_FILE("CNsmlObexClient::SendDataL: CWbxml2XmlConverter::ConvertL() end");
#endif // __NSML_DEBUG__
		        
	iDocumentLength = aStartPtr.Length();

	iDataBuf->Reset();
	iDataBuf->InsertL( 0, aStartPtr );
    TRAPD( err, iCurrObject->SetDataBufL( iDataBuf ) );

    if( KErrNone == err )
		{
        TBuf8<KNameLen> str;
		str.Copy( this->iMimeType->Des() );
        iCurrObject->SetTypeL( str );

        iClient->Put( *iCurrObject, iStatus );
		SetActive ();
		}
    }
//------------------------------------------------------------
// CNsmlObexClient::ReceiveDataL( TDes8& aStartPtr, TRequestStatus &aStatus )
//------------------------------------------------------------
void CNsmlObexClient::ReceiveDataL( TPtr8& aStartPtr, TRequestStatus &aStatus )
    {
	iAgentStatus = &aStatus;
	// agent
	*iAgentStatus = KRequestPending;
	
	iDataPtr.Set( aStartPtr );

    if ( iState != EWaitingToReceive )
        {
        User::Leave( KErrDisconnected );
        }
    else if ( IsActive() ) 
        {
        User::Leave( KErrInUse );
        }
	iDataBuf->Reset();
	iCurrObject->Reset();

	TBuf8<KNameLen> str;
	str.Copy( this->iMimeType->Des() );
	iCurrObject->SetTypeL( str );

	iClient->Get( *iCurrObject, iStatus );
	
	DBG_DUMP((void*)aStartPtr.Ptr(), aStartPtr.Length(), 
	_S8("ReceiveDataL (WBXML)"));
#ifdef __NSML_DEBUG__
	_DBG_FILE("CNsmlObexClient::ReceiveDataL: CWbxml2XmlConverter::ConvertL()\
	 begin");
	CWbxml2XmlConverter* c = CWbxml2XmlConverter::NewLC();
	c->ConvertL(aStartPtr.Ptr(), aStartPtr.Length());
	DBG_DUMP((void*)c->Document().Ptr(), c->Document().Length(), 
	_S8("ReceiveDataL (XML)") );
	CleanupStack::PopAndDestroy(); // c
	_DBG_FILE("CNsmlObexClient::ReceiveDataL: CWbxml2XmlConverter::ConvertL() end");
#endif // __NSML_DEBUG__

	SetActive ();
    }
//------------------------------------------------------------
// CNsmlObexClient::CloseCommunicationL( TRequestStatus &aStatus )
//------------------------------------------------------------
void CNsmlObexClient::CloseCommunicationL( TRequestStatus &aStatus )
	{
	iAgentStatus = &aStatus;

	DisconnectL();

	if( *iAgentStatus == KRequestPending )
		{
		User::RequestComplete( iAgentStatus, KErrCancel );
		}
	}
//------------------------------------------------------------
// CNsmlObexClient::StopL()
//------------------------------------------------------------
void CNsmlObexClient::StopL()
   {
	if ( iClient )
		{
		delete iClient;
		iClient = NULL;
		}
    }
//------------------------------------------------------------
// CNsmlObexClient::DisconnectL()
//------------------------------------------------------------
void CNsmlObexClient::DisconnectL()
    {
	if ( iObexSearcher )
		{
		iObexSearcher->Cancel();
		}
	
	StopL();
	iState = EDisconnected;
	}

//------------------------------------------------------------
// CNsmlObexClient::IsBusy()
//------------------------------------------------------------
TBool CNsmlObexClient::IsBusy()
    {
    return IsActive();
    }

//------------------------------------------------------------
// CNsmlObexClient::IsConnected()
//------------------------------------------------------------
TBool CNsmlObexClient::IsConnected()
    {
    return iState != EDisconnected;
    }

//----------------------------------------------------------------------------
// CNsmlObexClient::HandleDeviceFoundL()
//----------------------------------------------------------------------------
//
void CNsmlObexClient::HandleDeviceFoundL()
	{
	TRAPD( err,	iObexSearcher->SearchServiceL(); )
	
	DBG_FILE_CODE( err, _S8("CNsmlObexClient::HandleDeviceFoundL \
	                Before error conversion : ") );
	ErrorConversion( err, err );
	
	if ( err != KErrNone )
		{
		TRequestStatus* status = iAgentStatus;
		User::RequestComplete( status, err );
		}
	}

//----------------------------------------------------------------------------
// CNsmlObexClient::HandleDeviceErrorL( TInt aErr )
//----------------------------------------------------------------------------
//
void CNsmlObexClient::HandleDeviceErrorL( TInt aErr )
	{		
	DBG_FILE_CODE( aErr, _S8("CNsmlObexClient::HandleDeviceErrorL \
	                Before error conversion : ") );
	ErrorConversion( aErr, aErr );
	
	TRequestStatus* status = iAgentStatus;
	User::RequestComplete( status, aErr );
	}

//----------------------------------------------------------------------------
// CNsmlObexClient::HandleServiceFoundL()
//----------------------------------------------------------------------------
//
void CNsmlObexClient::HandleServiceFoundL()
	{
	TRAPD( err,
		iState = EGettingConnection;
		ConnectToServerL();
		)
	
	DBG_FILE_CODE( err, _S8("CNsmlObexClient::HandleServiceFoundL \
	                Before error conversion : ") );
	ErrorConversion( err, err );
	
	if ( err != KErrNone )
		{
		TRequestStatus* status = iAgentStatus;
		User::RequestComplete( status, err );
		}
	}

//----------------------------------------------------------------------------
// CNsmlObexClient::HandleServiceErrorL( TInt aErr )
//----------------------------------------------------------------------------
//
void CNsmlObexClient::HandleServiceErrorL( TInt aErr )
	{
	DBG_FILE_CODE( aErr, _S8("CNsmlObexClient::HandleServiceErrorL \
	                Before error conversion : ") );		
	ErrorConversion( aErr, aErr );
	
	TRequestStatus* status = iAgentStatus;
	User::RequestComplete( status, aErr );
	}

//----------------------------------------------------------------------------
// CNsmlObexClient::SetBTConnInfo( const TBTDevAddr aBTDevAddr, const TUUID aUid )
//----------------------------------------------------------------------------
//
void CNsmlObexClient::SetBTConnInfo( const TBTDevAddr aBTDevAddr, const TUUID aUid )
	{
	TBTConnInfo info;
	info.iDevAddr = aBTDevAddr;
	info.iServiceClass = aUid;

	iBTConnInfo = info;
	}

//----------------------------------------------------------------------------
// CNsmlObexClient::SetExtObserver( MExtBTSearcherObserver* aExtObserver )
//----------------------------------------------------------------------------
//
void CNsmlObexClient::SetExtObserver( MExtBTSearcherObserver* aExtObserver )
	{
	iExtObserver = aExtObserver;
	}

//----------------------------------------------------------------------------
// CNsmlObexClient::ErrorConversion( const TInt aError, TInt& aErrCode )
//----------------------------------------------------------------------------
//
void CNsmlObexClient::ErrorConversion( const TInt aError, TInt& aErrCode )
    {
    if ( aError > -6000 && aError <= -1 )
			{
    	aErrCode = KErrCancel;     
        return;        
    	}
    else if ( aError > -7000 && aError <= -6000 )
			{
      // Conversion of EPageTimedOut and ERemoteHostTimeout errors
      aErrCode = TNSmlError::ESmlCommunicationError;     
      return;
      }
        
    aErrCode = aError;
    }
    
//End of File

