/*
* Copyright (c) 2005-2007 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:   Discovered terminal
*
*/


#include "musavaterminal.h"

#include <escapeutils.h>
#include <uri8.h>
#include <sip.h>
#include <sipconnection.h>
#include <sipservertransaction.h>
#include <sipclienttransaction.h>
#include <sipheaderbase.h>
#include <sipextensionheader.h>
#include <sdpdocument.h>
#include <sipcontenttypeheader.h>
#include <sipmessageelements.h>
#include <sipresponseelements.h>
#include <siprequestelements.h>
#include <sipstrings.h>
#include <sipstrconsts.h>
#include <sipcontactheader.h>
#include <sipaddress.h>

#include "muslogger.h"
#include "musavacapabilityexchange.h"
#include "musavacapability.h"
#include "musavacapabilityquerybase.h"
#include "musavacapabilitycontext.h"
#include "mmusavacapabilityqueryobserver.h"
#include "mussettings.h"
#include "mussettingskeys.h"
#include "musavasipheaderutil.h"

// --------------------------------------------------------------------------
// Symbian two-phase constructor
// --------------------------------------------------------------------------
//
CMusAvaTerminal* CMusAvaTerminal::NewL( CMusAvaCapabilityExchange& aExchange,
                                    const TDesC8& aTerminalUri,
                                    const TDesC8& aTerminalId )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::NewL" )
    CMusAvaTerminal* self = new (ELeave) CMusAvaTerminal( aExchange );
    CleanupStack::PushL( self );
    self->ConstructL( aTerminalUri, aTerminalId );
    CleanupStack::Pop( self );
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::NewL" )
    return self;
    }


// --------------------------------------------------------------------------
// C++ destructor
// --------------------------------------------------------------------------
//
CMusAvaTerminal::~CMusAvaTerminal()
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::~CMusAvaTerminal" )

    ResetAndDestroyQuery();
    
    delete iUri;
    delete iTerminalId;
    iRequestedQuery = NULL;
    
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::~CMusAvaTerminal" )
    }

// --------------------------------------------------------------------------
// C++ constructor
// --------------------------------------------------------------------------
//
CMusAvaTerminal::CMusAvaTerminal( CMusAvaCapabilityExchange& aExchange )
    : iExchange( aExchange )
    {
    }


// --------------------------------------------------------------------------
// Symbian second-phase constructor
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::ConstructL( const TDesC8& aTerminalUri,
                                const TDesC8& aTerminalId )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::ConstructL" )

    MUS_LOG_TDESC8( "mus: [MUSAVA] - CMusAvaTerminal::Id ",aTerminalId)
    MUS_LOG_TDESC8( "mus: [MUSAVA] - CMusAvaTerminal::MatchL Uri",aTerminalUri )
    iUri = aTerminalUri.AllocL();
    iTerminalId = aTerminalId.AllocL();

    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::ConstructL" )
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::Id
// --------------------------------------------------------------------------
//
const TDesC8& CMusAvaTerminal::Id() const
    {
    return *iTerminalId;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::ToUriL
// --------------------------------------------------------------------------
//
const TDesC8& CMusAvaTerminal::Uri() const
    {
    return *iUri;
    }
    

// --------------------------------------------------------------------------
// CMusAvaTerminal::MacthL
// --------------------------------------------------------------------------
//
TBool CMusAvaTerminal::MatchL( const TDesC8& aTerminalUri, 
                             const TDesC8& aTerminalId )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::MatchL" )
    TBool match = EFalse;
    TBool useDoubleKeyMatch = Id().Length() > 0 && aTerminalId.Length() > 0;
    
    MUS_LOG_TDESC8( "mus: [MUSAVA] - CMusAvaTerminal::MatchL aTerminalId ",aTerminalId )
    MUS_LOG_TDESC8( "mus: [MUSAVA] - CMusAvaTerminal::MatchL aTerminalUri ",aTerminalUri )    
    if ( useDoubleKeyMatch )
        {
        match = Uri().Compare( aTerminalUri ) == 0 && 
                Id().CompareF( aTerminalId ) == 0;
        }
    else //not known yet or no terminal id in request i.e. best guess
        {
        match = Uri().Compare( aTerminalUri ) == 0;
        }
        
    if ( match )
        {
        AttachUriL( aTerminalUri );
        AttachIdL( aTerminalId );
        }

    MUS_LOG1( "mus: [MUSAVA] <- CMusAvaTerminal::MatchL = %d",match )
    return match;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::RequestedQuery
// --------------------------------------------------------------------------
//
const CMusAvaCapability* CMusAvaTerminal::RequestedQuery() const
    {
    return iRequestedQuery;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::ExecuteQueryL
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::ExecuteQueryL( CMusAvaCapabilityQueryBase* aQuery )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::ExecuteQueryL" )
    
    __ASSERT_ALWAYS( aQuery , User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( 
        aQuery->State() == CMusAvaCapabilityQueryBase::ECapabilityQueryCreated,
        User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( !iQuery, User::Leave( KErrAlreadyExists ) );

    ResetAndDestroyQuery();
                  
    aQuery->ExecuteL();
    
    iQuery = aQuery;                           
        
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::ExecuteQueryL" )
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::QueryCompletedL
// --------------------------------------------------------------------------
//
TBool CMusAvaTerminal::QueryCompletedL( const CSIPClientTransaction& aResponse )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::QueryCompletedL" )
    TBool consumed = EFalse;
    
    if ( iQuery && iQuery->State() != 
        CMusAvaCapabilityQueryBase::ECapabilityQueryCompleted )
        {
        iQuery->CompletedL( aResponse );
        consumed = iQuery->State() == 
            CMusAvaCapabilityQueryBase::ECapabilityQueryCompleted;
        
        if ( consumed )
            {
            const CSIPExtensionHeader* userAgent = 
                MusAvaCapabilityContext::UserAgentHeaderL( aResponse );
            if ( userAgent )
                {
                AttachIdL( userAgent->Value() );
                }
            }
        }
        
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::QueryCompletedL" )
    
    return consumed;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::QueryCanceled
// --------------------------------------------------------------------------
//
TBool CMusAvaTerminal::QueryCanceled( 
                                const CSIPClientTransaction& aTransaction )
    {
    TBool consumed = EFalse;
    if ( iQuery && iQuery->State() != 
            CMusAvaCapabilityQueryBase::ECapabilityQueryCompleted )
        {
        iQuery->Canceled( aTransaction );
        consumed = iQuery->State() == 
            CMusAvaCapabilityQueryBase::ECapabilityQueryCompleted;
        }
    return consumed;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::QueryExecuting
// --------------------------------------------------------------------------
//
TBool CMusAvaTerminal::QueryExecuting()
    {
    TBool executing = EFalse;
    if ( iQuery && iQuery->State() == 
            CMusAvaCapabilityQueryBase::ECapabilityQueryExecuting )
        {
        executing = ETrue;
        }
    return executing;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::QueryRequestedL
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::QueryRequestedL( CSIPServerTransaction& aQuery )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::QueryRequestedL" )

    CSIPResponseElements* response = 
          CSIPResponseElements::NewLC( 
                      KMUSAVASIP200, 
                      SIPStrings::StringF( SipStrConsts::EPhraseOk ) );
    
    CSdpDocument* sdpContent = 
        CMusAvaCapability::ResponseContentLC( aQuery );

    RPointerArray<CSIPHeaderBase> headers;
    CSIPHeaderBase::PushLC( &headers );
    
    if ( PopulateResponseL( aQuery, headers, *sdpContent ) )
        {
        RStringF feature = 
                 MusAvaCapabilityContext::SIPStringL( KCapabilitySwisFeature );
        CleanupClosePushL( feature );
        CMusAvaSipheaderUtil::AddAcceptContactHeaderL( headers,
                                                        feature );
        CleanupStack::PopAndDestroy(); //feature
        MUS_LOG( "mus: [MUSAVA]  AddAcceptContactHeaderL -> Success ")            

        MUS_LOG( "mus: [MUSAVA]  AddAcceptEncodingHeaderL -> Success ")                        
   
        MUS_LOG( "mus: [MUSAVA]  AddAcceptLanguageHeaderL -> Success ")                        
        CMusAvaSipheaderUtil::AddAcceptSdpHeaderL(headers); 
        MUS_LOG( "mus: [MUSAVA]  AddAcceptSDPHeaderL -> Success ")                                    
        AddResponseHeadersL( *response, headers );
        CleanupStack::Pop( &headers );        
        AddResponseContentL( *response, *sdpContent );        
        }
    else
        {
        CleanupStack::PopAndDestroy( &headers );
        response->SetStatusCodeL( KMUSAVASIPNotImplemented );
        response->SetReasonPhraseL( 
            SIPStrings::StringF( SipStrConsts::EPhraseNotImplemented ) );
        }
    CleanupStack::PopAndDestroy( sdpContent );

    MUS_LOG( "mus: [MUSAVA] -> Sending Response For Options Request ..." )    
    aQuery.SendResponseL( response );
    CleanupStack::Pop( response );
    
    iExchange.QueryObserver().CapabilityQueryAnswered( ETrue );
    
    // Add sipaddress proposal
    if ( response->StatusCode( ) == KMUSAVASIP200 )
        {
                                             
        const CSIPContactHeader* contact = 
            MusAvaCapabilityContext::ContactHeaderL( aQuery );
                                             
        if ( !contact )
            {
            MUS_LOG( "mus: [MUSAVA] => FAILED due lack of contact header" )
            }
        else
            {
    /*
           	const TDesC8& uriInContact =
    		    contact->SIPAddress()->Uri8().Uri().UriDes();
    		if ( uriInContact.Length() > 0 )
    		    {
    		    HBufC* uri = 
    		        EscapeUtils::ConvertToUnicodeFromUtf8L( uriInContact );
    		    CleanupStack::PushL( uri );   
    		    iExchange.QueryObserver().CapabilitiesResolvedL( *uri );
    		    CleanupStack::PopAndDestroy( uri );
    		    }
    */

            // If there is P-Asserted-Identity header then send the OPTIONS
            // to this. Store it in settings data as To header so then future
            // request can be sent there.
            
            CDesCArrayFlat* pAssertedIdentityHeaders =
                     MusAvaCapabilityContext::PAssertedIdentityHeaderL(  
                                 MusAvaCapabilityContext::MessageElementsL(aQuery));
            CleanupStack::PushL( pAssertedIdentityHeaders );
            __ASSERT_ALWAYS(pAssertedIdentityHeaders,User::Leave(KErrArgument));
            if(pAssertedIdentityHeaders->Count()>0)
                {
                iExchange.QueryObserver().SipHeadersL(*pAssertedIdentityHeaders,
                                                      SipStrConsts::EToHeader);
                }
            else
                {
                // P-Asserted-Identity header not exist. So rely on other contacts
                // read from phone book or Tel URI to send request.
                }
            pAssertedIdentityHeaders->Reset();
            CleanupStack::PopAndDestroy( pAssertedIdentityHeaders ); 

   		    TBool sequential = 
                  MultimediaSharingSettings::OperatorVariantSettingL() ==
                  	MusSettingsKeys::EOperatorSpecific || 
                  MusSettingsKeys::ESequential ==  
     	        	MultimediaSharingSettings::CapabilityQuerySettingL();
         
            TBool alreadyRequested = iExchange.QueryObserver().
                   CapabilitiesResolvedForCingular();

            if ( sequential && !alreadyRequested )
  		        {
   		        iExchange.QueryObserver().SetCapabilitiesResolvedForCingular();
   		        }
            }
        }
    
    
    //if pending query
    if ( iQuery )
        {
        MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::QueryRequestedL Executing Pending Query" )
        iQuery->ExecuteL();
        }

    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::QueryRequestedL" )
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::AttachQuery
// --------------------------------------------------------------------------
//
TInt CMusAvaTerminal::AttachQuery( CMusAvaCapabilityQueryBase& /*aQuery*/ )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::AttachQuery" )
    iQueries++; 
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::AttachQuery" )
    return iQueries;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::DetachQuery
// --------------------------------------------------------------------------
//
TInt CMusAvaTerminal::DetachQuery( CMusAvaCapabilityQueryBase& /*aQuery*/ )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::DetachQuery" )
    iQueries--;
    __ASSERT_ALWAYS( iQueries >= 0, 
                        User::Panic( _L("CMusAvaTerminal"), KErrGeneral ) );
    
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::DetachQuery" )
    
    return iQueries;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::AttachIdL
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::AttachIdL( const TDesC8& aTerminalId )
    {
    if ( aTerminalId.Length() > 0 )
        {
        //terminal id might change                                
        HBufC8* tmpTerminalId = aTerminalId.AllocL();
        delete iTerminalId;
        iTerminalId = tmpTerminalId;
        }
    }
    

// --------------------------------------------------------------------------
// CMusAvaTerminal::AttachUriL
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::AttachUriL( const TDesC8& aUri )
    {
    if ( aUri.Length() > 0 )
        {
        delete iUri;
        iUri = NULL;
        iUri = aUri.AllocL();
        }
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::ResetAndDestroyQuery
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::ResetAndDestroyQuery()
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::ResetAndDestroyQuery" )
    CMusAvaCapabilityQueryBase* query = iQuery;
    iQuery = NULL;
    delete query;
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::ResetAndDestroyQuery" )
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::PopulateResponseL
// --------------------------------------------------------------------------
//
TBool CMusAvaTerminal::PopulateResponseL( CSIPServerTransaction& aQuery,
                            RPointerArray<CSIPHeaderBase>& aResponseHeaders,
                            CSdpDocument& aSdpContent )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::PopulateResponseL" )
    
    TBool supported = EFalse;
    
    const CSIPAcceptContactHeader* featureSet = 
        MusAvaCapabilityContext::AcceptContactHeaderL( aQuery );
    
    User::LeaveIfError( !featureSet ? KErrGeneral : KErrNone );
    
    for( TInt i = 0; i < iExchange.Capabilities().Count();i++ )
        {
        CMusAvaCapability* capability = iExchange.Capabilities()[ i ];
        TBool containsFeature = capability->Supports( *featureSet );
        supported = containsFeature ? ETrue : supported;
                           
        if ( containsFeature )
            {
            const CSIPMessageElements& elements = 
                    MusAvaCapabilityContext::MessageElementsL( aQuery );
            const TDesC8& content = elements.Content();
            CSdpDocument* sdp = NULL;
            
            if ( content.Length() > 0 )
                {
                sdp = CSdpDocument::DecodeLC( content );                
                }
            else
                {
                sdp = CSdpDocument::NewLC();
                }

            //store video codecs that came from the other terminal
            CDesCArrayFlat* videoCodecs = CMusAvaCapability::ResolveCodecsL( *sdp );
            CleanupStack::PushL( videoCodecs );
            capability->Exchange().QueryObserver().VideoCodecsResolvedL( *videoCodecs );
            CleanupStack::PopAndDestroy( videoCodecs );                    
            
            //store fast startup mode if present
            MusSettingsKeys::TFastMode mode = 
                CMusAvaCapability::ResolveFastModeL( *sdp );
            capability->Exchange().QueryObserver().FastModeResolved( mode );
            
            CleanupStack::PopAndDestroy( sdp );
            
            capability->PopulateResponseL( aQuery,
                                           aResponseHeaders,
                                           aSdpContent );
            iRequestedQuery = capability;
            }
        }
        
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::PopulateResponseL" )
        
    return supported;
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::AddResponseHeadersL
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::AddResponseHeadersL( CSIPResponseElements& aResponse,
                            RPointerArray<CSIPHeaderBase>& aResponseHeaders )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::AddResponseHeadersL" )

    CSIPMessageElements& message = aResponse.MessageElements();

    if ( iExchange.TerminalId().Length() > 0 )
        {
        //terminal id
    	CSIPHeaderBase* userAgent = 
    	    MusAvaCapabilityContext::UserAgentHeaderLC( 
    	        iExchange.TerminalId() );
        aResponseHeaders.AppendL( userAgent );
        CleanupStack::Pop( userAgent );
        }
        
    //Set user headers
    message.SetUserHeadersL( aResponseHeaders );
    
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::AddResponseHeadersL" )
    }


// --------------------------------------------------------------------------
// CMusAvaTerminal::AddResponseContentL
// --------------------------------------------------------------------------
//
void CMusAvaTerminal::AddResponseContentL( CSIPResponseElements& aResponse,
                                        CSdpDocument& aSdpContent )
    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaTerminal::AddResponseContentL" )

    CSIPMessageElements& message = aResponse.MessageElements();

    //create content from SDP document
    HBufC8* content = MusAvaCapabilityContext::ToTextL( aSdpContent );
    CleanupStack::PushL( content );
    
    //create content type
    CSIPContentTypeHeader* contentTypeHeader = NULL;
    contentTypeHeader =
        CSIPContentTypeHeader::NewLC( KMUSAVASIPMediaTypeApplication,
                                      KMUSAVASIPMediaSubTypeSDP );
    
    //set content
    message.SetContentL( content, contentTypeHeader );
    CleanupStack::Pop( contentTypeHeader );
    CleanupStack::Pop( content );

    MUS_LOG( "mus: [MUSAVA] <- CMusAvaTerminal::AddResponseContentL" )
    }
// --------------------------------------------------------------------------
// CMusAvaTerminal::IsDiscovered
// --------------------------------------------------------------------------
//
TBool CMusAvaTerminal::IsDiscovered()
    {
    TBool isDiscovered = EFalse;
    TBool isGarbage = !iQueries && !RequestedQuery();
    
    if ( !isGarbage )
        {
        isDiscovered = 
            iQueries == 1 &&
            iQuery && 
            iQuery->State() == CMusAvaCapabilityQueryBase::ECapabilityQueryCompleted;
        }
    else
        {
        isDiscovered = ETrue;
        }
        
    return isDiscovered;
    
    }
