// 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:
//

#ifndef AVDTPMUXCHANNEL_H
#define AVDTPMUXCHANNEL_H

#include "avdtpTransportChannel.h"
#include "avdtpTransportSession.h"
#include "avdtpMessage.h"

class CTransportSession;
class CMuxChannel;


#ifdef _OOM_TEST
static const TInt KMuxSendInitial = 500;
static const TInt KMuxSendPeriod  = 500;
#else
static const TInt KMuxSendInitial = 2000000;
static const TInt KMuxSendPeriod  = 2000000;
#endif
// this constant already allows for AL header
static const TInt KMaxMuxedPacketLength = 335;// 339 for DH5 - 4 byte L2CAP
static const TInt KMuxedPacketLengthReadyToSend = 100; // for 3 slot use
static const TInt KALHeaderLength = 1; // we only support one byte (no AVDTP jumbograms)
static const TInt KALHeaderTSIDOffset = 5;
static const TInt KALHeaderFOffset = 2;
static const TInt KALHeaderLCODE_MSBOffset = 1;
static const TInt KALHeaderLCODE_LSBOffset = 0;


NONSHARABLE_CLASS(CAvdtpOutboundMuxedMessage) : public CAvdtpMessage
	{
	public:
	enum TMuxedMessageOperation
		{
		ENoMemory,
		EReadyToSendPacketAdded,
		EReadyToSendPacketNotConsumed,
		EPacketAdded,
		};
			
	static CAvdtpOutboundMuxedMessage* NewL();
	virtual TInt NewData(TUint aCount);
	TMuxedMessageOperation TryToAddPacket(TTSID aTSID, RMBufChain& aChain);
	TInt Reset();

private:
	TInt AddHeader(TTSID aTSID, TInt aPacketLen);
	/*
	*/	
	
/*
	class starts with blank muxpacket
	sessions write into channel
	muxchannel can put into send pool for that session
	muxchannelCB fetches stuff off pools into packet
	
OR!
	class starts with blank muxpacket
	sessions request to send (with a packet length?)
	channel invites send
	MuxTC can base sends on length (and above rules)
		
*/	
	
	// easier if send pools are in channels as then muxchannel can
	// "see" at any time what size packets it has and can squeeze into an ALPDU	

private:
	};


NONSHARABLE_CLASS(CAvdtpInboundMuxedMessage) : public CAvdtpMessage
	{
public:
	static CAvdtpInboundMuxedMessage* NewL(CMuxChannel& aMuxChannel);
	virtual TInt NewData(TUint aCount);	// should cause self-parsing
	
	void Reset();

private:
	CAvdtpInboundMuxedMessage(CMuxChannel& aMuxChannel);
private:
	CMuxChannel&	iMuxChannel;	// to callback when mux message parsed
	};


/*
helper for cmux channel to iterate over *all* of its sessions
it keeps its sessions in separate arrays cos of rom/ram/runtime savings
(e.g. it has to knwo the session type, but that would incur virtual function calls)

We prefer arrays so that inbound data can more quickly be put onto array rather than iter to find TSID

*/
// templates version (armv5 urel is only ~160 bytes bigger than a non-template version

template <class T>
class TSessionIterator
	{
public:
	TSessionIterator(const TArray< const TArray<T> >& aArrayOfArrays);

	const T& operator++(TInt aPostIncrementDummy);
	
	operator TBool();	// conversion operator to make iterator just like a normal one
	void Reset();
private:
	void DoIncrement();
	const TArray< const TArray<T> > iArrays;
	TInt	iArray;		// which array we're at
	TInt	iArrayIndex;// the index we're at in the array
	TBool	iFinished;
	};

	
NONSHARABLE_CLASS(CMuxChannel) : public CTransportChannel
	{
public:
	static CMuxChannel* NewL(CAvdtpProtocol& aProtocol, const TBTDevAddr& aRemoteDevice,
								TTCID aRemotelyAssignedTCID);

// management path
	virtual TInt AttachTransportSession(CUserPlaneTransportSession& aSession, TAvdtpTransportSessionType aType);
	virtual void DetachTransportSession(CUserPlaneTransportSession& aSession, TAvdtpTransportSessionType aType);
	virtual TBool CouldAttachSession(const TAvdtpSockAddr& aAddr);
	virtual TTCID TCID() const;
	CServProviderBase* ObtainSAP();
	void ActiveMultiplexer();
// send path
	virtual TUint SendPacket(TTSID aTSID, RMBufChain& aPacket);
// receive path	
	TInt GetData(RMBufChain& aRxBuffer);
	virtual void TransportSessionBlocked(TAvdtpTransportSessionType aSession, TBool aBlocked);

//... interface from inbound packet
//	TInt SessionDataReceived(TTSID aTSID, RMBufChain& aChain);
	
	~CMuxChannel();

private:	
	CMuxChannel(CAvdtpProtocol& aProtocol, const TBTDevAddr& aRemoteDevice);
						 
	static TInt MuxSendIntervalCb(TAny* aCMuxChannel);
	void CheckForClose();
	TUint DoSend();
						
private:
// interesting stuff from l2cap
	virtual void NewData(TUint aCount); // forwards to inbound mux message
	virtual void Error(TInt aError,TUint aOperationMask=EErrorAllOperations);
	virtual void Disconnect();
	virtual void CanSend();
	
	void ConstructL(TTCID aRemotelyAssignedTCID);

private:
	// receive path	
	struct TUserPlaneTransportSessionState
		{
		inline TUserPlaneTransportSessionState(CUserPlaneTransportSession& aSession);
		
		CUserPlaneTransportSession& iSession;
		TBool iIsBlocked;
		
		};
		
	RArray<TUserPlaneTransportSessionState>	iMediaSessions;
	RArray<TUserPlaneTransportSessionState>	iReportingSessions;
	RArray<TUserPlaneTransportSessionState>	iRecoverySessions; // recall not from same streams as other sessions

	RArray<const TArray<TUserPlaneTransportSessionState> > iSessions;

// for now shared by all the same-typed sessions
//	RPacketPool			iMediaReceivePool;
//	RPacketPool			iReportingReceivePool;
//	RPacketPool			iRecoveryReceivePool;
	
	CPeriodic*							iMuxSendTimer;	
	
	CAvdtpInboundMuxedMessage*			iInboundMessage;
	CAvdtpOutboundMuxedMessage*			iOutboundMessage;
#ifdef SESSION_ITERATOR_CONCRETE
	TSessionIterator iIter;
#else
	TSessionIterator<TUserPlaneTransportSessionState> iIter;
#endif
	// could have send iterator?
	};

inline CMuxChannel::TUserPlaneTransportSessionState::TUserPlaneTransportSessionState(
	CUserPlaneTransportSession& aSession)
: iSession(aSession)
	{
	}

		
#endif //AVDTPMUXCHANNEL_H
