/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 *
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
	File:		BroadcasterSession.h

	
*/

#ifndef __BROADCASTER_SESSION__
#define __BROADCASTER_SESSION__


#include "Task.h"

#include "TimeoutTask.h"


#include "RTSPClient.h"
#include "ClientSocket.h"
#include "SDPSourceInfo.h"
#include "UDPSocket.h"
#include "StrPtrLen.h"
#include "OSMutex.h"

class BroadcasterSession : public Task
{
	public:
	
		enum
		{
			kRTSPUDPBroadcasterType 			= 0,
			kRTSPTCPBroadcasterType 			= 1,
			kRTSPHTTPBroadcasterType 			= 2,
			kRTSPHTTPDropPostBroadcasterType 	= 3,
			kRTSPReliableUDPBroadcasterType 	= 4
		};
		typedef UInt32 BroadcasterType;
	
		BroadcasterSession(	UInt32 inAddr, UInt16 inPort, char* inURL,
						BroadcasterType inBroadcasterType,
						UInt32 inDurationInSec, UInt32 inStartPlayTimeInSec,
						UInt32 inRTCPIntervalInSec, UInt32 inOptionsIntervalInSec,
						UInt32 inHTTPCookie, Bool16 inAppendJunkData, UInt32 inReadInterval,
						UInt32 inSockRcvBufSize, 
						StrPtrLen *sdpSPLPtr,
						char *namePtr,
						char *passwordPtr,
						Bool16	deepDebug);
		virtual ~BroadcasterSession();
		
		//
		// Signals.
		//
		// Send a kKillEvent to delete this object.
		// Send a kTeardownEvent to tell the object to send a TEARDOWN and abort
		
		enum
		{
			kTeardownEvent = 0x00000100
		};
		
		virtual SInt64 Run();
		
		//
		// States. Find out what the object is currently doing
		enum
		{
			kSendingAnnounce	= 0,
			kSendingSetup		= 1,
			kSendingReceive		= 2,
			kBroadcasting		= 3,
			kSendingTeardown	= 4,
			kDone				= 5
		};
		//
		// Why did this session die?
		enum
		{
			kDiedNormally		= 0,	// Session went fine
			kTeardownFailed		= 1,	// Teardown failed, but session stats are all valid
			kRequestFailed		= 2,	// Session couldn't be setup because the server returned an error
			kBadSDP				= 3,	// Server sent back some bad SDP
			kSessionTimedout	= 4, 	// Server not responding
			kConnectionFailed	= 5,	// Couldn't connect at all.
			kDiedWhileBroadcasting	= 6,		// Connection was forceably closed while Broadcasting the movie
			kMemoryError		= 7
		};
		
		//
		// Once this client session is completely done with the TEARDOWN and ready to be
		// destructed, this will return true. Until it returns true, this object should not
		// be deleted. When it does return true, this object should be deleted.
		Bool16	IsDone() 		{ return fState == kDone; }
		
		//
		// ACCESSORS
	
		RTSPClient*				GetBroadcaster()	{ return fRTSPClient; }
		ClientSocket*			GetSocket()			{ return fSocket; }
		SDPSourceInfo*			GetSDPInfo()		{ return &fSDPParser; }
		UInt32					GetState() 			{ return fState; }
		UInt32					GetDeathState() 	{ return fDeathState; }
		
		// When this object is in the kDone state, this will tell you why the session died.
		UInt32					GetReasonForDying() { return fDeathReason; }
		UInt32					GetRequestStatus()	{ return fRTSPClient->GetStatus(); }
		
		// Tells you the total time we were receiving packets. You can use this
		// for computing bit rate
		SInt64					GetTotalPlayTimeInMsec() { return fTotalPlayTime; }
		
		QTSS_RTPPayloadType		GetTrackType(UInt32 inTrackIndex)
									{ return fSDPParser.GetStreamInfo(inTrackIndex)->fPayloadType; }
		UInt32					GetNumPacketsReceived(UInt32 inTrackIndex)
									{ return fStats[inTrackIndex].fNumPacketsReceived; }
		UInt32					GetNumBytesReceived(UInt32 inTrackIndex)
									{ return fStats[inTrackIndex].fNumBytesReceived; }
		UInt32					GetNumPacketsLost(UInt32 inTrackIndex)
									{ return fStats[inTrackIndex].fNumLostPackets; }
		UInt32					GetNumPacketsOutOfOrder(UInt32 inTrackIndex)
									{ return fStats[inTrackIndex].fNumOutOfOrderPackets; }
		UInt32					GetNumDuplicates(UInt32 inTrackIndex)
									{ return fStats[inTrackIndex].fNumDuplicates; }
		UInt32					GetNumAcks(UInt32 inTrackIndex)
									{ return fStats[inTrackIndex].fNumAcks; }
		UInt32					GetNumStreams()
									{ return fSDPParser.GetNumStreams();}
		UInt32					GetStreamDestPort(UInt32 inIndex)
									{ return fStats[inIndex].fDestRTPPort;}
		
		void					TearDownNow() {fTeardownImmediately = true; this->Signal(Task::kKillEvent);}
		//
		// Global stats
		static UInt32	GetActiveConnections()			{ return sActiveConnections; }
		static UInt32	GetBroadcastingConnections()	{ return sBroadcastingConnections; }
		static UInt32	GetConnectionAttempts()			{ return sTotalConnectionAttempts; }
		char* 			GetNextPacket(UInt32 *packetLen, UInt8 *channel);
		OS_Error		SendPacket(char* data, UInt32 len,UInt8	channel);
		OS_Error 		SendWaitingPackets();
		
		enum
		{
			kUDPTransportType			= 0,
			kReliableUDPTransportType	= 1,
			kTCPTransportType			= 2
		};
		typedef UInt32 TransportType;

		TransportType		GetTransportType()	{ return fTransportType; }
		
		UInt32				GetPacketQLen() 	{ return fPacketQueue.GetLength(); }
		OSMutex*			GetMutex() 		{ return &fMutex; }
	private:
	
		enum
		{
			kRawRTSPControlType 		= 0,
			kRTSPHTTPControlType		= 1,
			kRTSPHTTPDropPostControlType= 2
		};
		typedef UInt32 ControlType;
		
		
		ClientSocket*	fSocket;	// Connection object
		RTSPClient*		fRTSPClient;	// Manages the client connection
		SDPSourceInfo	fSDPParser;	// Parses the SDP in the DESCRIBE response
		TimeoutTask		fTimeoutTask; // Kills this connection in the event the server isn't responding
		
		ControlType 	fControlType;
		TransportType 	fTransportType;
		UInt32			fDurationInSec;
		UInt32			fStartPlayTimeInSec;
		UInt32			fRTCPIntervalInSec;
		UInt32			fOptionsIntervalInSec;
		
		UInt32			fState;		// For managing the state machine
		UInt32			fDeathState; // state at time of death
		UInt32			fDeathReason;
		UInt32			fNumSetups;
		UDPSocket**		fUDPSocketArray;
		
		SInt64			fPlayTime;
		SInt64			fTotalPlayTime;
		SInt64			fLastRTCPTime;
		
		Bool16			fTeardownImmediately;
		Bool16			fAppendJunk;
		UInt32			fReadInterval;
		UInt32			fSockRcvBufSize;
		
		//
		// Broadcaster stats
		struct TrackStats
		{
			enum
			{
				kSeqNumMapSize = 100,
				kHalfSeqNumMap = 50
			};
		
			UInt16			fDestRTPPort;
			UInt16			fDestRTCPPort;
			UInt32			fNumPacketsReceived;
			UInt32			fNumBytesReceived;
			UInt32			fNumLostPackets;
			UInt32			fNumOutOfOrderPackets;
			UInt32			fNumThrownAwayPackets;
			UInt8			fSequenceNumberMap[kSeqNumMapSize];
			UInt16			fWrapSeqNum;
			UInt16			fLastSeqNum;
			UInt32			fSSRC;
			Bool16			fIsSSRCValid;
			
			UInt16			fHighestSeqNum;
			UInt16			fLastAckedSeqNum;
			Bool16			fHighestSeqNumValid;
			
			UInt32			fNumAcks;
			UInt32			fNumDuplicates;
			
		};
		TrackStats*			fStats;
		
		static	char* fPacket;
		//
		// Global stats
		static UInt32			sActiveConnections;
		static UInt32			sBroadcastingConnections;
		static UInt32			sTotalConnectionAttempts;
		
		//
		// Helper functions for Run()
		void		SetupUDPSockets();
		void		ProcessMediaPacket(char* inPacket, UInt32 inLength, UInt32 inTrackID, Bool16 isRTCP);
		OS_Error	ReadMediaData();
		void 		SendReceiverReport();
		void 		AckPackets(UInt32 inTrackIndex, UInt16 inCurSeqNum, Bool16 inCurSeqNumValid);

		OSMutex				fMutex;//this data structure is shared!

		struct RTPPacket
		{
			RTPPacket() : fQueueElem(NULL), fData(NULL), fLen(0), fChannel(0),fCount(0){}
			~RTPPacket() { fData = NULL; fLen = 0; fChannel = 0; }
			void SetEnclosingObject(void *obj) {fQueueElem.SetEnclosingObject(obj);}
			void SetPacketData(char* data, UInt32 len,UInt8	channel) { fData = data; fLen = len; fChannel = channel; }
			OSQueueElem *GetQElement() {return &fQueueElem;}
			OSQueueElem	fQueueElem;
			char*	fData;
			UInt32	fLen;
			UInt8	fChannel;
			UInt64	fCount;
		};
		UInt64	fPacketCount;
		OSQueue fPacketQueue;
		
		UInt32 fPacketLen;
		UInt8 fChannel;
//		char* fPacket;
		

};

#endif //__BROADCASTER_SESSION__
