#include "munga.h"
#pragma hdrstop

#include "namelist.h"

//#############################################################################
//##############    ObjectNameList    #########################################
//#############################################################################

ObjectNameList::ObjectNameList()
{
	Check_Pointer(this);
	firstEntry = lastEntry = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ObjectNameList::~ObjectNameList()
{
	Check(this);

	Entry
		*next;

	while (firstEntry)
	{
		Check_Pointer(firstEntry);
		next = firstEntry->nextEntry;
		Unregister_Pointer(firstEntry);
		delete firstEntry;
		firstEntry = next;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
	ObjectNameList::AddEntry(
		const char *name,
		void *data
	)
{
	Check(this);
	Check_Pointer(name);

	Entry
		*entry;

	entry = (Entry *)new char[ sizeof(Entry) + strlen(name) + 1 ];
	Register_Pointer(entry);

	entry->dataReference = data;
	entry->nextEntry = NULL;

	if (firstEntry)
	{
		Check_Pointer(lastEntry);
		lastEntry->nextEntry = entry;
	}
	else
	{
		firstEntry = entry;
	}
	lastEntry = entry;

	entry->SetName(name);

	return entry->GetName();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void*
	ObjectNameList::FindObject(const char *name)
{
	Check(this);
	Check_Pointer(name);

	Entry
		*entry;

	for (entry = firstEntry; entry; entry = entry->nextEntry)
	{
		Check_Pointer(entry);
		if (!strcmp(entry->GetName(), name))
		{
			break;
		}
	}
	return (entry)?entry->dataReference:NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ObjectNameList::DeleteEntry(const char *name)
{
	Check(this);
	Check_Pointer(name);

	Entry
		*cur,
		*prev,
		*entry;

	//----------------------------------------------------
	// ***** DANGEROUS!!! see notice in namelist.hh *****
	//----------------------------------------------------
	entry = (Entry *)(name - sizeof(Entry));
	Check_Pointer(entry);
	prev = NULL;
	for (cur=firstEntry; cur && cur != entry; cur = cur->nextEntry)
	{
		Check_Pointer(cur);
		prev = cur;
	}
	Verify(cur == entry);
	if (!prev)
	{
		firstEntry = entry->nextEntry;
	}
	else
	{
		prev->nextEntry = entry->nextEntry;
	}
	if (entry == lastEntry)
	{
		lastEntry = prev;
	}
	Unregister_Pointer(entry);
	delete entry;
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ObjectNameList::EntryCount() const
{
	Check(this);

	Entry
		*entry = firstEntry;
	int
		count = 0;

	while (entry)
	{
		Check_Pointer(entry);
		count++;
		entry = entry->nextEntry;
	}
	return count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ObjectNameList::BuildSubList(
		const ObjectNameList &source_list,
		const char *prefix
	)
{
	Check(this);
	Check(&source_list);
	Check_Pointer(prefix);

   const int
		length = strlen(prefix);
	Entry
		*entry = source_list.firstEntry;
	const char
		*name;
	int
		count = 0;

	//------------------------------------------------------
	// add entries to SubList whose name begins with prefix
	//------------------------------------------------------
	while (entry)
	{
		Check_Pointer(entry);
		name = entry->GetName();
		Check_Pointer(name);
		if (!strncmp(name, prefix, length))
		{
			count++;
			AddEntry(name, entry->dataReference);
		}
		entry = entry->nextEntry;
	}
	//-------------------------------------------
	// return number of entries added to SubList
	//-------------------------------------------
	return count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	ObjectNameList::TestInstance() const
{
	// nothing to check
	return True;
}

//#############################################################################
//##############    ObjectNameList::Entry    ##################################
//#############################################################################

void
	ObjectNameList::Entry::SetName(const char *name)
{
	Check_Pointer(this);
	Check_Pointer(name);
	strcpy((char *)this + sizeof(ObjectNameList::Entry), name);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(USE_SIGNATURE)
	int
		Is_Signature_Bad(const volatile ObjectNameList__Entry *)
	{
		return False;
	}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	ObjectNameList::Entry::IsName(const char *name) const
{
	Check(this);
	//do not check name here

	if (name)
	{
		Check_Pointer(name);
		return strcmp(GetName(), name) == 0;
	}
	else
	{
		return GetName() == name;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	ObjectNameList::Entry::TestInstance() const
{
	// nothing to check
	//do not check dataReference even if it is not NULL!
	return True;
}

//#############################################################################
//##############    NameList    ###############################################
//#############################################################################

NameList::NameList()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList::~NameList()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
	NameList::FindName(void *data)
{
	Check(this);

	Entry
		*entry;

	for (entry = firstEntry; entry; entry = entry->nextEntry)
	{
		Check_Pointer(entry);
		if (entry->dataReference == data)
		{
			break;
		}
	}
	return (entry)?entry->GetName():NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList::Entry*
	NameList::FindEntry(const char *name)
{
	Check(this);
	Check_Pointer(name);

	Entry
		*entry;

	for (entry = firstEntry; entry; entry = entry->nextEntry)
	{
		Check_Pointer(entry);
		if (!strcmp(entry->GetName(), name))
		{
			break;
		}
	}
	return entry;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList::Entry
	*NameList::FindEntry(void *data)
{
	Check(this);

	Entry
		*entry;

	for (entry = firstEntry; entry; entry = entry->nextEntry)
	{
		Check_Pointer(entry);
		if (entry->dataReference == data)
		{
			break;
		}
	}
	return entry;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NameList::DeleteEntry(const char *name)
{
	Check(this);
	Check_Pointer(name);

	Entry
		*prev = NULL,
		*entry;

	for (entry = firstEntry; entry; entry = entry->nextEntry)
	{
		Check_Pointer(entry);
		if (!strcmp(entry->GetName(), name))
		{
			break;
		}
		prev = entry;
	}
	if (entry)
	{
		if (!prev)
		{
			firstEntry = entry->nextEntry;
		}
		else
		{
			prev->nextEntry = entry->nextEntry;
		}
		if (entry == lastEntry)
		{
			lastEntry = prev;
		}
		Unregister_Pointer(entry);
		delete entry;
	}
	return;
}

//#############################################################################
//##############    AlphaNameList    ##########################################
//#############################################################################

AlphaNameList::AlphaNameList()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AlphaNameList::~AlphaNameList()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
	AlphaNameList::AddEntry(
		const char *name,
		void *data
	)
{
	Check(this);
	Check_Pointer(name);

	Entry
		*entry,
		*next = firstEntry,
		*prev = NULL;

	entry = (Entry *)new char[ sizeof(Entry) + strlen(name) + 1 ];
	Verify(entry);
	Register_Pointer(entry);

	//-----------------------------------
	// find location to insert new entry
	//-----------------------------------
	while (next)
	{
		Check_Pointer(next);
		if (strcmp(name, next->GetName()) < 0)
		{
			break;
		}
		prev = next;
		next = next->nextEntry;
	}
	//---------------------------------------------
	// insert new entry after prev and before next
	//---------------------------------------------
	if (!next)
	{
		lastEntry = entry;
	}
	if (!prev)
	{
		firstEntry = entry;
	}
	else
	{
		prev->nextEntry = entry;
	}
	entry->dataReference = data;
	entry->nextEntry = next;
	entry->SetName(name);

	return entry->GetName();
}

#if defined(TEST_CLASS)
	#include "namelist.tcp"
#endif