// 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 media transport session
// 
//

/**
 @file
 @internalComponent
*/

#include <bluetooth/logger.h>
#include "avdtpMediaSession.h"
#include "avdtp.h"
#include "avdtpsap.h"
#include "avdtpStream.h"
#include "avdtputil.h"
#include "gavdpinterface.h"

//#include <es_mbman.h> // for MBUF logging

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

CMediaSession* CMediaSession::NewLC(CAvdtpProtocol& aProtocol, CAvdtpSAP& aSAP, CAVStream& aStream)
	{
	LOG_STATIC_FUNC
	CMediaSession* s = new (ELeave) CMediaSession(aProtocol, aSAP, aStream);
	CleanupStack::PushL(s);
	s->ConstructL();
	return s;
	}
	
CMediaSession* CMediaSession::NewL(CAvdtpProtocol& aProtocol, CAvdtpSAP& aSAP, CAVStream& aStream)
	{
	LOG_STATIC_FUNC
	CMediaSession* s = CMediaSession::NewLC(aProtocol, aSAP, aStream);
	CleanupStack::Pop();
	return s;		
	}
	
CMediaSession::CMediaSession(CAvdtpProtocol& aProtocol, CAvdtpSAP& aSAP, CAVStream& aStream)
: CUserPlaneTransportSession(aProtocol, aSAP, EMedia, aStream)
	{
	LOG_FUNC
	iNotifyMediaPacketDroppedFlag = EFalse;
	iPacketsLost = 0;
	}
	
void CMediaSession::ConstructL()
	{
	LOG_FUNC
	CUserPlaneTransportSession::ConstructL();
	
	// Balking is set to true.  This allows the circular buffer to wrap
	// and overwrite the oldest data.
	User::LeaveIfError(iSendPool.Create(4, ETrue));
	User::LeaveIfError(iReceivePool.Create(16, ETrue));
	}

TInt CMediaSession::DoActiveOpen()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStream, Panic(EAvdtpTransportSessionBaseNotCheckStream));
	TInt ret = KErrGeneral; // may be OOM or notfound

	ret = iStream->AddSession(EMedia,*this,iTransportChannel);
	if (ret!=KErrNone)
		{
		// not erroring the stream, as it's not it's fault
		// and it may not exist anyway!
		iStream = NULL;
		iSAP.Error(ret);
		}
	return ret;	
	}

TInt CMediaSession::Send(RMBufChain& aData, TUint /*aOptions*/, TSockAddr* /*aAddr*/)
	{
	LOG_FUNC
	// if we get flow controlled should we *could* indicate to reporting session which could complete
	// an ioctl (for rtcp use)
//	if (iStream && iStream->CanSend(EMedia))
	TInt requestedToSend = aData.Length();
	TInt packetsLostThisSend = 0; // Packets lost during this send (if any)

	if (!iTransportChannelBlocked)
		{
		RMBufChain toSend;
		iSendPool.Remove(toSend);
		
		if (toSend.IsEmpty())
			{
			toSend.Assign(aData);
			}
		else
			{
			iSendPool.Add(aData, packetsLostThisSend);
			iPacketsLost += packetsLostThisSend;
			}
			
		DoSend(toSend);
		}
	else
		{
		iSendPool.Add(aData, packetsLostThisSend);
		iPacketsLost += packetsLostThisSend;
		}
	
	// If client app has previous sent pack drop IOCTL it needs to be notified
	if (iNotifyMediaPacketDroppedFlag && iPacketsLost)
		{
		TPckgBuf<TInt> buf(iPacketsLost);
		iSAP.ServiceComplete(&buf);
		iNotifyMediaPacketDroppedFlag = EFalse;
		iPacketsLost = 0;
		}
		
	return requestedToSend;
	}

// if pools become nicely generic then we could move this sending stuff to base class
// and have sessions just configure the Tx, Rx pools
void CMediaSession::DoSend(RMBufChain& aData)
	{
	LOG_FUNC
	if (iTransportChannel->SendPacket(EMedia, aData)==0)
		{
		// as media session, just free stale AV data (gets tricky putting stuff back on pool, and not really needed)
		// but mark TC as blokced so we can begin buffering in future
		aData.Free();
		iTransportChannelBlocked = ETrue;
		}
	}
	

void CMediaSession::CanSend()
	{
	LOG_FUNC
	// pluck off pool
	iTransportChannelBlocked = EFalse;
	//RDebug::Print(_L("MBuf free %d"), CMBufManager::Context()->__DbgGetBufSpace());
	// something was placed on the send pool - remove it and send
	RMBufChain chain;

	do
		{
		iSendPool.Remove(chain);
		if (!chain.IsEmpty())
			{
			DoSend(chain);
			}
		}
	while (!chain.IsEmpty());
	}

void CMediaSession::ReceivePacketLost()
	{
	// Increment the packet lost count.
	iPacketsLost++;
	
	// If client app has previous sent pack drop IOCTL it needs to be notified
	if (iNotifyMediaPacketDroppedFlag)
		{
		TPckgBuf<TInt> buf(iPacketsLost);
		iSAP.ServiceComplete(&buf);
		iNotifyMediaPacketDroppedFlag = EFalse;
		iPacketsLost = 0;
		}
	}

TInt CMediaSession::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
	{
	LOG_FUNC
	TInt ret = KErrArgument;
	if (aLevel == KSolBtAVDTPMedia)
		{
		switch (aName)
			{
			case EAvdtpMediaGetMaximumPacketSize:
				{
				if (iTransportChannel)
					{
					TInt mtu;
					ret = iTransportChannel->MaxPacketSize(mtu);
					if (ret==KErrNone)
						{
						LOG1(_L("Media session transport channel MTU = %d"), mtu);
						TPckg<TInt> mtuBuf(mtu);
						aOption = mtuBuf;
						}
					}
				else
					{
					ret = KErrNotReady;
					}
				}
			break;
			case EAvdtpMediaGetMaximumReceivePacketSize:
				{
				if (iTransportChannel)
					{
					TInt mru;
					ret = iTransportChannel->MaxReceivePacketSize(mru);
					if (ret==KErrNone)
						{
						LOG1(_L("Media session transport channel MRU = %d"), mru);
						TPckg<TInt> mruBuf(mru);
						aOption = mruBuf;
						}
					}
				else
					{
					ret = KErrNotReady;
					}
				}
			break;
			default:
				ret = KErrNotSupported;
			}
		}
			
	return ret;
	}

void CMediaSession::Ioctl(TUint aLevel, TUint aName, const TDesC8* /* aOption */)
	{
	LOG_FUNC
	TInt res;
	
	if (aLevel == KSolBtAVDTPMedia)	
		{
		switch (aName)
			{
			case ENotifyAvdtpMediaPacketDropped:
				{
				iNotifyMediaPacketDroppedFlag= ETrue;
				res = KErrNone;
				break;
				}
			default:
				{
				res = KErrNotSupported;
				break;
				}
			}
		if (res!=KErrNone)
			{
			iSAP.Error(res);
			}
		}
	}
