// Copyright (c) 2003-2009 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:
// Implements the avdtp configurators
// 
//

/**
 @file
 @internalComponent
*/

#include <bluetooth/logger.h>
#include "avdtpConfigurators.h"
#include "avdtpSignallingSession.h"
#include "avdtpSignallingChannel.h"
#include "avdtp.h"
#include "avdtputil.h"

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_AVDTP);
#endif

CSEPConfigurator::CSEPConfigurator(CSignallingSession& aSignallingSession,
						 		   CAvdtpProtocol& aProtocol)
	: iSignallingSession(aSignallingSession), iProtocol(aProtocol)
	{
	LOG_FUNC
	}


CSEPConfigurator::~CSEPConfigurator()
	{
	LOG_FUNC
	}

CRemoteSEPConfigurator* CRemoteSEPConfigurator::NewL(CSignallingSession& aSignallingSession,
													 CAvdtpProtocol& aProtocol,
													 TSEID aLocalSEID,
													 TSEID aRemoteSEID,
													 TBool aIsReconfigure)
	{
	LOG_STATIC_FUNC
	CRemoteSEPConfigurator* s = new (ELeave) CRemoteSEPConfigurator(aSignallingSession,
																	aProtocol, aIsReconfigure);
	CleanupStack::PushL(s);
	s->ConstructL(aLocalSEID, aRemoteSEID);
	CleanupStack::Pop(s);
	return s;	
	}
	
	
void CRemoteSEPConfigurator::ConstructL(TSEID aLocalSEID, TSEID aRemoteSEID)
	{
	LOG_FUNC
	if (aLocalSEID.IsLocal() && !aRemoteSEID.IsLocal() && iProtocol.RemoteSEPCache().Exists(SignallingSession().RemoteAddress(), aRemoteSEID))
		{
		// check in cache...(i.e capabilities have been retrieved)
		// create the store for the configuration
		iSetConfigBuffer.CreateL(KAvdtpMinSetConfigLength);
		iLocalSEP = &iSignallingSession.FindLocalSEPL(aLocalSEID);
		iRemoteSEID = aRemoteSEID;	
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

CRemoteSEPConfigurator::CRemoteSEPConfigurator(CSignallingSession& aSignallingSession,
						 		  			   CAvdtpProtocol& aProtocol,
												TBool aIsReconfigure)
	: CSEPConfigurator(aSignallingSession, aProtocol), iIsReconfigure(aIsReconfigure)
	{
	LOG_FUNC
	}

CRemoteSEPConfigurator::~CRemoteSEPConfigurator()
	{
	LOG_FUNC
	iSetConfigBuffer.Close();
	}


TInt CRemoteSEPConfigurator::AddCapability(const TDesC8& aCapability)
/*
	Add in caps to SetConf - one per SetOpt
	When Ioctl comes in we have a complete packet to send to sigch
*/
	{
	LOG_FUNC
	TInt ret = KErrNone;
	
	// this has come in from GAVDP in "protocol" form (as an easy way to do IPC)
	// we sanity check this here before sending
	// run visitor over it
	CCapabilitySummaryVisitor* visitor = new CCapabilitySummaryVisitor();
	if (visitor)
		{
		TPtr8 ptr(const_cast<TUint8*>(aCapability.Ptr()), aCapability.Length(), aCapability.Length());

		visitor->Process(ptr);
		TAvdtpServiceCategories cats = visitor->CapabilitiesPresent();
		
		if (iIsReconfigure)
			{
			if (!(cats.CapabilityPresent(EServiceCategoryMediaCodec) || cats.CapabilityPresent(EServiceCategoryContentProtection)))
				{
				// check that if reconfiguring, the capability is reconfigurable (see 8.10.1, AVDTP)
				ret = KErrArgument;
				}
			}
		else
			{
			// check for interesting cases during non-reconfigure
			if (cats.CapabilityPresent(EServiceCategoryMultiplexing))
				{
				ret = KErrArgument; // only stack manage this capability
				}
			else if (cats.CapabilityPresent(EServiceCategoryReporting))
				{
				// this saves the stream having to reparse the set configuration buffer
				iReportingConfigured = ETrue;
				}
			else if (cats.CapabilityPresent(EServiceCategoryRecovery))
				{
				// this saves the stream having to reparse the set configuration buffer
				iRecoveryConfigured = ETrue;
				}
			}
		}
	else
		{
		ret = KErrNoMemory;
		}
		
	if (KErrNone==ret)
		{
		// ok to send, grow buffer to take this capability
		ret = iSetConfigBuffer.ReAlloc(iSetConfigBuffer.Length() + aCapability.Length());
		if (KErrNone==ret)
			{
			iSetConfigBuffer.Append(aCapability);
			}
		}
	delete visitor;

	return ret;
	}
	
	
TInt CRemoteSEPConfigurator::Finalise()
	{
	LOG_FUNC
	// the client has now sent all SetOpts for config
	// we need to finalise - ask stream to finalise out SetConfigBuffer
	TRAPD(res, SignallingSession().DoConfigureStreamL(iSetConfigBuffer,
														*iLocalSEP,
														iRemoteSEID,
														iReportingConfigured,
														iRecoveryConfigured));
		
	if (res!=KErrNone)
		{
		iSetConfigBuffer.Close();
		}
	else
		{
		// for remote seps we really do go async
		res = KRequestPending;
		// transferred ownership of iSetConfigBuffer, so don't close, just zero
		iSetConfigBuffer.Assign(NULL);
		}
	return res;
	}
	

CLocalSEPConfigurator* CLocalSEPConfigurator::NewL(CSignallingSession& aSignallingSession,
													 CAvdtpProtocol& aProtocol,
													 TSEID aSEID)
	{
	LOG_STATIC_FUNC
	CLocalSEPConfigurator* s = new (ELeave) CLocalSEPConfigurator(aSignallingSession,
																	aProtocol);
	CleanupStack::PushL(s);
	s->ConstructL(aSEID);
	CleanupStack::Pop(s);
	return s;	
	}

void CLocalSEPConfigurator::ConstructL(TSEID aSEID)
	{
	LOG_FUNC
	if (!aSEID.IsLocal())
		{
		User::Leave(KErrArgument);
		}
	// check in local sep queue (i.e. has been registered)
	iLocalSEP = &SignallingSession().FindLocalSEPL(aSEID);
	TAvdtpServiceCategories cats;
	cats.SetCapability(EAllServiceCategories);
	iCapabilityParseVisitor = new (ELeave) CCapabilityParseVisitor(cats);
	}


CLocalSEPConfigurator::CLocalSEPConfigurator(CSignallingSession& aSignallingSession,
						   CAvdtpProtocol& aProtocol)
	: CSEPConfigurator(aSignallingSession, aProtocol)
	{
	LOG_FUNC
	}
	
CLocalSEPConfigurator::~CLocalSEPConfigurator()
	{
	LOG_FUNC
	delete iCapabilityParseVisitor;
	}

TInt CLocalSEPConfigurator::AddCapability(const TDesC8& aCapabilityInProtocolForm)
/*
Add in caps to SetConf - one per SetOpt
When finalise Ioctl comes in we have a complete packet to send to sigch
*/
	{
	LOG_FUNC
	// convert into T-class form to santiy check this is goign to end up as a good configuration
	TPtr8 ptr(const_cast<TUint8*>(aCapabilityInProtocolForm.Ptr()), aCapabilityInProtocolForm.Length(), aCapabilityInProtocolForm.Length());
	iCapabilityParseVisitor->Process(ptr);

	return KErrNone;
	}
	
TInt CLocalSEPConfigurator::Finalise()
	{
	LOG_FUNC
	// get all the set capabilities
	TCapabilitiesArray caps = iCapabilityParseVisitor->GetCapabilities();
	
	 // muxing shouldnt be set yet
	__ASSERT_DEBUG(!caps[EServiceCategoryMultiplexing], Panic(EAvdtpConfiguratorsMultiplexingUnexpectedlySet));
	
	// at this point we put in support for muxing as all Symbian local SEPs to
#ifndef __SYMBIAN_AVDTP_HIDE_MUX 

	TAvdtpMultiplexingCapability* mux = new TAvdtpMultiplexingCapability;
	if (mux)
		{
		TAvdtpMultiplexingCapabilityHelper helper(*mux, !!caps[EServiceCategoryReporting], !!caps[EServiceCategoryRecovery]);
		// set all relevant transport ids to zero to request values from INT
		// see AVDTP spec p90
		// if we are an INT we'll update this capability later in statespace
		helper.SetMediaSID(0);
		helper.SetMediaCID(0);
		
		if (caps[EServiceCategoryReporting])
			{
			helper.SetReportingSID(0);
			helper.SetReportingCID(0);
			}
		// if recovery exposed then set to 0, else leave
		if (caps[EServiceCategoryRecovery])
			{
			helper.SetRecoverySID(0);
			helper.SetRecoveryCID(0);
			}

		caps[EServiceCategoryMultiplexing] = mux; // ownership transferred	
		}
#endif
	// else forgot about trying to do muxing - it was an optimisation
	
	for (TInt i=0; i<ENumberOfServiceCategories; i++)
		{
		if (caps[i])
			{
			iLocalSEP->AddCapability(caps[i]); // transfer ownership of element
			}
		}
		
	return KErrNone;
	}

