//	Zinc Application Framework - Z_PRST1.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_win.hpp>
#include <z_string.hpp>
#if defined(ZAF_PERSIST_ALL)
#	include <zaf.hpp>
#endif
#define ZAF_OBJECT_PERSISTENCE_INFO
#include "gbl_def.cpp"

#if defined(USE_ZAF_PERSISTENCE)
// ----- ZafObjectPersistence -----------------------------------------------

ZafObjectPersistence::ZafObjectPersistence(ZafFileSystem *tFileSystem, DataConstructor *tDataConstructor,
	ObjectConstructor *tObjectConstructor, UserFunction *tUserFunction,
	CompareFunction *tCompareFunction, UserObject *tUserObject) :
	ZafDataPersistence(tFileSystem, tDataConstructor),
	objectConstructor(ZAF_NULLP(ObjectConstructor)),
	compareFunction(ZAF_NULLP(CompareFunction)),
	userFunction(ZAF_NULLP(UserFunction)),
	userObject(ZAF_NULLP(UserObject))
{
	SetObjectConstructors(tObjectConstructor);
	SetCompareFunctions(tCompareFunction);
	SetUserCallbacks(tUserFunction);
	SetUserObjects(tUserObject);
}

ZafObjectPersistence::ZafObjectPersistence(const ZafObjectPersistence &copy) :
	ZafDataPersistence(copy),
	objectConstructor(ZAF_NULLP(ObjectConstructor)),
	compareFunction(ZAF_NULLP(CompareFunction)),
	userFunction(ZAF_NULLP(UserFunction)),
	userObject(ZAF_NULLP(UserObject))
{
	SetObjectConstructors(copy.objectConstructor);
	SetCompareFunctions(copy.compareFunction);
	SetUserCallbacks(copy.userFunction);
	SetUserObjects(copy.userObject);
}

ZafObjectPersistence::~ZafObjectPersistence(void)
{
	// Clear the tables.
	ClearObjectConstructors();
	ClearCompareFunctions();
	ClearUserCallbacks();
	ClearUserObjects();
}

ZafObjectPersistence::CompareFunction *ZafObjectPersistence::AddCompareFunction(
	ZafDataName name, ZafCompareFunction function)
{
	// Check for an existing entry.
	int offset = 0;
	if (compareFunction)
	{
		// Try to find a matching compare function.
		for ( ; compareFunction[offset].name; offset++)
			if (!streq(name, compareFunction[offset].name))
			{
				// Already in there.  Return the entry.  Do NOT write over!
				return (&compareFunction[offset]);
			}

		// Reallocate the array.
		if (((offset + 1) % ZAF_DYNAMIC_ARRAY_INCREMENT) == 0)
		{
			CompareFunction *tempArray = compareFunction;
			compareFunction = new CompareFunction[offset + ZAF_DYNAMIC_ARRAY_INCREMENT + 1];
			memcpy(compareFunction, tempArray, offset * sizeof(CompareFunction));
			delete []tempArray;
		}
	}
	else
		compareFunction = new CompareFunction[ZAF_DYNAMIC_ARRAY_INCREMENT];

	// Set the compare function.
	compareFunction[offset].name = strdup(name);
	compareFunction[offset].function = function;

	// Set the end-of-array.
	offset++;
	compareFunction[offset].name = ZAF_NULLP(ZafDataNameChar);
	compareFunction[offset].function = ZAF_NULLF(ZafCompareFunction);

	// Return the compare function.
	return (&compareFunction[offset-1]);
}

ZafObjectPersistence::ObjectConstructor *ZafObjectPersistence::AddObjectConstructor(
	ZafClassName className, ZafClassID classID, ZafObjectConstructor constructor)
{
	// Check for an existing entry.
	int offset = 0;
	if (objectConstructor)
	{
		// Try to find a matching class.
		for ( ; objectConstructor[offset].classID != ID_END; offset++)
			if (!streq(className, objectConstructor[offset].className))
			{
				// Already in there.  Return the entry.  Do NOT write over!
				return (&objectConstructor[offset]);
			}

		// Reallocate the array.
		if (((offset + 1) % ZAF_DYNAMIC_ARRAY_INCREMENT) == 0)
		{
			ObjectConstructor *tempArray = objectConstructor;
			objectConstructor = new ObjectConstructor[offset + ZAF_DYNAMIC_ARRAY_INCREMENT + 1];
			for (int i = 0; i < offset; i++)
			{
				objectConstructor[i].classID = tempArray[i].classID;
				objectConstructor[i].className = tempArray[i].className;
				objectConstructor[i].constructor = tempArray[i].constructor;
			}
			delete []tempArray;
		}
	}
	else
		objectConstructor = new ObjectConstructor[ZAF_DYNAMIC_ARRAY_INCREMENT];

	// Set the read function.
	objectConstructor[offset].classID = classID;
	objectConstructor[offset].className = strdup(className);
	objectConstructor[offset].constructor = constructor;

	// Set the end-of-array.
	offset++;
	objectConstructor[offset].classID = ID_END;
	objectConstructor[offset].className = ZAF_NULLP(ZafClassNameChar);
	objectConstructor[offset].constructor = ZAF_NULLF(ZafObjectConstructor);

	// Return the read function.
	return (&objectConstructor[offset-1]);
}

ZafObjectPersistence::UserFunction *ZafObjectPersistence::AddUserCallback(
	ZafDataName name, ZafUserFunction function)
{
	// Check for an existing entry.
	int offset = 0;
	if (userFunction)
	{
		// Try to find a matching class.
		for ( ; userFunction[offset].name; offset++)
			if (!streq(name, userFunction[offset].name))
			{
				// Already in there.  Return the entry.  Do NOT write over!
				return (&userFunction[offset]);
			}

		// Reallocate the array.
		if (((offset + 1) % ZAF_DYNAMIC_ARRAY_INCREMENT) == 0)
		{
			UserFunction *tempArray = userFunction;
			userFunction = new UserFunction[offset + ZAF_DYNAMIC_ARRAY_INCREMENT + 1];
			memcpy(userFunction, tempArray, offset * sizeof(UserFunction));
			delete []tempArray;
		}
	}
	else
		userFunction = new UserFunction[ZAF_DYNAMIC_ARRAY_INCREMENT];

	// Set the user function.
	userFunction[offset].name = strdup(name);
	userFunction[offset].function = function;

	// Set the end-of-array.
	offset++;
	userFunction[offset].name = ZAF_NULLP(ZafDataNameChar);
	userFunction[offset].function = ZAF_NULLF(ZafUserFunction);

	// Return the user function.
	return (&userFunction[offset-1]);
}

ZafObjectPersistence::UserObject *ZafObjectPersistence::AddUserObject(
	ZafDataName name, void *object)
{
	// Check for an existing entry.
	int offset = 0;
	if (userObject)
	{
		// Try to find a matching class.
		for ( ; userObject[offset].name; offset++)
			if (!streq(name, userObject[offset].name))
			{
				// Already in there.  Return the entry.  Do NOT write over!
				return (&userObject[offset]);
			}

		// Reallocate the array.
		if (((offset + 1) % ZAF_DYNAMIC_ARRAY_INCREMENT) == 0)
		{
			UserObject *tempArray = userObject;
			userObject = new UserObject[offset + ZAF_DYNAMIC_ARRAY_INCREMENT + 1];
			memcpy(userObject, tempArray, offset * sizeof(UserObject));
			delete []tempArray;
		}
	}
	else
		userObject = new UserObject[ZAF_DYNAMIC_ARRAY_INCREMENT];

	// Set the user object.
	userObject[offset].name = strdup(name);
	userObject[offset].object = object;

	// Set the end-of-array.
	offset++;
	userObject[offset].name = ZAF_NULLP(ZafDataNameChar);
	userObject[offset].object = ZAF_NULLP(void);

	// Return the user object.
	return (&userObject[offset-1]);
}

void ZafObjectPersistence::ClearCompareFunctions(void)
{
	// Check for a valid persist table.
	if (compareFunction)
	{
		// Remove the compare function names.
		for (int i = 0; compareFunction[i].name; i++)
			delete [](ZafIChar *)compareFunction[i].name;

		// Delete the array.
		delete []compareFunction;
		compareFunction = ZAF_NULLP(CompareFunction);
	}
}

void ZafObjectPersistence::ClearObjectConstructors(void)
{
	// Check for a valid persist table.
	if (objectConstructor)
	{
		// Remove the object constructor names.
		for (int i = 0; objectConstructor[i].classID != ID_END; i++)
			delete [](ZafIChar *)objectConstructor[i].className;

		// Delete the array.
		delete []objectConstructor;
		objectConstructor = ZAF_NULLP(ObjectConstructor);
	}
}

void ZafObjectPersistence::ClearUserCallbacks(void)
{
	// Check for a valid persist table.
	if (userFunction)
	{
		// Remove the user function names.
		for (int i = 0; userFunction[i].name; i++)
			delete [](ZafIChar *)userFunction[i].name;

		// Delete the array.
		delete []userFunction;
		userFunction = ZAF_NULLP(UserFunction);
	}
}

void ZafObjectPersistence::ClearUserObjects(void)
{
	// Check for a valid persist table.
	if (userObject)
	{
		// Remove the user object names.
		for (int i = 0; userObject[i].name; i++)
			delete [](ZafIChar *)userObject[i].name;

		// Delete the array.
		delete []userObject;
		userObject = ZAF_NULLP(UserObject);
	}
}

ZafClassID ZafObjectPersistence::CurrentClassID(void)
{
	// Check for a valid level.
	if (currentEntry < 0)
		return (ID_END);

	// Find a valid entry.
	ZafClassID matchID = entry[currentEntry].classID;
	if (!matchID && entry[currentEntry].className)
	{
		// First, try the object table.
		for (int i = 0; objectConstructor[i].classID != ID_END; i++)
			if (!streq(objectConstructor[i].className, entry[currentEntry].className))
			{
				matchID = objectConstructor[i].classID;
				break;
			}

		// Next, try the data table.
		if (!matchID)
			matchID = ZafDataPersistence::CurrentClassID();
	}

	// Return the class name.
	return (matchID);
}

ZafClassName ZafObjectPersistence::CurrentClassName(void)
{
	// Check for a valid level.
	if (currentEntry < 0)
		return (ZAF_NULLP(ZafClassNameChar));

	// Find a valid entry.
	ZafClassName matchName = entry[currentEntry].className;
	if (!matchName && entry[currentEntry].classID)
	{
		// First, try the object table.
		for (int i = 0; objectConstructor[i].classID != ID_END; i++)
			if (objectConstructor[i].classID == entry[currentEntry].classID)
			{
				matchName = objectConstructor[i].className;
				break;
			}

		// Next, try the data table.
		if (!matchName)
			matchName = ZafDataPersistence::CurrentClassName();
	}

	// Return the class name.
	return (matchName);
}

ZafCompareFunction ZafObjectPersistence::GetCompareFunction(ZafDataName name)
{
	// Check for a valid persist table.
	if (compareFunction)
	{
		// Try to find a matching entry.
		for (int i = 0; compareFunction[i].name; i++)
			if (name && !streq(name, compareFunction[i].name))
				return (compareFunction[i].function);
	}

	// Compare function not found.
	return (ZAF_NULLF(ZafCompareFunction));
}

ZafDataName ZafObjectPersistence::GetCompareFunctionName(ZafCompareFunction function)
{
	// Check for a valid persist table.
	if (compareFunction && function)
	{
		// Try to find a matching entry.
		for (int i = 0; compareFunction[i].name; i++)
			if (function == compareFunction[i].function)
				return (compareFunction[i].name);
	}

	// Compare function name not found.
	return (ZAF_NULLP(ZafDataNameChar));
}

ZafDataName ZafObjectPersistence::GetClassName(ZafClassID classID)
{
	// Check for a valid object table.
	if (objectConstructor)
	{
		// Try to find a matching entry.
		for (int i = 0; objectConstructor[i].className; i++)
			if (classID == objectConstructor[i].classID)
				return (objectConstructor[i].className);
	}

	// Check for a valid data table.
	if (dataConstructor)
	{
		// Try to find a matching entry.
		for (int i = 0; dataConstructor[i].className; i++)
			if (classID == dataConstructor[i].classID)
				return (dataConstructor[i].className);
	}

	// Class name not found.
	return (ZAF_NULLP(ZafDataNameChar));
}

ZafObjectConstructor ZafObjectPersistence::GetObjectConstructor(ZafClassID classID, ZafClassName className)
{
	// Check for a valid persist table.
	if (objectConstructor)
	{
		// Try to find a matching entry.
		for (int i = 0; objectConstructor[i].classID != ID_END; i++)
			if ((classID && classID == objectConstructor[i].classID) ||
				(className && !streq(className, objectConstructor[i].className)))
				return (objectConstructor[i].constructor);
	}

	// Read function not found.
	return (ZAF_NULLF(ZafObjectConstructor));
}

ZafUserFunction ZafObjectPersistence::GetUserCallback(ZafDataName name)
{
	// Check for a valid persist table.
	if (userFunction)
	{
		// Try to find a matching entry.
		for (int i = 0; userFunction[i].name; i++)
			if (name && !streq(name, userFunction[i].name))
				return (userFunction[i].function);
	}

	// User function not found.
	return (ZAF_NULLF(ZafUserFunction));
}

void *ZafObjectPersistence::GetUserObject(ZafDataName name)
{
	// Check for a valid persist table.
	if (userObject)
	{
		// Try to find a matching entry.
		for (int i = 0; userObject[i].name; i++)
			if (name && !streq(name, userObject[i].name))
				return (userObject[i].object);
	}

	// User object not found.
	return (ZAF_NULLP(void));
}

ZafDataName ZafObjectPersistence::GetUserCallbackName(ZafUserFunction function)
{
	// Check for a valid persist table.
	if (userFunction && function)
	{
		// Try to find a matching entry.
		for (int i = 0; userFunction[i].name; i++)
			if (function == userFunction[i].function)
				return (userFunction[i].name);
	}

	// User function name not found.
	return (ZAF_NULLP(ZafDataNameChar));
}

ZafDataName ZafObjectPersistence::GetUserObjectName(void *tUserObject)
{
	// Check for a valid persist table.
	if (userObject && tUserObject)
	{
		// Try to find a matching entry.
		for (int i = 0; userObject[i].name; i++)
			if (tUserObject == userObject[i].object)
				return (userObject[i].name);
	}

	// User object name not found.
	return (ZAF_NULLP(ZafDataNameChar));
}

bool ZafObjectPersistence::Merge(ZafObjectPersistence &copy)
{
	// Merge the data portion.
	ZafDataPersistence::Merge(copy);

	// Merge the object tables.
	int i;
	for (i = 0; copy.compareFunction[i].name; i++)
		AddCompareFunction(copy.compareFunction[i].name, copy.compareFunction[i].function);
	for (i = 0; copy.objectConstructor[i].classID != ID_END; i++)
		AddObjectConstructor(copy.objectConstructor[i].className,
			copy.objectConstructor[i].classID,
			copy.objectConstructor[i].constructor);
	for (i = 0; copy.userFunction[i].name; i++)
		AddUserCallback(copy.userFunction[i].name, copy.userFunction[i].function);
	for (i = 0; copy.userObject[i].name; i++)
		AddUserObject(copy.userObject[i].name, copy.userObject[i].object);

	// Return success.
	return (true);
}

ZafObjectPersistence &ZafObjectPersistence::PopLevel(void)
{
	// Defer to the base class.
	ZafDataPersistence::PopLevel();
	return (*this);
}

ZafObjectPersistence &ZafObjectPersistence::PushLevel(ZafClassName className, ZafClassID classID, ZafPersistEntryType type)
{
	ZafDataPersistence::PushLevel(className, classID, type);
	return (*this);
}

bool ZafObjectPersistence::SetCompareFunctions(CompareFunction *tCompareFunction)
{
	// Clear the old compare functions.
	ClearCompareFunctions();

	// Add the new table.
	if (tCompareFunction)
	{
		for (int i = 0; tCompareFunction[i].name; i++)
			AddCompareFunction(tCompareFunction[i].name,
				tCompareFunction[i].function);
	}

	// Return success.
	return (true);
}

bool ZafObjectPersistence::SetObjectConstructors(ObjectConstructor *tObjectConstructor)
{
	// Clear the old object constructors.
	ClearObjectConstructors();

	// Add the new table.
	if (tObjectConstructor)
	{
		for (int i = 0; tObjectConstructor[i].classID != ID_END; i++)
			AddObjectConstructor(tObjectConstructor[i].className,
				tObjectConstructor[i].classID,
				tObjectConstructor[i].constructor);
	}

	// Return success.
	return (true);
}

bool ZafObjectPersistence::SetUserCallbacks(UserFunction *tUserFunction)
{
	// Clear the old user functions.
	ClearUserCallbacks();

	// Add the new table.
	if (tUserFunction)
	{
		for (int i = 0; tUserFunction[i].name; i++)
			AddUserCallback(tUserFunction[i].name,
				tUserFunction[i].function);
	}

	// Return success.
	return (true);
}

bool ZafObjectPersistence::SetUserObjects(UserObject *tUserObject)
{
	// Clear the old user objects.
	ClearUserObjects();

	// Add the new table.
	if (tUserObject)
	{
		for (int i = 0; tUserObject[i].name; i++)
			AddUserObject(tUserObject[i].name,
				tUserObject[i].object);
	}

	// Return success.
	return (true);
}

bool ZafObjectPersistence::SubtractCompareFunction(ZafDataName name)
{
	// Check for a valid table.
	if (!compareFunction)
		return (false);

	// Try to find a matching entry.
	for (int i = 0; compareFunction[i].name; i++)
		if (!streq(name, compareFunction[i].name))
		{
			// Clean up the current entry.
			delete [](ZafIChar *)compareFunction[i].name;

			// Check for an empty array.
			if (i == 0 && compareFunction[i+1].name == ZAF_NULLP(ZafDataNameChar))
			{
				delete []compareFunction;
				compareFunction = ZAF_NULLP(CompareFunction);
			}
			else
			{
				// Remove the object from the notification array.
				for (int j = i; compareFunction[j].name; j++)
					compareFunction[j] = compareFunction[j+1];
			}

			// Entry found.
			return (true);
		}

	// Entry not found.
	return (false);
}

bool ZafObjectPersistence::SubtractObjectConstructor(ZafClassID classID, ZafClassName className)
{
	// Check for a valid table.
	if (!objectConstructor)
		return (false);

	// Try to find a matching entry.
	for (int i = 0; objectConstructor[i].classID != ID_END; i++)
		if ((classID && classID == objectConstructor[i].classID) ||
			(className && !streq(className, objectConstructor[i].className)))
		{
			// Check for an empty array.
			if (i == 0 && objectConstructor[i+1].classID == 0)
			{
				delete []objectConstructor;
				objectConstructor = ZAF_NULLP(ObjectConstructor);
			}
			else
			{
				// Remove the object from the notification array.
				for (int j = i; objectConstructor[j].classID != ID_END; j++)
					objectConstructor[j] = objectConstructor[j+1];
			}

			// Entry found.
			return (true);
		}

	// Entry not found.
	return (false);
}

bool ZafObjectPersistence::SubtractUserCallback(ZafDataName name)
{
	// Check for a valid table.
	if (!userFunction)
		return (false);

	// Try to find a matching entry.
	for (int i = 0; userFunction[i].name; i++)
		if (!streq(name, userFunction[i].name))
		{
			// Clean up the current entry.
			delete [](ZafIChar *)userFunction[i].name;

			// Check for an empty array.
			if (i == 0 && userFunction[i+1].name == ZAF_NULLP(ZafDataNameChar))
			{
				delete []userFunction;
				userFunction = ZAF_NULLP(UserFunction);
			}
			else
			{
				// Remove the object from the notification array.
				for (int j = i; userFunction[j].name; j++)
					userFunction[j] = userFunction[j+1];
			}

			// Entry found.
			return (true);
		}

	// Entry not found.
	return (false);
}

bool ZafObjectPersistence::SubtractUserObject(ZafDataName name)
{
	// Check for a valid table.
	if (!userObject)
		return (false);

	// Try to find a matching entry.
	for (int i = 0; userObject[i].name; i++)
		if (!streq(name, userObject[i].name))
		{
			// Clean up the current entry.
			delete [](ZafIChar *)userObject[i].name;

			// Check for an empty array.
			if (i == 0 && userObject[i+1].name == ZAF_NULLP(ZafDataNameChar))
			{
				delete []userObject;
				userObject = ZAF_NULLP(UserObject);
			}
			else
			{
				// Remove the object from the notification array.
				for (int j = i; userObject[j].name; j++)
					userObject[j] = userObject[j+1];
			}

			// Entry found.
			return (true);
		}

	// Entry not found.
	return (false);
}
#endif
