/*
 *
 * @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:		QTSSMP3StreamingModule.h

	Contains:	Handle ShoutCast/IceCast-style MP3 streaming.
	
	Written by:	Steve Ussery	

*/

#ifndef __QTSSMP3STREAMINGMODULE_H__
#define __QTSSMP3STREAMINGMODULE_H__

#include "QTSS.h"
#include "OSQueue.h"
#include "OSMutex.h"
#include "OSHashTable.h"
#include "OSBufferPool.h"

extern "C"
{
	EXPORT QTSS_Error QTSSMP3StreamingModule_Main(void* inPrivateArgs);
}

#define kURLBufferSize 1024
#define kSongNameBufferSize 1024
#define kHeaderBufferSize 1024
#define kRequestBufferSize 512
#define kHostNameBufferSize 256
#define kUserAgentBufferSize 256
#define kClientMetaInt 24576

// The client poll interval in milliseconds
#define kClientPollInterval 253

// FORWARD CLASS DEFINES

class MP3ClientSessionRef;
class MP3ClientSession;
class MP3ClientQueue;
class MP3BroadcasterSession;
class MP3BroadcasterQueue;
class MP3SessionRef;
class MP3SessionRefKey;
class MP3SessionTable;

enum {
	kMP3UndefinedSessionType = 0,
	kMP3BroadcasterSessionType,
	kMP3ClientSessionType
};

//
// MP3Session -- This is a base class to hold all the MP3 Session state info.
// We will subclass it for MP3 Broadcaster sessions and MP3 Client sessions.
// (See the MP3BroadcasterSession & MP3ClientSession classes defined below.)
//
class MP3Session 
{
public:
                                MP3Session();

                                MP3Session(QTSS_RTSPSessionObject sess, QTSS_StreamRef stream);
					
	virtual			~MP3Session();
	
	// The IsA() method is a mechanism for subclasses of this class to
	// identify their type. It is kMP3UndefinedSessionType for this
	// base class.
	
	virtual UInt8		IsA() const;
	
	// Session data field accessor methods
	
	QTSS_RTSPSessionObject	GetSession() { return fSession; }
	
	void			SetSessionID(UInt32 sessID) { fSessID = sessID; }
	
	UInt32			GetSessionID() { return fSessID; }
	
	QTSS_StreamRef		GetStreamRef() { return fStream; }
	
	UInt16			GetState() { return fState; }
	
	void			SetState(UInt16 state) { fState = state; }
	
	UInt16			GetResult() { return fResult; }
	
	void			SetResult(UInt16 result) { fResult = result; }
	
	static SInt32		GetTotalNumMP3Sessions() { return sTotalNumMP3Sessions; }

protected:
	
	void			SetSession(QTSS_RTSPSessionObject session) { fSession = session; }

	void			SetStreamRef(QTSS_StreamRef stream) { fStream = stream; }
	
private:
	
	static SInt32		sTotalNumMP3Sessions;

	UInt16			fState;
	UInt16			fResult;
	QTSS_StreamRef 		fStream;
	QTSS_RTSPSessionObject 	fSession;
	UInt32	 		fSessID;
};
	
//
// MP3BroadcasterSession -- This is a class to hold all the MP3 Broadcaster
// session-related state info. There is a global queue of these managed by the
// server. Each instance of this class must have a unique mountpoint name
// string which clients will use to identify the broadcast stream they are
// "tuning" into.
//
class MP3BroadcasterSession : public MP3Session
{
public:


	// MP3BroadcasterSession states

	enum {
		kBroadcasterInitState = 0,
		kBroadcasterValidatePasswordState,
		kBroadcasterGetHeaderState,
		kBroadcasterRecvDataState,
		kBroadcasterShutDownState
	};

                                MP3BroadcasterSession(QTSS_RTSPSessionObject sess, QTSS_StreamRef stream);
					
	virtual			~MP3BroadcasterSession();
	
	// The IsA() method is a mechanism for subclasses of the MP3Session class
	// to identify their type. It is kMP3BroadcasterSessionType for this
	// subclass.
	
	virtual UInt8		IsA() const;
	
	// Session state control methods
	
	QTSS_Error		ExecuteState();
	
	void			InitBroadcastSessionState();
	
	void			AcceptBroadcastSessionState();
	
	void			AcceptPasswordState();
	
	void			AcceptDataStreamState();
	
	void			ShutDownState();
	
	// MP3 Client handling methods
	
	QTSS_Error		AddClient(QTSS_RTSPSessionObject sess, QTSS_StreamRef stream);
	
	QTSS_Error		RemoveClient(QTSS_RTSPSessionObject sess);
	
	Bool16 			IsMyClient(QTSS_RTSPSessionObject sess);

	// Session data field accessor methods

	void			SetMountpoint(char* mp);
	
	void			SetMountpoint(StrPtrLen& mp);
	
	Bool16			MountpointEqual(char* mp);
	
	Bool16			MountpointEqual(StrPtrLen& mp);
	
	char*			GetMountpoint() { return fMountpoint; }
	
	char*			GetHeader() { return fHeader; }
	
	void			SetSongName(char* sn);
	
	char*			GetSongName() { return fSongName; }
	
	// Session data handlers
	
	QTSS_Error 		SendOKResponse();

	QTSS_Error 		GetBroadcastHeaders();

	QTSS_Error 		GetBroadcastData();

	QTSS_Error 		SendClientsData();

	void	 		PreflightClients();

protected:
	
	void			TerminateHeaders();

private:
	
                                MP3BroadcasterSession();
					
	MP3ClientQueue*		fMP3ClientQueue;
	UInt32 			fDataBufferLen;
	UInt32 			fDataBufferSize;
	char			fMountpoint[kURLBufferSize];
	char			fHeader[kHeaderBufferSize];
	char			fSongName[kSongNameBufferSize];
	char*			fBuffer;
	OSMutex			fSongNameMutex;
	Bool16			fNewSongName;
};

//
// MP3ClientSession -- This is a class to hold all the MP3 client
// session-related state info. These class instances are always owned by a
// instance of the MP3BroadcasterSession class which has a queue of these.
//
class MP3ClientSession : public MP3Session
{
public:


	// MP3ClientSession states

	enum {
		kClientInitState = 0,
		kClientSendResponse,
		kClientSendDataState,
		kClientShutDownState
	};

                                MP3ClientSession(QTSS_RTSPSessionObject sess, 
                                                 QTSS_StreamRef stream,
                                                 MP3BroadcasterSession* owner);
					
	virtual			~MP3ClientSession();
	
	// The IsA() method is a mechanism for subclasses of the MP3Session class
	// to identify their type. It is kMP3ClientSessionType for this subclass.
	
	virtual UInt8		IsA() const;
	
	// Session stream handlers
	
	QTSS_Error 		SendResponse();

	QTSS_Error 		SendMP3Data(char* buffer, UInt32 bufferlen);

	QTSS_Error 		RetrySendData();

	QTSS_Error 		SendMetaData();
	
	// Session data field accessor methods
	
	void			SetHeader(char* header);
	
	char*			GetHeader() { return fHeader; }
	
	char*			GetHostName() { return fHostName; }
	
	char*			GetUserAgent() { return fUserAgent; }
	
	void			SetSongName(char* sn);
	
	char*			GetSongName() { return fSongName; }
	
	MP3BroadcasterSession*	GetOwner() { return fOwner; }
	
	void			SetOwner(MP3BroadcasterSession* owner) { fOwner = owner; }
	
	Bool16			WasBlocked() { return fWasBlocked; }

	Bool16			WantsContentLength() { return fNeedsContentLength; }

	void			SetRequest(char* req) { ::strcpy(fRequestBuffer, req); }

	char*			GetRequest() { return fRequestBuffer; }

	UInt32			GetTotalCount() { return fBytesSent; }

	SInt64			GetConnectTime() { return fConnectTime; }
	
private:
	
                                MP3ClientSession();
					
	void			UpdateBitRateInternal(const SInt64& curTime);
	
	void			ParseRequestParams(QTSS_StreamRef stream);

	UInt32	 		fBytesSent;
	UInt32	 		fCurrentBitRate;
	UInt32	 		fLastBitRateBytes;
	SInt64	 		fLastBitRateUpdateTime;
        SInt64			fConnectTime;
	UInt32	 		fSendCount;
	UInt32	 		fRetryCount;
	MP3BroadcasterSession*	fOwner;
	char			fHeader[kHeaderBufferSize];
	char			fHostName[kHostNameBufferSize];
	char			fUserAgent[kUserAgentBufferSize];
	char			fSongName[kSongNameBufferSize];
	OSMutex			fSongNameMutex;
	Bool16			fNewSongName;
	Bool16			fWasBlocked;
	SInt64			fBlockTime;
	Bool16			fNeedsContentLength;
	Bool16			fWantsMetaData;
	char			fRequestBuffer[kRequestBufferSize];
	QTSS_Object		fQTSSObject;
};

//
// MP3SessionRef -- This class is just a wrapper class for handling the
// mapping of RTSP Session refs to the corresponding MP3 Session class refs.
// It will be the an element of our hash table in the MP3SessionTable class.
//
class MP3SessionRef 
{
public:

                                MP3SessionRef(MP3Session* mp3Session);
					
                                ~MP3SessionRef();
	
private:
	
	MP3Session*		GetMP3Session() { return fMP3Session; }

	QTSS_RTSPSessionObject	GetSession() { return fMP3Session->GetSession(); }

	MP3SessionRef*		fNextHashEntry;
	SInt32			fHashValue;
	MP3Session*		fMP3Session;
	
	friend class MP3SessionRefKey;
	friend class OSHashTable<MP3SessionRef, MP3SessionRefKey>;
	friend class OSHashTableIter<MP3SessionRef, MP3SessionRefKey>;
	friend class MP3SessionTable;
};

//
// MP3SessionRefKey -- This class is used to generate hash keys for looking
// up values in our MP3SessionTable.
//
class MP3SessionRefKey 
{
public:

                                MP3SessionRefKey(MP3SessionRef* mp3SessRef);

                                MP3SessionRefKey(QTSS_RTSPSessionObject rtspSessRef);
					
                                ~MP3SessionRefKey();
	
private:
	
	SInt32			GetHashKey() { return fHashValue; }
	
	friend int operator ==(const MP3SessionRefKey &key1, const MP3SessionRefKey &key2)
	{
		return (key1.fHashValue == key2.fHashValue);
	}

	MP3SessionRef*		fKeyValue;
	SInt32			fHashValue;
	MP3Session*		fMP3Session;
	
	friend class OSHashTable<MP3SessionRef, MP3SessionRefKey>;
};

typedef OSHashTable<MP3SessionRef, MP3SessionRefKey> MP3SessRefHashTable;
typedef OSHashTableIter<MP3SessionRef, MP3SessionRefKey> MP3SessRefHashTableIter;

//
// MP3SessionTable -- This class provides a way to map RTSP Session references
// into the corresponding MP3Session class instances if any.
//
class MP3SessionTable 
{
public:

	enum
	{
		kDefaultTableSize = 19
	};
	
	// tableSize is the number of hash buckets not the number of entries.

                                MP3SessionTable(UInt32 tableSize = kDefaultTableSize);
					
                                ~MP3SessionTable();
	
	UInt32			GetNumSessionsInTable() { return fTable.GetNumEntries(); }
	
	// Attempt to add a new MP3Session ref to the table's map.
	// returns true on success or false if it fails.
	Bool16			RegisterSession(MP3Session* session);
	
	// Given an QTSS_RTSPSessionObject resolve it into a MP3Session class
	// reference. Returns NULL if there's none in out map.
	MP3Session*		Resolve(QTSS_RTSPSessionObject rtspSession);

	// Attempt to remove a  MP3Session ref from the table's map.
	// returns true on success or false if it fails.
	Bool16			UnRegisterSession(MP3Session* session);

private:
	
	MP3SessRefHashTable 	fTable;
	OSMutex			fMutex;
};

//
// MP3ClientQueue -- This is a class manages a queue of MP3Client objects.
// Every MP3BroadcasterSession class instance contains one of these objects.
//
class MP3ClientQueue 
{
public:

                                MP3ClientQueue();
					
                                ~MP3ClientQueue();
	
	UInt32			GetNumClients() { return fQueue.GetLength(); }

	QTSS_Error 		AddClient(QTSS_RTSPSessionObject clientsess, QTSS_StreamRef clientstream, 
                                            MP3BroadcasterSession* owner);

	QTSS_Error 		RemoveClient(QTSS_RTSPSessionObject clientsess);

	Bool16 			InQueue(QTSS_RTSPSessionObject clientsess);

	QTSS_Error 		SendToAllClients(char* buffer, UInt32 bufferlen);

	void	 		PreflightClients(char* songname);

private:
	
	void	 		TerminateClients();

	OSMutex			fMutex;
	OSQueue			fQueue;
};

//
// MP3BroadcasterQueue -- This is a class manages a queue of MP3Broadcaster objects.
// There is only one global instance of this class owned by the server.
//
class MP3BroadcasterQueue 
{
public:

                                MP3BroadcasterQueue();
					
                                ~MP3BroadcasterQueue();
	
	UInt32			GetNumBroadcasters() { return fQueue.GetLength(); }

	QTSS_Error 		CreateBroadcaster(QTSS_RTSPSessionObject bcastsess, QTSS_StreamRef bcaststream, StrPtrLen& mountpt);

	QTSS_Error 		RemoveBroadcaster(QTSS_RTSPSessionObject bcastsess);

	QTSS_Error 		RemoveClient(QTSS_RTSPSessionObject clientsess);

	Bool16 			InQueue(QTSS_RTSPSessionObject bcastsess);

	Bool16 			IsActiveClient(QTSS_RTSPSessionObject clientsess);

	MP3BroadcasterSession* 	FindByMountPoint(char* mountpoint);

	MP3BroadcasterSession* 	FindByMountPoint(StrPtrLen& mountpoint);

	MP3BroadcasterSession* 	FindBySession(QTSS_RTSPSessionObject bcastsess);

	void	 		TerminateAllBroadcastSessions();

private:
	
	OSMutex			fMutex;
	OSQueue			fQueue;
};

#endif // __QTSSMP3STREAMINGMODULE_H__

