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

	Contains:	Definitions of two classes: QTSSDictionary and QTSSDictionaryMap.
				Collectively, these classes implement the "dictionary" APIs in QTSS
				API. A QTSSDictionary corresponds to a QTSS_Object,
				a QTSSDictionaryMap corresponds to a QTSS_ObjectType.

	Created: Tue, Mar 2, 1999 @ 4:23 PM
*/



#ifndef _QTSSDICTIONARY_H_
#define _QTSSDICTIONARY_H_

#include <stdlib.h>

#include "QTSS.h"
#include "OSHeaders.h"
#include "OSMutex.h"
#include "StrPtrLen.h"
#include "MyAssert.h"
#include "QTSSStream.h"

class QTSSDictionary;
class QTSSDictionaryMap;
class QTSSAttrInfoDict;

#define __DICTIONARY_TESTING__ 0

//
// Function prototype for attr functions
typedef void* (*QTSS_AttrFunctionPtr)(QTSSDictionary* , UInt32* );

class QTSSDictionary : public QTSSStream
{
	public:
	
		//
		// CONSTRUCTOR / DESTRUCTOR
		
		QTSSDictionary(QTSSDictionaryMap* inMap, OSMutex* inMutex = NULL);
		virtual ~QTSSDictionary();
		
		//
		// QTSS API CALLS
		
		// Flags used by internal callers of these routines
		enum
		{
			kNoFlags = 0,
			kDontObeyReadOnly = 1,
			kDontCallCompletionRoutine = 2
		};
		
		// This version of GetValue copies the element into a buffer provided by the caller
		// Returns: 	QTSS_BadArgument, QTSS_NotPreemptiveSafe (if attribute is not preemptive safe),
		//				QTSS_BadIndex (if inIndex is bad)
		QTSS_Error GetValue(QTSS_AttributeID inAttrID, UInt32 inIndex, void* ioValueBuffer, UInt32* ioValueLen);


		//This version of GetValue returns a pointer to the internal buffer for the attribute.
		//Only usable if the attribute is preemptive safe.
		//
		// Returns: 	Same as above, but also QTSS_NotEnoughSpace, if value is too big for buffer.
		QTSS_Error GetValuePtr(QTSS_AttributeID inAttrID, UInt32 inIndex, void** outValueBuffer, UInt32* outValueLen)
						{ return GetValuePtr(inAttrID, inIndex, outValueBuffer, outValueLen, false); }
		
		// This version of GetValue converts the value to a string before returning it. Memory for
		// the string is allocated internally.
		//
		// Returns: QTSS_BadArgument, QTSS_BadIndex, QTSS_ValueNotFound
		QTSS_Error GetValueAsString(QTSS_AttributeID inAttrID, UInt32 inIndex, char** outString);
		
		// Returns:		QTSS_BadArgument, QTSS_ReadOnly (if attribute is read only),
		//				QTSS_BadIndex (attempt to set indexed parameter with param retrieval)
		QTSS_Error SetValue(QTSS_AttributeID inAttrID, UInt32 inIndex,
							const void* inBuffer,  UInt32 inLen, UInt32 inFlags = kNoFlags);
		
		// Returns:		QTSS_BadArgument, QTSS_ReadOnly (if attribute is read only),
		QTSS_Error SetValuePtr(QTSS_AttributeID inAttrID,
							const void* inBuffer,  UInt32 inLen, UInt32 inFlags = kNoFlags);
		
		// Returns:		QTSS_BadArgument, QTSS_ReadOnly (if attribute is read only),
		QTSS_Error CreateObjectValue(QTSS_AttributeID inAttrID, UInt32* outIndex,
										QTSSDictionary** newObject, QTSSDictionaryMap* inMap = NULL, 
										UInt32 inFlags = kNoFlags);
		
		// Returns:		QTSS_BadArgument, QTSS_ReadOnly, QTSS_BadIndex
		QTSS_Error RemoveValue(QTSS_AttributeID inAttrID, UInt32 inIndex, UInt32 inFlags = kNoFlags);
		
		// Utility routine used by the two external flavors of GetValue
		QTSS_Error GetValuePtr(QTSS_AttributeID inAttrID, UInt32 inIndex,
											void** outValueBuffer, UInt32* outValueLen,
											Bool16 isInternal);

		//
		// ACCESSORS
		
		QTSSDictionaryMap*	GetDictionaryMap() { return fMap; }
		
		// Returns the Instance dictionary map for this dictionary. This may return NULL
		// if there are no instance attributes in this dictionary
		QTSSDictionaryMap*	GetInstanceDictMap() { return fInstanceMap; }
		
		// Returns the number of values associated with a given attribute
		UInt32				GetNumValues(QTSS_AttributeID inAttrID);
		void				SetNumValues(QTSS_AttributeID inAttrID, UInt32 inNumValues);
		
		// Meant only for internal server use. Does no error checking,
		// doesn't invoke the param retrieval function.
		StrPtrLen*	GetValue(QTSS_AttributeID inAttrID) 
					{ 	return &fAttributes[inAttrID].fAttributeData;	}
					
		OSMutex*	GetMutex() { return fMutexP; }
		
		void		SetLocked(Bool16 inLocked) { fLocked = inLocked; }
		Bool16		IsLocked() { return fLocked; }

		//
		// GETTING ATTRIBUTE INFO
		QTSS_Error GetAttrInfoByIndex(UInt32 inIndex, QTSSAttrInfoDict** outAttrInfoDict);
		QTSS_Error GetAttrInfoByName(const char* inAttrName, QTSSAttrInfoDict** outAttrInfoDict);
		QTSS_Error GetAttrInfoByID(QTSS_AttributeID inAttrID, QTSSAttrInfoDict** outAttrInfoDict);
		

		//
		// INSTANCE ATTRIBUTES
		
		QTSS_Error	AddInstanceAttribute(	const char* inAttrName,
											QTSS_AttrFunctionPtr inFuncPtr,
											QTSS_AttrDataType inDataType,
											QTSS_AttrPermission inPermission );
											
		QTSS_Error	RemoveInstanceAttribute(QTSS_AttributeID inAttr);
		//
		// MODIFIERS

		// These functions are meant to be used by the server when it is setting up the
		// dictionary attributes. They do no error checking.

		// They don't set fNumAttributes & fAllocatedInternally.
		void	SetVal(QTSS_AttributeID inAttrID, void* inValueBuffer, UInt32 inBufferLen);
		void	SetVal(QTSS_AttributeID inAttrID, StrPtrLen* inNewValue)
					{ this->SetVal(inAttrID, inNewValue->Ptr, inNewValue->Len); }

		// Call this if you want to assign empty storage to an attribute
		void	SetEmptyVal(QTSS_AttributeID inAttrID, void* inBuf, UInt32 inBufLen);
		
#if __DICTIONARY_TESTING__
		static void	Test(); // API test for these objects
#endif

	protected:
	
		// Derived classes can provide a completion routine for some dictionary functions
		virtual void	RemoveValueComplete(UInt32 /*inAttrIndex*/, QTSSDictionaryMap* /*inMap*/, UInt32 /*inValueIndex*/) {}
		
		virtual void 	SetValueComplete(UInt32 /*inAttrIndex*/, QTSSDictionaryMap* /*inMap*/,
									UInt32 /*inValueIndex*/, const void* /*inNewValue*/, UInt32 /*inNewValueLen*/) {}
		virtual void	RemoveInstanceAttrComplete(UInt32 /*inAttrindex*/, QTSSDictionaryMap* /*inMap*/) {}
		
		virtual QTSSDictionary* CreateNewDictionary(QTSSDictionaryMap* inMap, OSMutex* inMutex);

	private:
	
		struct DictValueElement
		{
			// This stores all necessary information for each attribute value.
			
			DictValueElement() : 	fAllocatedLen(0), fNumAttributes(0),
									fAllocatedInternally(false), fIsDynamicDictionary(false) {}
									
			// Does not delete! You Must call DeleteAttributeData for that
			~DictValueElement() {}
			
			StrPtrLen	fAttributeData;	// The data
			UInt32		fAllocatedLen;	// How much space do we have allocated?
			UInt32		fNumAttributes;	// If this is an iterated attribute, how many?
			Bool16		fAllocatedInternally; //Should we delete this memory?
			Bool16		fIsDynamicDictionary; //is this a dictionary object?
		};
		
		DictValueElement*	fAttributes;
		DictValueElement*	fInstanceAttrs;
		UInt32				fInstanceArraySize;
		QTSSDictionaryMap* 	fMap;
		QTSSDictionaryMap* 	fInstanceMap;
		OSMutex*			fMutexP;
		Bool16				fMyMutex;
		Bool16				fLocked;
  		
		void DeleteAttributeData(DictValueElement* inDictValues, UInt32 inNumValues);
};


class QTSSAttrInfoDict : public QTSSDictionary
{
	public:
	
		struct AttrInfo
		{
			// This is all the relevent information for each dictionary
			// attribute.
			char 					fAttrName[QTSS_MAX_ATTRIBUTE_NAME_SIZE + 1];
			QTSS_AttrFunctionPtr 	fFuncPtr;
			QTSS_AttrDataType		fAttrDataType;
			QTSS_AttrPermission		fAttrPermission;
		};

		QTSSAttrInfoDict();
		virtual ~QTSSAttrInfoDict();
		
	private:
		
		AttrInfo fAttrInfo;
		QTSS_AttributeID fID;
		
		static AttrInfo	sAttributes[];
		
		friend class QTSSDictionaryMap;

};

class QTSSDictionaryMap
{
	public:
	
		//
		// This must be called before using any QTSSDictionary or QTSSDictionaryMap functionality
		static void Initialize();
		
		// Stores all meta-information for attributes
		
		// CONSTRUCTOR FLAGS
		enum
		{
			kNoFlags		= 0,
			kAllowRemoval 	= 1,
			kIsInstanceMap 	= 2,
			kInstanceAttrsAllowed = 4,
			kCompleteFunctionsAllowed = 8
		};
		
		//
		// CONSTRUCTOR / DESTRUCTOR
		
		QTSSDictionaryMap(UInt32 inNumReservedAttrs, UInt32 inFlags = kNoFlags);
		~QTSSDictionaryMap(){ delete fAttrArray; }

		//
		// QTSS API CALLS
				
		// All functions either return QTSS_BadArgument or QTSS_NoErr
		QTSS_Error 		AddAttribute(	const char* inAttrName,
										QTSS_AttrFunctionPtr inFuncPtr,
										QTSS_AttrDataType inDataType,
										QTSS_AttrPermission inPermission );
										
		//
		// Marks this attribute as removed
		QTSS_Error	RemoveAttribute(QTSS_AttributeID inAttrID);
		QTSS_Error	UnRemoveAttribute(QTSS_AttributeID inAttrID);

		//
		// Searching / Iteration. These never return removed attributes
		QTSS_Error	GetAttrInfoByName(const char* inAttrName, QTSSAttrInfoDict** outAttrInfoDict, Bool16 returnRemovedAttr = false);
		QTSS_Error	GetAttrInfoByID(QTSS_AttributeID inID, QTSSAttrInfoDict** outAttrInfoDict);
		QTSS_Error	GetAttrInfoByIndex(UInt32 inIndex, QTSSAttrInfoDict** outAttrInfoDict);
		QTSS_Error	GetAttrID(const char* inAttrName, QTSS_AttributeID* outID);
		
		//
		// PRIVATE ATTR PERMISSIONS
		enum
		{
			qtssPrivateAttrModeRemoved = 0x80000000
		};

		//
		// CONVERTING attribute IDs to array indexes. Returns -1 if inAttrID doesn't exist
		inline SInt32					ConvertAttrIDToArrayIndex(QTSS_AttributeID inAttrID);

		static Bool16			IsInstanceAttrID(QTSS_AttributeID inAttrID)
			{ return (inAttrID & 0x80000000) != 0; }

		// ACCESSORS
		
		// These functions do no error checking. Be careful.
		
		// Includes removed attributes
		UInt32			GetNumAttrs() 			{ return fNextAvailableID; }
		UInt32			GetNumNonRemovedAttrs() { return fNumValidAttrs; }
		
		Bool16					IsPreemptiveSafe(UInt32 inIndex) 
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssAttrModePreempSafe; }

		Bool16					IsWriteable(UInt32 inIndex) 
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssAttrModeWrite; }

		Bool16					IsRemoved(UInt32 inIndex) 
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved; }

		QTSS_AttrFunctionPtr	GetAttrFunction(UInt32 inIndex)
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fFuncPtr; }
			
		char*					GetAttrName(UInt32 inIndex)
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrName; }
			
		QTSS_AttributeID		GetAttrID(UInt32 inIndex)
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fID; }

		QTSS_AttrDataType		GetAttrType(UInt32 inIndex)
			{ Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrDataType; }
		
		Bool16					InstanceAttrsAllowed() { return fFlags & kInstanceAttrsAllowed; }
		Bool16					CompleteFunctionsAllowed() { return fFlags & kCompleteFunctionsAllowed; }

		// MODIFIERS
		
		// Sets this attribute ID to have this information
		
		void 		SetAttribute(	QTSS_AttributeID inID, 
									const char* inAttrName,
									QTSS_AttrFunctionPtr inFuncPtr,
									QTSS_AttrDataType inDataType,
									QTSS_AttrPermission inPermission );

		
		//
		// DICTIONARY MAPS
		
		// All dictionary maps are stored here, and are accessable
		// through these routines
		
		// This enum allows all QTSSDictionaryMaps to be stored in an array	
		enum
		{
			kServerDictIndex 				= 0,
			kPrefsDictIndex 				= 1,
			kTextMessagesDictIndex 			= 2,
			kServiceDictIndex 				= 3,
			
			kRTPStreamDictIndex 			= 4,
			kClientSessionDictIndex	 		= 5,
			kRTSPSessionDictIndex 			= 6,
			kRTSPRequestDictIndex 			= 7,
			kRTSPHeaderDictIndex 			= 8,
			kFileDictIndex		 			= 9,
			kModuleDictIndex	 			= 10,
			kModulePrefsDictIndex			= 11,
			kAttrInfoDictIndex				= 12,
			kQTSSUserProfileDictIndex		= 13,
			kQTSSConnectedUserDictIndex		= 14,

			kNumDictionaries				= 15,
			
			kNumDynamicDictionaryTypes		= 500,
			kIllegalDictionary				= kNumDynamicDictionaryTypes + kNumDictionaries
		};
		
		// This function converts a QTSS_ObjectType to an index
		static UInt32					GetMapIndex(QTSS_ObjectType inType);

		// Using one of the above predefined indexes, this returns the corresponding map
		static QTSSDictionaryMap*		GetMap(UInt32 inIndex)
			{ Assert(inIndex < kNumDynamicDictionaryTypes + kNumDictionaries); return sDictionaryMaps[inIndex]; }

		static QTSS_ObjectType			CreateNewMap();

	private:

		//
		// Repository for dictionary maps
		
		static QTSSDictionaryMap*		sDictionaryMaps[kNumDictionaries + kNumDynamicDictionaryTypes];
		static UInt32					sNextDynamicMap;
			
		enum
		{
			kMinArraySize = 20
		};

		UInt32 							fNextAvailableID;
		UInt32 							fNumValidAttrs;
		UInt32 							fAttrArraySize;
		QTSSAttrInfoDict**				fAttrArray;
		UInt32							fFlags;
		
		friend class QTSSDictionary;
};

inline SInt32	QTSSDictionaryMap::ConvertAttrIDToArrayIndex(QTSS_AttributeID inAttrID)
{
	SInt32 theIndex = inAttrID & 0x7FFFFFFF;
	if ((theIndex < 0) || (theIndex >= (SInt32)fNextAvailableID))
		return -1;
	else
		return theIndex;
}


#endif
