/*
* 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:   Capability (answer to OPTIONS)
*
*/



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

#include <e32math.h>
#include <uri8.h>
#include <escapeutils.h>
//#include <sip.h>
#include <sipservertransaction.h>
//#include <sipclienttransaction.h>
#include <sipacceptcontactheader.h>
#include <sipcontactheader.h>
#include <siprequestelements.h>
#include <sdpdocument.h>
#include <sdpmediafield.h>
#include <sdpconnectionfield.h>
#include <sdporiginfield.h>
#include <sdpfmtattributefield.h>
#include <sdpattributefield.h>
#include <sdprtpmapvalue.h>
#include <sipstrings.h>
#include <sdpcodecstringpool.h>
#include <sipstrconsts.h>

_LIT8(KSessionName, "-");
_LIT8(KOriginFieldUser, "-");

const TInt KMaxNumAsStringSize = 16;

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

// --------------------------------------------------------------------------
// C++ destructor
// --------------------------------------------------------------------------
//
CMusAvaCapability::~CMusAvaCapability()
    {
    MUS_LOG( 
        "mus: [MUSAVA]: -> CMusAvaCapability::~CMusAvaCapability" )
    
    iFeature.Close();
    SIPStrings::Close();
    SdpCodecStringPool::Close();
    
    MUS_LOG( 
        "mus: [MUSAVA]: <- CMusAvaCapability::~CMusAvaCapability" )
    }


// --------------------------------------------------------------------------
// C++ constructor
// --------------------------------------------------------------------------
//
CMusAvaCapability::CMusAvaCapability( 
                                    CMusAvaCapabilityExchange& aExchange )
    : iExchange( aExchange ),
      iIsEnabled( ETrue )
    {
    }


// --------------------------------------------------------------------------
// Symbian second-phase constructor
// --------------------------------------------------------------------------
//
void CMusAvaCapability::ConstructL( )
    {
    MUS_LOG( "mus: [MUSAVA]: -> CMusAvaCapability::ConstructL" )
    
    MUS_LOG( "mus: [MUSAVATEST]: SIPStrings::OpenL()" )
    TRAP_IGNORE( SIPStrings::OpenL() )
    TRAP_IGNORE( SdpCodecStringPool::OpenL() ) 
    
    MUS_LOG( "mus: [MUSAVATEST]: MusAvaCapabilityContext::SIPStringL( KCapabilitySwisFeature )" )
    iFeature = MusAvaCapabilityContext::SIPStringL( KCapabilitySwisFeature );
    
    MUS_LOG( "mus: [MUSAVA]: <- CMusAvaCapability::ConstructL" )
    }


// --------------------------------------------------------------------------
// CMusAvaCapability::Exchange
// --------------------------------------------------------------------------
//
CMusAvaCapabilityExchange& CMusAvaCapability::Exchange() const
    {
    return iExchange;
    }


// --------------------------------------------------------------------------
// CMusAvaCapability::Feature
// --------------------------------------------------------------------------
//
const RStringF& CMusAvaCapability::Feature() const
    {
    return iFeature;
    }


// --------------------------------------------------------------------------
// CMusAvaCapability::Supports
// --------------------------------------------------------------------------
//
TBool CMusAvaCapability::Supports( 
                            const CSIPAcceptContactHeader& acceptContact )
    {
    return acceptContact.HasParam( Feature() );
    }
    
    
// --------------------------------------------------------------------------
// CMusAvaCapability::Enabled
// --------------------------------------------------------------------------
//
TBool& CMusAvaCapability::Enabled()
    {
    return iIsEnabled;
    }


// --------------------------------------------------------------------------
// CMusAvaCapability::PopulateResponseL
// --------------------------------------------------------------------------
//
void CMusAvaCapability::PopulateResponseL( 
                            CSIPServerTransaction& aQuery,
                            RPointerArray<CSIPHeaderBase>& aResponseHeaders,
                            CSdpDocument& aResponseContent )

    {
    MUS_LOG( "mus: [MUSAVA]: -> CMusAvaCapability::PopulateResponseL" )

    AddContactHeaderL( aQuery, aResponseHeaders );
    
    DoPopulateResponseL( aResponseContent );
    
    MUS_LOG( "mus: [MUSAVA]: <- CMusAvaCapability::PopulateResponseL" )
    }

// --------------------------------------------------------------------------
// CMusAvaCapability::DoPopulateResponseL
// --------------------------------------------------------------------------
//
void CMusAvaCapability::DoPopulateResponseL( CSdpDocument& aResponseContent )

    {
    MUS_LOG( "mus: [MUSAVA] -> CMusAvaCapability::DoPopulateResponseL" )

    MUS_LOG( "mus: [MUSAVA] Adding application attribute to SDP" )
    //application
    CSdpAttributeField* application = CSdpAttributeField::NewLC(
       MusAvaCapabilityContext::SDPStringL( 
            SdpCodecStringConstants::EMediaApplication ), 
       KCapabilitySwisApplication );
                                      
    aResponseContent.AttributeFields().AppendL( application );
    CleanupStack::Pop( application );

    MUS_LOG( "mus: [MUSAVA] Adding type attribute to SDP" )
    //type
    RStringF typeStr = MusAvaCapabilityContext::SDPStringL( 
                                        KCapabilitySDPAttributeNameType );
    CleanupClosePushL( typeStr );
    CSdpAttributeField* type = 
        CSdpAttributeField::NewL( typeStr, KCapabilitySDPAttributeType );
    CleanupStack::PopAndDestroy();//typeStr 
    CleanupStack::PushL( type );
                                         
    aResponseContent.AttributeFields().AppendL( type );
    CleanupStack::Pop( type );
    
    MUS_LOG( "mus: [MUSAVA] Adding media line to SDP" )
    //media line    
    CSdpMediaField* mediaLine = MediaLineLC( 
                MusAvaCapabilityContext::SDPStringL( 
                    SdpCodecStringConstants::EMediaVideo ), 
                    KCapabilitySwisFormatListH263Only );
    
    //adding H.264 codec to SDP
    if( !MultimediaSharingSettings::IsAvcDisabled())
        {
        MUS_LOG( "mus: [MUSAVA]  - Adding avc to supported codec list" )
        // reconstruct media line with H264 and H263
        CleanupStack::PopAndDestroy(mediaLine);
        mediaLine = MediaLineLC( 
                 MusAvaCapabilityContext::SDPStringL( 
                    SdpCodecStringConstants::EMediaVideo ), 
                    KCapabilitySwisFormatList );
        
        //adding H.264 codec to SDP
        CSdpFmtAttributeField* rtpMapH264 = 
                                RtpMapLineLC( KCapabilitySDPAttributeCodecH264,
                                KCapabilitySDPAttributePayloadTypeH264,
                                KCapabilitySDPAttributeClockrate ); 
    
        mediaLine->FormatAttributeFields().AppendL( rtpMapH264 );
        CleanupStack::Pop( rtpMapH264 );
        }    
    //H.263 codec
    CSdpFmtAttributeField* rtpMapH263 = 
        RtpMapLineLC( KCapabilitySDPAttributeCodecH263,
                    KCapabilitySDPAttributePayloadType,
                    KCapabilitySDPAttributeClockrate ); 

    mediaLine->FormatAttributeFields().AppendL( rtpMapH263 );
    CleanupStack::Pop( rtpMapH263 );

    aResponseContent.MediaFields().AppendL( mediaLine );
    CleanupStack::Pop( mediaLine );

    
    MUS_LOG( "mus: [MUSAVA] <- CMusAvaCapability::DoPopulateResponseL" )
    }


// --------------------------------------------------------------------------
// CMusAvaCapability::ResponseContentLC
// --------------------------------------------------------------------------
//
CSdpDocument* CMusAvaCapability::ResponseContentLC( 
                            const CSIPServerTransaction& aQuery )

    {
    MUS_LOG( 
        "mus: [MUSAVA]: -> CMusAvaCapability::CreateResponseContentL" )
    
	RStringF netType = 
	MusAvaCapabilityContext::SDPStringL( SdpCodecStringConstants::ENetType );
    
    CSdpDocument* responseContent = CSdpDocument::NewLC();
    
    responseContent->SetSessionNameL( KSessionName );

    TPtrC8 localHost;
    RStringF hostType;
    
    MusAvaCapabilityContext::LocalHostL( aQuery, localHost, hostType );
    
    CSdpOriginField* originField = OriginFieldL( 
                                            netType, localHost, hostType );
    responseContent->SetOriginField( originField );
    
    // Connection field
    CSdpConnectionField* connectionField = 
        CSdpConnectionField::NewL( netType, hostType, localHost );
    responseContent->SetConnectionField( connectionField );         
    
    MUS_LOG( 
        "mus: [MUSAVA]: <- CMusAvaCapability::CreateResponseContentL" )
    
    return responseContent;
    }
    

// -----------------------------------------------------------------------------
// CMusAvaCapability::Codec
// -----------------------------------------------------------------------------
// 
CDesCArrayFlat* CMusAvaCapability::ResolveCodecsL( CSdpDocument& aSDP )
    {
    // TODO: use array of 2 elements, as we assume that max 2 codecs are supported (H.263 and H.264)
    CDesCArrayFlat* codecs =  new( ELeave ) CDesCArrayFlat( 2 );
    CleanupStack::PushL( codecs );
    
    RStringF media = MusAvaCapabilityContext::SDPStringL( 
                                        SdpCodecStringConstants::EMediaVideo );
    
	RStringF rtpmap =  MusAvaCapabilityContext::SDPStringL( 
	                            SdpCodecStringConstants::EAttributeRtpmap );
    
    RPointerArray<CSdpMediaField>& mediaFields = aSDP.MediaFields();
    
    for ( TInt i=0; i < mediaFields.Count(); i++ )
        {
        if ( mediaFields[ i ]->Media() == media )
            {
        	RPointerArray<CSdpFmtAttributeField>& fmtLines =  
                                mediaFields[ i ]->FormatAttributeFields();
            
            for ( TInt j=0; j < fmtLines.Count(); j++ )
                {
                if ( fmtLines[ j ]->Attribute() == rtpmap ) 
                    {
                    if ( CAPABILITY_CONTAINS( fmtLines[ j ]->Value(), KCapabilitySDPAttributeCodecH263 )
                            || ( CAPABILITY_CONTAINS( fmtLines[ j ]->Value(), KCapabilitySDPAttributeCodecH264 ) && 
                                 !MultimediaSharingSettings::IsAvcDisabled() ) )
                        {
                        HBufC* codecBuf = 
                            EscapeUtils::ConvertToUnicodeFromUtf8L( fmtLines[ j ]->Value() );
                        CleanupStack::PushL( codecBuf );  
                        codecs->AppendL( *codecBuf );
                        CleanupStack::PopAndDestroy( codecBuf );
                        }
                    }
                }
            }
        }
    CleanupStack::Pop( codecs );
    
    return codecs;
    }
    

// -----------------------------------------------------------------------------
// CMusAvaCapability::Attribute
// -----------------------------------------------------------------------------
// 
const CSdpAttributeField* CMusAvaCapability::Attribute( 
                                                const RStringF& aAttribute,
                                                CSdpDocument& aSDP )
    {
    const CSdpAttributeField* field = NULL;
    
    TBool found = EFalse;
    RPointerArray<CSdpAttributeField>& attributes = aSDP.AttributeFields();
    
    for ( TInt i=0; i < attributes.Count() && !found; i++ )
        {
        if ( attributes[ i ]->Attribute() == aAttribute )
            {
            field = attributes[ i ];
            found = ETrue;
            }
        }
        
    if ( !found )
        {
        TRAP_IGNORE( field = Attribute( MusAvaCapabilityContext::SDPStringL( 
                                    SdpCodecStringConstants::EMediaVideo ),
                                aAttribute,
                                aSDP ) );
        }
    return field;
    }


// -----------------------------------------------------------------------------
// CMusAvaCapability::Attribute
// -----------------------------------------------------------------------------
// 
const CSdpAttributeField* CMusAvaCapability::Attribute( 
                                                const RStringF& aMedia,
                                                const RStringF& aAttribute,
                                                CSdpDocument& aSDP )
    {
    const CSdpAttributeField* field = NULL;
    
    TBool found = EFalse;
    RPointerArray<CSdpMediaField>& mediaFields = aSDP.MediaFields();
    
    for ( TInt i=0; i < mediaFields.Count() && !found; i++ )
        {
        if ( mediaFields[ i ]->Media() == aMedia )
            {
            RPointerArray<CSdpAttributeField>& attributes = 
                                mediaFields[ i ]->AttributeFields();

            for ( TInt j=0; j < attributes.Count() && !found; j++ )
                {
                if ( attributes[ j ]->Attribute() == aAttribute )
                    {
                    field = attributes[ j ];
                    found = ETrue;
                    }
                }
            }
        }
    return field;
    }


// --------------------------------------------------------------------------
// CMusAvaCapability::AddContactHeaderL
// --------------------------------------------------------------------------
//
void CMusAvaCapability::AddContactHeaderL(
                        CSIPServerTransaction& aQuery,
                        RPointerArray<CSIPHeaderBase>& aResponseHeaders )
    {
    MUS_LOG( "mus: [MUSAVA]: -> CMusAvaCapability::AddContactHeaderL" )

    const CUri8& uri = aQuery.RequestElements()->RemoteUri();

    
    TBool cingular = 
                  MultimediaSharingSettings::OperatorVariantSettingL() ==
                  MusSettingsKeys::EOperatorSpecific;
          
    TBool alreadyRequested = iExchange.QueryObserver().
                    CapabilitiesResolvedForCingular() && cingular;

    CSIPContactHeader* contact =  NULL;
    
    if ( alreadyRequested )
        {
        contact = MusAvaCapabilityContext::ContactHeaderLC( uri, 
            SIPStrings::StringF( SipStrConsts::EEmpty ) );
        }
    else
        {
        //Contact header with feature tag
	    contact = MusAvaCapabilityContext::ContactHeaderLC( uri, Feature() );
        }
        
    aResponseHeaders.AppendL( contact );
    CleanupStack::Pop( contact );

    MUS_LOG( "mus: [MUSAVA]: <- CMusAvaCapability::AddContactHeaderL" )
    }
    

// ----------------------------------------------------------------------------
// CMusAvaCapability::OriginFieldL
// ----------------------------------------------------------------------------
//    
CSdpOriginField* CMusAvaCapability::OriginFieldL( 
                                                const RStringF& aNetType,
                                                const TDesC8& aLocalHost,
                                                const RStringF& aHostType )
    {
#ifdef CAPABILITY_UNIT_TESTING    
    TInt64 sessionID = 452027953;
#else    
    TTime now;
    now.UniversalTime();
    TInt64 rand = now.Int64();
    TInt64 sessionID = Math::Rand( rand );
#endif
    
    return CSdpOriginField::NewL( KOriginFieldUser,
                                 sessionID,
                                 sessionID,
                                 aNetType,
                                 aHostType,
                                 aLocalHost );    
    }
  
// -----------------------------------------------------------------------------
// CMusAvaCapability::MediaLineLC
// -----------------------------------------------------------------------------
// 
CSdpMediaField* CMusAvaCapability::MediaLineLC( const RStringF& aMedia,
                                                const TDesC8& aFormatList ) const
    {
	return CSdpMediaField::NewLC( aMedia,
                            NULL,
                            MusAvaCapabilityContext::SDPStringL( 
                                SdpCodecStringConstants::EProtocolRtpAvp ), 
                            aFormatList );
    }

// -----------------------------------------------------------------------------
// CMusAvaCapability::RtpMapLineLC
// ------------------------------------------------ -----------------------------
// 
CSdpFmtAttributeField* CMusAvaCapability::RtpMapLineLC( 
                                                const TDesC8& aCodec,
                                                const TUint64 aPayloadType,
				                                const TUint64 aClockRate,
				                                const TDesC8& aParams )
    {
    
    HBufC8* clockrate  = HBufC8::NewLC( KMaxNumAsStringSize );
    clockrate->Des().Num( aClockRate, EDecimal );
	HBufC8* payload  = HBufC8::NewLC( KMaxNumAsStringSize );
    payload->Des().Num( aPayloadType, EDecimal );
    
    			
	TSdpRtpmapValue rtpmapValue( aCodec, 
								 *clockrate, 
								 aParams );
								 
	
	HBufC8* rtpmap = rtpmapValue.EncodeL();
	CleanupStack::PushL( rtpmap );
	
	CSdpFmtAttributeField* rtpmapAttribute = 
	    CSdpFmtAttributeField::NewL( MusAvaCapabilityContext::SDPStringL( 
	                            SdpCodecStringConstants::EAttributeRtpmap ),
	                        *payload,
	                        *rtpmap );
	    
	CleanupStack::PopAndDestroy( rtpmap ); 
	CleanupStack::PopAndDestroy( payload ); 
	CleanupStack::PopAndDestroy( clockrate ); 
    
    CleanupStack::PushL( rtpmapAttribute );
    
    return rtpmapAttribute;
    }



