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

#include <z_list.hpp>
#include <z_string.hpp>
#define ZAF_LIST_INFO
#include "gbl_def.cpp"

// THIS COPYRIGHT NOTICE CANNOT BE REMOVED!
char *_zafCopyright = "\
COPYRIGHT (C) 1990-1999. \
All Rights Reserved. \
Zinc Software Incorporated.  Pleasant Grove, Utah  USA.";

// ----- ZafElement ---------------------------------------------------------

ZafElement::ZafElement(void) : previous(ZAF_NULLP(ZafElement)),
	next(ZAF_NULLP(ZafElement)), numberID(0), stringID(ZAF_NULLP(ZafStringIDChar))
{
}

ZafElement::ZafElement(const ZafElement &copy) :
	previous(ZAF_NULLP(ZafElement)), next(ZAF_NULLP(ZafElement)),
	numberID(0), stringID(ZAF_NULLP(ZafStringIDChar))
{
	SetNumberID(copy.numberID);
	SetStringID(copy.stringID);
}

ZafElement::~ZafElement(void)
{
	// Check for an allocated stringID.
	if (stringID)
		delete [](ZafIChar *)stringID;
}

ZafElement *ZafElement::EvaluateIsA(ZafElement *element, ZafClassID compareID)
{
	// This function is used in conjunction with DynamicPtrCast().  It gives
	// the result of an IsA comparison.  It is done in this function so
	// the value "element" is not evaluated twice in the DynamicPtrCast()
	// macro.
	if (element)
		return (element->IsA(compareID) ? element : ZAF_NULLP(ZafElement));
	return (ZAF_NULLP(ZafElement));
}

ZafElement *ZafElement::Find(ZafNumberID matchID)
{
	// Check the match against numberID.
	return ((numberID == matchID) ? this : ZAF_NULLP(ZafElement));
}

ZafElement *ZafElement::Find(const ZafIChar *matchID)
{
	// Check the match against stringID.
	return (!streq(matchID, stringID) ? this : ZAF_NULLP(ZafElement));
}

bool ZafElement::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : false);
}

bool ZafElement::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : false);
}

int ZafElement::ListIndex(void) const
{
	// Get the element index.
	int index = 0;
	for (ZafElement *element = previous; element; element = element->previous)
		index++;

	// Return the element index.
	return (index);
}

ZafNumberID ZafElement::SetNumberID(ZafNumberID tNumberID)
{
	numberID = tNumberID;
	return (numberID);
}

ZafStringID ZafElement::SetStringID(ZafStringID newStringID)
{
	// Check the old stringID.
	if (stringID)
		delete [](ZafIChar *)stringID;

	// Duplicate the new stringID.
	if (newStringID)
		stringID = strdup(newStringID);
	else
		stringID = ZAF_NULLP(ZafStringIDChar);

	// Return a pointer to the stringID.
	return (stringID);
}

#if defined(ZAF_UNICODE)
#include <z_str.hpp>
ZafStringID ZafElement::SetStringID(const char *newStringID)
{
	ZafStringData stringData(newStringID);
	return (SetStringID(stringData.Text()));
}
#endif


// ----- ZafList ------------------------------------------------------------

ZafList::ZafList(ZafCompareFunction _compareFunction) :
	compareFunction(_compareFunction), first(ZAF_NULLP(ZafElement)),
	last(ZAF_NULLP(ZafElement)), current(ZAF_NULLP(ZafElement)),
	count(0)
{
}

ZafList::~ZafList(void)
{
	ZafList::Destroy();
}

ZafElement *ZafList::Add(ZafElement *newElement)
{
	// See if it is a default insertion.
	if (!first || !compareFunction)
		return (ZafList::Add(newElement, ZAF_NULLP(ZafElement)));

	ZafElement *positionElement = ZAF_NULLP(ZafElement);

	// Shortcuts for elements in special orders.
	if ((*compareFunction)(last, newElement) <= 0)
		positionElement = last;
	else if ((*compareFunction)(first, newElement) < 0)
	{
		// Walk the list until find the proper insertion point.
		positionElement = last;
		while (positionElement && (*compareFunction)(positionElement, newElement) > 0)
			positionElement = positionElement->previous;
	}

	// Place the element in the sorted list.
	if (positionElement)					// Insert after positionElement.
	{
		newElement->previous = positionElement;
		newElement->next = positionElement->next;
		if (newElement->next)
			newElement->next->previous = newElement;
		positionElement->next = newElement;
		if (last == positionElement)
			last = newElement;
	}
	else							// Insert at the beginning of the list.
	{
		newElement->previous = ZAF_NULLP(ZafElement);
		newElement->next = first;
		first->previous = newElement;
		first = newElement;
	}

	++count;

	// Return a pointer to the element.
	return (newElement);
}

ZafElement *ZafList::Add(ZafElement *newElement, ZafElement *positionElement)
{
	// Add the element to the list.
	if (!first)						// Put at the first of the list.
	{
		newElement->previous = newElement->next = ZAF_NULLP(ZafElement);
		first = last = newElement;
	}
	else if (!positionElement)		// Put at the end of the list.
	{
		newElement->previous = last;
		newElement->next = ZAF_NULLP(ZafElement);
		last->next = newElement;
		last = newElement;
	}
	else							// Put before the specified element.
	{
		newElement->previous = positionElement->previous;
		newElement->next = positionElement;
		if (!positionElement->previous)
			first = newElement;
		else
			positionElement->previous->next = newElement;
		positionElement->previous = newElement;
	}

	++count;

	// Return a pointer to the element.
	return (newElement);
}

ZafCompareFunction ZafList::CompareFunction(void) const
{
	return (compareFunction);
}

ZafElement *ZafList::Find(ZafNumberID matchID)
{
	// Try to find a matching entry.
	ZafElement *match = ZAF_NULLP(ZafElement);
	for (ZafElement *object = First(); object && !match; object = object->Next())
		match = object->Find(matchID);

	// Return the object.
	return (match);
}

ZafElement *ZafList::Find(const ZafIChar *matchID)
{
	// Try to find a matching entry.
	ZafElement *match = ZAF_NULLP(ZafElement);
	for (ZafElement *object = First(); object && !match; object = object->Next())
		match = object->Find(matchID);

	// Return the object.
	return (match);
}

ZafElement *ZafList::Subtract(ZafElement *element)
{
	// Make sure an element is specified.
	if (!element)
		return(ZAF_NULLP(ZafElement));

	// Make sure the element is in the list.
	ZafElement *tElement = element;
	while (tElement->previous)
		tElement = tElement->previous;
	if (tElement != first)
		return (element);

	// Delete the specified element from the list.
	if (element->previous)
		element->previous->next = element->next;
	else
		first = element->next;
	if (element->next)
		element->next->previous = element->previous;
	else
		last = element->previous;
	if (current == element)
		current = ZAF_NULLP(ZafElement);

	--count;

	// Return the next element.
	tElement = element->next;
	element->next = element->previous = ZAF_NULLP(ZafElement);
	return (tElement);
}

void ZafList::Destroy(void)
{
	ZafElement *tElement;

	// Delete all the elements in the list.
	for (ZafElement *element = first; element; )
	{
		tElement = element;
		element = element->next;
		delete tElement;
	}
	// Reset the list element pointers and count.
	first = last = current = ZAF_NULLP(ZafElement);
	count = 0;
}

ZafElement *ZafList::Get(int index)
{
	// Negative indexes don't match any elements.
	if (index < 0)
		return (ZAF_NULLP(ZafElement));

	// Get the list element.
	ZafElement *element;
	for (element = first; index > 0 && element; element = element->next)
		index--;

	// Return the matching element.  If no match, element will be NULL.
	return (element);
}

ZafElement *ZafList::Get(int (*findFunction)(ZafElement *element1, ZafElement *matchData), ZafElement *matchData)
{
	// See if the current element matches.
	if (current && !(*findFunction)(current, matchData))
		return (current);

	// Try to find the list element.
	ZafElement *element;
	for (element = first; element && (*findFunction)(element, matchData);
		element = element->next)
		;

	// Return the matching element.  If no match, element will be NULL.
	return (element);
}

int ZafList::Index(const ZafElement *element) const
{
	// Make sure there is an element.
	if (!element)
		return (-1);

	// Get the element index.
	int index = 0;
	ZafElement *tElement;
	for (tElement = first; tElement && tElement != element;
		tElement = tElement->next)
		index++;

	// Return the element index.  If no match, return value is -1.
	if (tElement)
		return (index);
	return (-1);
}

void ZafList::Sort(void)
{
	// Make sure there are elements in the list and a comparison routine.
	if (!first || !compareFunction)
		return;

	// Start the list with the first element.
	ZafElement *element;
	ZafElement *addElement = first;
	first = last = ZAF_NULLP(ZafElement);
	count = 0;

	// Re-add the elements (Add will sort them).
	while (addElement)
	{
		element = addElement->next;
		ZafList::Add(addElement);
		addElement = element;
	}
}

ZafCompareFunction ZafList::SetCompareFunction(ZafCompareFunction tCompareFunction)
{
	compareFunction = tCompareFunction;
	return (compareFunction);
}

