/*
 *
 * @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@
 *
 */
 /*
	Contains:	API interface for objects to use to get access to attributes,
				data items, whatever, specific to RTP sessions (see RTPSession.h
				for more details on what that object is). This object
				implements the RTP Session Dictionary.

	

*/


#ifndef _RTPSESSIONINTERFACE_H_
#define _RTPSESSIONINTERFACE_H_

#include "QTSSDictionary.h"

#include "RTCPSRPacket.h"
#include "RTSPSessionInterface.h"
#include "TimeoutTask.h"
#include "Task.h"
#include "RTPBandwidthTracker.h"
#include "RTPOverbufferWindow.h"

#include "OSMutex.h"
#include "atomic.h"

class RTPSessionInterface : public QTSSDictionary, public Task
{
	public:
	
		// Initializes dictionary resources
		static void	Initialize();
		
		//
		// CONSTRUCTOR / DESTRUCTOR
		
		RTPSessionInterface();
		virtual ~RTPSessionInterface()
			{ 	if (fRTSPSession != NULL)
					fRTSPSession->DecrementObjectHolderCount();
				delete [] fSRBuffer.Ptr;
				delete [] fAuthNonce.Ptr;		
				delete [] fAuthOpaque.Ptr;		
			}
		
		virtual void SetValueComplete(	UInt32 inAttrIndex, QTSSDictionaryMap* inMap, UInt32 inValueIndex, const void* inNewValue, 
								UInt32 inNewValueLen);

		//Timeouts. This allows clients to refresh the timeout on this session
		void	RefreshTimeout()  	  	{ fTimeoutTask.RefreshTimeout(); }
		void	RefreshRTSPTimeout()  	{ if (fRTSPSession != NULL) fRTSPSession->RefreshTimeout(); }
		void	RefreshTimeouts() 		{ RefreshTimeout(); RefreshRTSPTimeout();}
		
		//
		// ACCESSORS
		
		Bool16	IsFirstPlay()			{ return fIsFirstPlay; }
		SInt64	GetFirstPlayTime()		{ return fFirstPlayTime; }
		//Time (msec) most recent play was issued
		SInt64	GetPlayTime()			{ return fPlayTime; }
		SInt64	GetNTPPlayTime()		{ return fNTPPlayTime; }
		SInt64	GetSessionCreateTime()	{ return fSessionCreateTime; }
		//Time (msec) most recent play, adjusted for start time of the movie
		//ex: PlayTime() == 20,000. Client said start 10 sec into the movie,
		//so AdjustedPlayTime() == 10,000
		QTSS_PlayFlags GetPlayFlags()	{ return fPlayFlags; }
		OSMutex*		GetSessionMutex()	{ return &fSessionMutex; }
		UInt32			GetPacketsSent()	{ return fPacketsSent; }
		UInt32			GetBytesSent()	{ return fBytesSent; }
		OSRef*		GetRef()			{ return &fRTPMapElem; }
		RTSPSessionInterface* GetRTSPSession() { return fRTSPSession; }
		UInt32		GetMovieAvgBitrate(){ return fMovieAverageBitRate; }
		QTSS_CliSesTeardownReason GetTeardownReason() { return fTeardownReason; }
		QTSS_RTPSessionState	GetSessionState() { return fState; }
		void	SetUniqueID(UInt32 theID)	{fUniqueID = theID;}
		UInt32  GetUniqueID()           { return fUniqueID; }
		RTPBandwidthTracker* GetBandwidthTracker() { return &fTracker; }
		RTPOverbufferWindow* GetOverbufferWindow() { return &fOverbufferWindow; }
		UInt32	GetFramesSkipped() { return fFramesSkipped; }

		//
		// MEMORY FOR RTCP PACKETS
		
		//
		// Class for easily building a standard RTCP SR
		RTCPSRPacket*	GetSRPacket()		{ return &fRTCPSRPacket; }

		//
		// Memory if you want to build your own
		char*			GetSRBuffer(UInt32 inSRLen);
		
		//
		// STATISTICS UPDATING
		
		//The session tracks the total number of bytes sent during the session.
		//Streams can update that value by calling this function
		void			UpdateBytesSent(UInt32 bytesSent) { fBytesSent += bytesSent; }
						
		//The session tracks the total number of packets sent during the session.
		//Streams can update that value by calling this function				
		void  			UpdatePacketsSent(UInt32 packetsSent) { fPacketsSent += packetsSent; }
										
		void			UpdateCurrentBitRate(const SInt64& curTime)
			{ if (curTime > (fLastBitRateUpdateTime + 10000)) this->UpdateBitRateInternal(curTime); }

		void			SetAllTracksInterleaved(Bool16 newValue) { fAllTracksInterleaved = newValue; }		
		//
		// RTSP RESPONSES

		// This function appends a session header to the SETUP response, and
		// checks to see if it is a 304 Not Modified. If it is, it sends the entire
		// response and returns an error
		QTSS_Error DoSessionSetupResponse(RTSPRequestInterface* inRequest);
		
		//
		// RTSP SESSION
								
		// This object has a current RTSP session. This may change over the
		// life of the RTSPSession, so update it. It keeps an RTSP session in
		// case interleaved data or commands need to be sent back to the client. 
		void			UpdateRTSPSession(RTSPSessionInterface* inNewRTSPSession);
				
		// let's RTSP Session pass along it's query string
		void			SetQueryString( StrPtrLen* queryString );
		
		// SETTERS and ACCESSORS for auth information
		// Authentication information that needs to be kept around
		// for the duration of the session		
		QTSS_AuthScheme	GetAuthScheme() { return fAuthScheme; }
		StrPtrLen*		GetAuthNonce() { return &fAuthNonce; }
		UInt32			GetAuthQop() { return fAuthQop; }
		UInt32			GetAuthNonceCount() { return fAuthNonceCount; }
		StrPtrLen*		GetAuthOpaque() { return &fAuthOpaque; }
		void			SetAuthScheme(QTSS_AuthScheme scheme) { fAuthScheme = scheme; }
		// Use this if the auth scheme or the qop has to be changed from the defaults 
		// of scheme = Digest, and qop = auth
		void			SetChallengeParams(QTSS_AuthScheme scheme, UInt32 qop, Bool16 newNonce, Bool16 createOpaque);
		// Use this otherwise...if newNonce == true, it will create a new nonce
		// and reset nonce count. If newNonce == false but nonce was never created earlier
		// a nonce will be created. If newNonce == false, and there is an existing nonce,
		// the nounce count will be incremented.
		void 			UpdateDigestAuthChallengeParams(Bool16 newNonce, Bool16 createOpaque, UInt32 qop);
	
		Float32* GetPacketLossPercent() { UInt32 outLen;return  (Float32*) this->PacketLossPercent(this, &outLen);}

		SInt32			GetQualityLevel()	{ return fSessionQualityLevel; }
		SInt32*			GetQualityLevelPtr()	{ return &fSessionQualityLevel; }
		void			SetQualityLevel(SInt32 level) { fSessionQualityLevel = level; }
		SInt64			fLastQualityCheckTime;
		SInt64			fLastQualityCheckMediaTime;
		Bool16			fStartedThinning;
		
#if RTP_STREAM_THINNING_TIMING_LOG
		SInt64			fFirstPacketSendTime;
		SInt64			fFirstPacketTransmitTime;
		UInt32			fNumPrints;

		static FILE*	sStatsLog;
		static OSMutex*	sStatsLogMutex;
#endif

	protected:
	
		// These variables are setup by the derived RTPSession object when
		// Play and Pause get called

		//Some stream related information that is shared amongst all streams
		Bool16		fIsFirstPlay;
		Bool16		fAllTracksInterleaved;
		SInt64		fFirstPlayTime;//in milliseconds
		SInt64		fPlayTime;
		SInt64		fAdjustedPlayTime;
		SInt64		fNTPPlayTime;
		SInt64		fNextSendPacketsTime;
		
		SInt32		fSessionQualityLevel;
		
		//keeps track of whether we are playing or not
		QTSS_RTPSessionState fState;
		
		// If we are playing, this are the play flags that were set on play
		QTSS_PlayFlags	fPlayFlags;
		
		//Session mutex. This mutex should be grabbed before invoking the module
		//responsible for managing this session. This allows the module to be
		//non-preemptive-safe with respect to a session
		OSMutex		fSessionMutex;

		//Stores the session ID
		OSRef			 	fRTPMapElem;
		char				fRTSPSessionIDBuf[QTSS_MAX_SESSION_ID_LENGTH + 4];
		
		UInt32		fLastBitRateBytes;
		SInt64		fLastBitRateUpdateTime;
		
		// In order to facilitate sending data over the RTSP channel from
		// an RTP session, we store a pointer to the RTSP session used in
		// the last RTSP request.
		RTSPSessionInterface* fRTSPSession;
#if RTP_OVER_BUFFER_WINDOW_LOGGING		
		static FILE*				sOverbufferTimesLog;
		static OSMutex*				sOverbufferTimesLogMutex;
#endif

	private:
	
		//
		// Utility function for calculating current bit rate
		void UpdateBitRateInternal(const SInt64& curTime);

		static void* PacketLossPercent(QTSSDictionary* inSession, UInt32* outLen);
		static void* TimeConnected(QTSSDictionary* inSession, UInt32* outLen);		
		
		// Create nonce
		void CreateDigestAuthenticationNonce();

		// One of the RTP session attributes is an iterated list of all streams.
		// As an optimization, we preallocate a "large" buffer of stream pointers,
		// even though we don't know how many streams we need at first.
		enum
		{
			kStreamBufSize 				= 4,
			kUserAgentBufSize 			= 256,
			kPresentationURLSize		= 256,
			kFullRequestURLBufferSize	= 256,
			kRequestHostNameBufferSize	= 80,
			
			kIPAddrStrBufSize 			= 20,
			kLocalDNSBufSize 			= 80,
			
			kAuthNonceBufSize			= 32,
			kAuthOpaqueBufSize			= 32,
			
		};
		
		void* 		fStreamBuffer[kStreamBufSize];

		
		// theses are dictionary items picked up by the RTSPSession
		// but we need to store copies of them for logging purposes.
		char		fUserAgentBuffer[kUserAgentBufSize];
		char		fPresentationURL[kPresentationURLSize];			// eg /foo/bar.mov
		char		fFullRequestURL[kFullRequestURLBufferSize];		// eg rtsp://yourdomain.com/foo/bar.mov
		char		fRequestHostName[kRequestHostNameBufferSize];	// eg yourdomain.com
	
		char		fRTSPSessRemoteAddrStr[kIPAddrStrBufSize];
		char		fRTSPSessLocalDNS[kLocalDNSBufSize];
		char		fRTSPSessLocalAddrStr[kIPAddrStrBufSize];
		
		char		fUserNameBuf[RTSPSessionInterface::kMaxUserNameLen];
		char		fUserPasswordBuf[RTSPSessionInterface::kMaxUserPasswordLen];
		char		fUserRealmBuf[RTSPSessionInterface::kMaxUserRealmLen];
		UInt32		fLastRTSPReqRealStatusCode;

		//for timing out this session
		TimeoutTask	fTimeoutTask;
		UInt32		fTimeout;
		
		// Time when this session got created
		SInt64		fSessionCreateTime;

		//Packet priority levels. Each stream has a current level, and
		//the module that owns this session sets what the number of levels is.
		UInt32		fNumQualityLevels;
		
		//Statistics
		UInt32 fBytesSent;
		UInt32 fPacketsSent;	
		Float32 fPacketLossPercent;
		SInt64 fTimeConnected;
		// Movie size & movie duration. It may not be so good to associate these
		// statistics with the movie, for a session MAY have multiple movies...
		// however, the 1 movie assumption is in too many subsystems at this point
		Float64		fMovieDuration;
		UInt64		fMovieSizeInBytes;
		UInt32		fMovieAverageBitRate;
		UInt32		fMovieCurrentBitRate;
		
		QTSS_CliSesTeardownReason fTeardownReason;
		// So the streams can send sender reports
		UInt32		fUniqueID;
		
		RTCPSRPacket		fRTCPSRPacket;
		StrPtrLen			fSRBuffer;
		
		RTPBandwidthTracker	fTracker;
		RTPOverbufferWindow	fOverbufferWindow;
		
		// Built in dictionary attributes
		static QTSSAttrInfoDict::AttrInfo	sAttributes[];
		static unsigned int	sRTPSessionIDCounter;

		// Authentication information that needs to be kept around
		// for the duration of the session		
		QTSS_AuthScheme				fAuthScheme;
		StrPtrLen					fAuthNonce;
		UInt32						fAuthQop;
		UInt32						fAuthNonceCount;					
		StrPtrLen					fAuthOpaque;
		UInt32						fQualityUpdate;
		
		UInt32						fFramesSkipped;
        
#if RTP_OVER_BUFFER_WINDOW_LOGGING
		static char*				sOverbufferTimesLogFilename;
#endif
#if RTP_STREAM_THINNING_TIMING_LOG
		static char*				sStatsLogFilename;
#endif

        Bool16                      fOverBufferEnabled;
};

#endif //_RTPSESSIONINTERFACE_H_
