/*
 *
 * @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:		QTSSPrefs.cpp

	Contains:	Implements class defined in QTSSPrefs.h.

	Change History (most recent first):
	
*/

#include "QTSSPrefs.h"
#include "MyAssert.h"
#include "OSMemory.h"
#include "QTSSDataConverter.h"
#include "OSArrayObjectDeleter.h"
 

QTSSPrefs::QTSSPrefs(XMLPrefsParser* inPrefsSource, StrPtrLen* inModuleName, QTSSDictionaryMap* inMap,
						Bool16 areInstanceAttrsAllowed, QTSSPrefs* parentDictionary )
:	QTSSDictionary(inMap, &fPrefsMutex),
	fPrefsSource(inPrefsSource),
	fPrefName(NULL),
	fParentDictionary(parentDictionary)
{
	if (inModuleName != NULL)
		fPrefName = inModuleName->GetAsCString();
}

QTSSDictionary* QTSSPrefs::CreateNewDictionary(QTSSDictionaryMap* inMap, OSMutex* /* inMutex */)
{
	return NEW QTSSPrefs(fPrefsSource, NULL, inMap, true, this );
}

void QTSSPrefs::RereadPreferences()
{
	RereadObjectPreferences(GetContainerRef());
}
	
void QTSSPrefs::RereadObjectPreferences(ContainerRef container)
{
	QTSS_Error theErr = QTSS_NoErr;
	
	//
	// Keep track of which pref attributes should remain. All others
	// will be removed.
	// This routine uses names because it adds and deletes attributes. This means attribute indexes,positions and counts are constantly changing.
	UInt32 initialNumAttrs = 0;
	if (this->GetInstanceDictMap() != NULL)
	{	initialNumAttrs = this->GetInstanceDictMap()->GetNumAttrs();
	};
	char** modulePrefInServer = NEW char*[initialNumAttrs ];
	::memset(modulePrefInServer, 0, sizeof(char*) * initialNumAttrs);
	
	OSMutexLocker locker(&fPrefsMutex);
	UInt32 theNumPrefs = fPrefsSource->GetNumPrefsByContainer(container);

	for (UInt32 i = 0; i < initialNumAttrs;i++) // pull out all the names in the server 
	{	
		QTSSAttrInfoDict* theAttrInfoPtr = NULL;
		theErr = this->GetInstanceDictMap()->GetAttrInfoByIndex(i, &theAttrInfoPtr);
		if (theErr != QTSS_NoErr)
			continue;
		
		UInt32 nameLen = 0;
		theErr = theAttrInfoPtr->GetValuePtr(qtssAttrName,0, (void **) &modulePrefInServer[i], &nameLen);
		Assert(theErr == QTSS_NoErr);
		//printf("QTSSPrefs::RereadPreferences modulePrefInServer in server=%s\n",modulePrefInServer[i]);
	}
	
	// Use the names of the attributes in the attribute map as the key values for
	// finding preferences in the config file.
	
	for (UInt32 x = 0; x < theNumPrefs; x++)
	{
		char* thePrefTypeStr = NULL;
		char* thePrefName = NULL;
		(void)fPrefsSource->GetPrefValueByIndex(container, x, 0, &thePrefName, &thePrefTypeStr);

		// What type is this data type?
		QTSS_AttrDataType thePrefType = QTSSDataConverter::TypeStringToType(thePrefTypeStr);

		//
		// Check to see if there is an attribute with this name already in the
		// instance map. If one matches, then we don't need to add this attribute.
		QTSSAttrInfoDict* theAttrInfo = NULL;
		if (this->GetInstanceDictMap() != NULL)
			(void)this->GetInstanceDictMap()->GetAttrInfoByName(thePrefName,
																&theAttrInfo,
																false ); // false=don't return info on deleted attributes
		UInt32 theLen = sizeof(QTSS_AttrDataType);
		QTSS_AttributeID theAttrID = qtssIllegalAttrID;
		
		for (UInt32 i = 0; i < initialNumAttrs;i++) // see if this name is in the server
		{	if (modulePrefInServer[i] != NULL && thePrefName != NULL && 0 == ::strcmp(modulePrefInServer[i],thePrefName))
			{	modulePrefInServer[i] = NULL; // in the server so don't delete later
				//printf("QTSSPrefs::RereadPreferences modulePrefInServer in file and in server=%s\n",thePrefName);
			}
		}

		if ( theAttrInfo == NULL )
		{
			theAttrID = this->AddPrefAttribute(thePrefName, thePrefType); // not present or deleted
			this->SetPrefValuesFromFile(container, x, theAttrID, 0); // will add another or replace a deleted attribute
		}
		else
		{
			QTSS_AttrDataType theAttrType = qtssAttrDataTypeUnknown;
			theErr = theAttrInfo->GetValue(qtssAttrDataType, 0, &theAttrType, &theLen);
			Assert(theErr == QTSS_NoErr);
			
			theLen = sizeof(theAttrID);
			theErr = theAttrInfo->GetValue(qtssAttrID, 0, &theAttrID, &theLen);
			Assert(theErr == QTSS_NoErr);

			if (theAttrType != thePrefType)
			{
				//
				// This is not the same pref as before, because the data types
				// are different. Remove the old one from the map, add the new one.
				(void)this->RemoveInstanceAttribute(theAttrID);
				theAttrID = this->AddPrefAttribute(thePrefName, thePrefType);
			}
			else
			{
				//
				// This pref already exists
			}
			
			//
			// Set the values
			this->SetPrefValuesFromFile(container, x, theAttrID, 0);

			// Mark this pref as found.
			SInt32 theIndex = this->GetInstanceDictMap()->ConvertAttrIDToArrayIndex(theAttrID);
			Assert(theIndex >= 0);
		}
	}
	
	// Remove all attributes that no longer apply
	if (this->GetInstanceDictMap() != NULL && initialNumAttrs > 0)
	{	
		for (UInt32 a = 0; a < initialNumAttrs; a++) 
		{
			if (NULL != modulePrefInServer[a]) // found a pref in the server that wasn't in the file
			{	
				QTSSAttrInfoDict* theAttrInfoPtr = NULL;
				theErr = this->GetInstanceDictMap()->GetAttrInfoByName(modulePrefInServer[a], &theAttrInfoPtr);
				Assert(theErr == QTSS_NoErr);
				if (theErr != QTSS_NoErr) continue;
		
				QTSS_AttributeID theAttrID = qtssIllegalAttrID;	
				UInt32 theLen = sizeof(theAttrID);
				theErr = theAttrInfoPtr->GetValue(qtssAttrID, 0, &theAttrID, &theLen);
				Assert(theErr == QTSS_NoErr);
				if (theErr != QTSS_NoErr) continue;
							
				if (0)
				{	char* theName = NULL;
					UInt32 nameLen = 0;
					theAttrInfoPtr->GetValuePtr(qtssAttrName,0, (void **) &theName, &nameLen);
					printf("QTSSPrefs::RereadPreferences about to delete modulePrefInServer=%s attr=%s id=%lu\n",modulePrefInServer[a], theName,theAttrID);
				}
			
			
				this->GetInstanceDictMap()->RemoveAttribute(theAttrID);
				modulePrefInServer[a] = NULL;
			}
		}
	}
	
	delete modulePrefInServer;
}
		
void QTSSPrefs::SetPrefValuesFromFile(ContainerRef container, UInt32 inPrefIndex, QTSS_AttributeID inAttrID, UInt32 inNumValues)
{
	ContainerRef pref = fPrefsSource->GetPrefRefByIndex(container, inPrefIndex);
	SetPrefValuesFromFileWithRef(pref, inAttrID, inNumValues);
}
		
void QTSSPrefs::SetPrefValuesFromFileWithRef(ContainerRef pref, QTSS_AttributeID inAttrID, UInt32 inNumValues)
{
	//
	// We have an attribute ID for this pref, it is in the map and everything.
	// Now, let's add all the values that are in the pref file.
	if (pref == 0)
		return;
	
	UInt32 numPrefValues = inNumValues;
	if (inNumValues == 0)
		numPrefValues = fPrefsSource->GetNumPrefValues(pref);
		
	char* thePrefName = NULL;
	char* thePrefValue = NULL;
	char* thePrefTypeStr = NULL;
	QTSS_AttrDataType thePrefType = qtssAttrDataTypeUnknown;
	
	// find the type.  If this is a QTSSObject, then we need to call a different routine
	thePrefValue = fPrefsSource->GetPrefValueByRef(	pref, 0, &thePrefName, &thePrefTypeStr);
	thePrefType = QTSSDataConverter::TypeStringToType(thePrefTypeStr);
	if (thePrefType == qtssAttrDataTypeQTSS_Object)
	{
		SetObjectValuesFromFile(pref, inAttrID, numPrefValues, thePrefName);
		return;
	}

	UInt32 maxPrefValueSize = 0;
	QTSS_Error theErr = QTSS_NoErr;
	
	//
	// We have to loop through all the values associated with this pref twice:
	// first, to figure out the length (in bytes) of the longest value, secondly
	// to actually copy these values into the dictionary.
	for (UInt32 y = 0; y < numPrefValues; y++)
	{
		UInt32 tempMaxPrefValueSize = 0;
		
		thePrefValue = fPrefsSource->GetPrefValueByRef(	pref, y, &thePrefName, &thePrefTypeStr);

		theErr = QTSSDataConverter::StringToValue( 	thePrefValue, thePrefType,
													NULL, &tempMaxPrefValueSize );
		Assert(theErr == QTSS_NotEnoughSpace);
		
		if (tempMaxPrefValueSize > maxPrefValueSize)
			maxPrefValueSize = tempMaxPrefValueSize;
	}

	
	for (UInt32 z = 0; z < numPrefValues; z++)
	{
		thePrefValue = fPrefsSource->GetPrefValueByRef( pref, z, &thePrefName, &thePrefTypeStr);
		this->SetPrefValue(inAttrID, z, thePrefValue, thePrefType, maxPrefValueSize);
	}
	
	//
	// Make sure the dictionary knows exactly how many values are associated with
	// this pref
	this->SetNumValues(inAttrID, numPrefValues);
}
		
void QTSSPrefs::SetObjectValuesFromFile(ContainerRef pref, QTSS_AttributeID inAttrID, UInt32 inNumValues, char* prefName)
{
	for (UInt32 z = 0; z < inNumValues; z++)
	{
		ContainerRef object = fPrefsSource->GetObjectValue( pref, z );
		QTSSPrefs* prefObject;
		UInt32 len = sizeof(QTSSPrefs*);
		QTSS_Error err = this->GetValue(inAttrID, z, &prefObject, &len);
		if (err != QTSS_NoErr)
		{
			UInt32 tempIndex;
			err = CreateObjectValue(inAttrID, &tempIndex, (QTSSDictionary**)&prefObject, NULL, QTSSDictionary::kDontObeyReadOnly | QTSSDictionary::kDontCallCompletionRoutine);
			Assert(err == QTSS_NoErr);
			Assert(tempIndex == z);
			if (err != QTSS_NoErr)	// this shouldn't happen
				return;
			StrPtrLen temp(prefName);
			prefObject->fPrefName = temp.GetAsCString();
		}
		prefObject->RereadObjectPreferences(object);
	}
	
	//
	// Make sure the dictionary knows exactly how many values are associated with
	// this pref
	this->SetNumValues(inAttrID, inNumValues);
}

void	QTSSPrefs::SetPrefValue(QTSS_AttributeID inAttrID, UInt32 inAttrIndex,
								const char* inPrefValue, QTSS_AttrDataType inPrefType, UInt32 inValueSize)
						
{
	static const UInt32 kMaxPrefValueSize = 1024;
	char convertedPrefValue[kMaxPrefValueSize];
	::memset(convertedPrefValue, 0, kMaxPrefValueSize);
	Assert(inValueSize < kMaxPrefValueSize);
	
	UInt32 convertedBufSize = kMaxPrefValueSize;
	QTSS_Error theErr = QTSSDataConverter::StringToValue
		(inPrefValue, inPrefType, convertedPrefValue, &convertedBufSize );
	Assert(theErr == QTSS_NoErr);
	
	if (inValueSize == 0)
		inValueSize = convertedBufSize;
		
	this->SetValue(inAttrID, inAttrIndex, convertedPrefValue, inValueSize, QTSSDictionary::kDontObeyReadOnly | QTSSDictionary::kDontCallCompletionRoutine);							

}

QTSS_AttributeID QTSSPrefs::AddPrefAttribute(const char* inAttrName, QTSS_AttrDataType inDataType)
{
	QTSS_Error theErr = this->AddInstanceAttribute( inAttrName, NULL, inDataType, qtssAttrModeRead | qtssAttrModeWrite);
	Assert(theErr == QTSS_NoErr);
	
	QTSS_AttributeID theID = qtssIllegalAttrID;
	theErr = this->GetInstanceDictMap()->GetAttrID( inAttrName, &theID);
	Assert(theErr == QTSS_NoErr);
	
	return theID;
}

void	QTSSPrefs::RemoveValueComplete(UInt32 inAttrIndex, QTSSDictionaryMap* inMap,
										UInt32 inValueIndex)
{
	ContainerRef objectRef = GetContainerRef();
	ContainerRef pref = fPrefsSource->GetPrefRefByName( objectRef, inMap->GetAttrName(inAttrIndex));
	Assert(pref != NULL);
	if (pref != NULL)
		fPrefsSource->RemovePrefValue( pref, inValueIndex);
	
	if (fPrefsSource->WritePrefsFile())
		QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgCantWriteFile, 0);
}

void	QTSSPrefs::RemoveInstanceAttrComplete(UInt32 inAttrIndex, QTSSDictionaryMap* inMap)
{
	ContainerRef objectRef = GetContainerRef();
	ContainerRef pref = fPrefsSource->GetPrefRefByName( objectRef, inMap->GetAttrName(inAttrIndex));
	Assert(pref != NULL);
	if (pref != NULL)
	{
		fPrefsSource->RemovePref(pref);
	}
	
	if (fPrefsSource->WritePrefsFile())
		QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgCantWriteFile, 0);
}

void QTSSPrefs::SetValueComplete(UInt32 inAttrIndex, QTSSDictionaryMap* inMap,
									UInt32 inValueIndex, const void* inNewValue, UInt32 inNewValueLen)
{
	ContainerRef objectRef = GetContainerRef();
	ContainerRef pref = fPrefsSource->AddPref(objectRef, inMap->GetAttrName(inAttrIndex), QTSSDataConverter::TypeToTypeString(inMap->GetAttrType(inAttrIndex)));
	if (inMap->GetAttrType(inAttrIndex) == qtssAttrDataTypeQTSS_Object)
	{
		QTSSPrefs* object = *(QTSSPrefs**)inNewValue;	// value is a pointer to a QTSSPrefs object
		StrPtrLen temp(inMap->GetAttrName(inAttrIndex));
		object->fPrefName = temp.GetAsCString();
		if (inValueIndex == fPrefsSource->GetNumPrefValues(pref))
			fPrefsSource->AddNewObject(pref);
	}
	else
	{
		OSCharArrayDeleter theValueAsString(QTSSDataConverter::ValueToString(inNewValue, inNewValueLen, inMap->GetAttrType(inAttrIndex)));
		fPrefsSource->SetPrefValue(pref, inValueIndex, theValueAsString.GetObject());
	}

	if (fPrefsSource->WritePrefsFile())
		QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgCantWriteFile, 0);
}

ContainerRef QTSSPrefs::GetContainerRefForObject(QTSSPrefs* object)
{
	ContainerRef thisContainer = GetContainerRef();
	ContainerRef pref = fPrefsSource->GetPrefRefByName(thisContainer, object->fPrefName);
	if (pref == NULL)
		return NULL;
		
	if (fPrefsSource->GetNumPrefValues(pref) <= 1)
		return fPrefsSource->GetObjectValue(pref, 0);
	
	QTSSAttrInfoDict* theAttrInfoPtr = NULL;
	QTSS_Error theErr = this->GetInstanceDictMap()->GetAttrInfoByName(object->fPrefName, &theAttrInfoPtr);
	Assert(theErr == QTSS_NoErr);
	if (theErr != QTSS_NoErr) return NULL;
	QTSS_AttributeID theAttrID = qtssIllegalAttrID;
	UInt32 len = sizeof(theAttrID);
	theErr = theAttrInfoPtr->GetValue(qtssAttrID, 0, &theAttrID, &len);
	Assert(theErr == QTSS_NoErr);
	if (theErr != QTSS_NoErr) return NULL;

	UInt32 index = 0;
	QTSSPrefs* prefObject;
	len = sizeof(prefObject);
	while (this->GetValue(theAttrID, index, &prefObject, &len) == QTSS_NoErr)
	{
		if (prefObject == object)
		{
			return fPrefsSource->GetObjectValue(pref, index);
		}
	}
	
	return NULL;
}

ContainerRef QTSSPrefs::GetContainerRef()
{
	if (fParentDictionary == NULL)	// this is a top level Pref, so it must be a module
		return fPrefsSource->GetRefForModule(fPrefName);
	else	
		return fParentDictionary->GetContainerRefForObject(this);
}
