/*
**	termPhone.c
**
**	Phonebook support routines
**
**	Copyright © 1990-1995 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* RemoveDialEntry(struct PhoneNode *Node):
	 *
	 *	Remove a phone book entry from the dial list.
	 */

VOID __regargs
RemoveDialEntry(struct PhoneNode *Node)
{
	LONG Count = Node -> Entry -> Count,i;

	for(i = 0 ; i < NumPhoneEntries ; i++)
	{
		if(Phonebook[i] -> Count > Count)
		{
			SPrintf(Phonebook[i] -> Node -> LocalName,"%3ld - %s",Phonebook[i] -> Count--,Phonebook[i] -> Header -> Name);

			if(Phonebook[i] -> NodeGroup)
				strcpy(Phonebook[i] -> NodeGroup -> LocalName,Phonebook[i] -> Node -> LocalName);
		}
	}

	Node -> Entry -> Count = -1;

	SPrintf(Node -> LocalName,"      %s",Node -> Entry -> Header -> Name);

	if(Node -> Entry -> NodeGroup)
		strcpy(Node -> Entry -> NodeGroup -> LocalName,Node -> LocalName);
}

	/* RemoveDialNode(struct PhoneNode *Node):
	 *
	 *	Remove a node from the dialing list.
	 */

VOID __regargs
RemoveDialNode(struct PhoneNode *Node)
{
	if(Node)
	{
			/* If it's got a name it was built in the phone book panel,
			 * else the ARexx interface has linked it into the list.
			 */

		if(Node -> VanillaNode . ln_Name && Node -> VanillaNode . ln_Name[0])
		{
			LONG Count = Node -> Entry -> Count,i;

				/* Adjust all entries. */

			for(i = 0 ; i < NumPhoneEntries ; i++)
			{
				if(Phonebook[i] -> Count > Count)
					Phonebook[i] -> Count--;
			}

				/* Can the index count. */

			Node -> Entry -> Count = -1;
		}
	}
}

	/* SortToList(struct PhoneNode *PhoneNode,struct PhoneEntry *Entry):
	 *
	 *	Add a node to the dial list.
	 */

BOOL __regargs
SortToList(struct PhoneNode *PhoneNode,struct PhoneEntry *Entry)
{
	BOOLEAN JustCreated = FALSE;

		/* No dial list prepared? Let's create one! */

	if(!DialList)
	{
		if(DialList = (struct List *)AllocVecPooled(sizeof(struct List),MEMF_ANY))
		{
			NewList(DialList);

			JustCreated = TRUE;
		}
	}

		/* Do we have a dial list to mangle? */

	if(DialList)
	{
		struct PhoneNode *NewNode;

			/* Create a new node to be added to the dial list. */

		if(NewNode = (struct PhoneNode *)AllocVecPooled(sizeof(struct PhoneNode),MEMF_ANY | MEMF_CLEAR))
		{
			struct PhoneNode *Node = (struct PhoneNode *)DialList -> lh_Head;

				/* Take care of the name and the corresponding phone book entry. */

			NewNode -> VanillaNode . ln_Name = NewNode -> LocalName;

			if(Entry)
			{
				NewNode -> Entry = Entry;

				SPrintf(NewNode -> LocalName,"      %s",Entry -> Header -> Name);
			}
			else
			{
				NewNode -> Entry = PhoneNode -> Entry;

				strcpy(NewNode -> LocalName,PhoneNode -> LocalName);
			}

				/* Install back-link. */

			NewNode -> Entry -> Node = NewNode;

				/* Sort the node into the list. */

			while(Node -> VanillaNode . ln_Succ)
			{
				if(Node -> Entry -> Count > NewNode -> Entry -> Count)
				{
					if(Node == (struct PhoneNode *)DialList -> lh_Head)
						AddHead(DialList,&NewNode -> VanillaNode);
					else
						Insert(DialList,&NewNode -> VanillaNode,Node -> VanillaNode . ln_Pred);

					return(TRUE);
				}

				Node = (struct PhoneNode *)Node -> VanillaNode . ln_Succ;
			}

			AddTail(DialList,&NewNode -> VanillaNode);

			return(TRUE);
		}
	}

	if(JustCreated)
	{
		FreeVecPooled(DialList);
		DialList = NULL;
	}

	return(FALSE);
}

	/* FreeDialList(BYTE ClearAll):
	 *
	 *	Release the contents of the dial list.
	 */

VOID __regargs
FreeDialList(BYTE ClearAll)
{
	if(DialList)
	{
		struct PhoneNode *SubNode,*NextNode;

		for(SubNode = (struct PhoneNode *)DialList -> lh_Head ; NextNode = (struct PhoneNode *)SubNode -> VanillaNode . ln_Succ ; SubNode = NextNode)
		{
			if(ClearAll || !SubNode -> VanillaNode . ln_Name || !SubNode -> VanillaNode . ln_Name[0])
				FreeVecPooled(SubNode);
		}

		FreeVecPooled(DialList);

		DialList = NULL;
	}
}

	/* CreatePhoneList():
	 *
	 *	Turn the array of pointers to phonebook entries into
	 *	a linked standard Amiga List (gadtools needs this).
	 */

struct List *
CreatePhoneList()
{
	struct List *PhoneList;

	if(Phonebook && NumPhoneEntries)
	{
		struct PhoneNode *PhoneNode;

		if(PhoneList = (struct List *)AllocVecPooled(sizeof(struct List) + NumPhoneEntries * sizeof(struct PhoneNode),MEMF_ANY | MEMF_CLEAR))
		{
			LONG i;

			NewList(PhoneList);

			PhoneNode = (struct PhoneNode *)(PhoneList + 1);

			for(i = 0 ; i < NumPhoneEntries ; i++)
			{
				if(Phonebook[i] -> Count != -1)
				{
					SPrintf(PhoneNode[i] . LocalName,"%3ld - %s",Phonebook[i] -> Count + 1,Phonebook[i] -> Header -> Name);

					if(Phonebook[i] -> NodeGroup)
						strcpy(Phonebook[i] -> NodeGroup -> LocalName,PhoneNode[i] . LocalName);
				}
				else
				{
					SPrintf(PhoneNode[i] . LocalName,"      %s",Phonebook[i] -> Header -> Name);

					if(Phonebook[i] -> NodeGroup)
						strcpy(Phonebook[i] -> NodeGroup -> LocalName,PhoneNode[i] . LocalName);
				}

				PhoneNode[i] . VanillaNode . ln_Name = PhoneNode[i] . LocalName;

				Phonebook[i] -> Node = &PhoneNode[i];

				PhoneNode[i] . Entry = Phonebook[i];

				AddTail(PhoneList,&PhoneNode[i]);
			}

			return(PhoneList);
		}
	}
	else
	{
		if(PhoneList = (struct List *)AllocVecPooled(sizeof(struct List),MEMF_ANY))
		{
			NewList(PhoneList);

			return(PhoneList);
		}
	}

	return(NULL);
}

	/* UpdatePhoneList(struct List *PhoneList):
	 *
	 *	Rebuilds the phone header list, such as after entries got moved.
	 */

VOID __regargs
UpdatePhoneList(struct List *PhoneList)
{
	struct PhoneNode	*PhoneNode;
	LONG			 i;

	for(i = 0, PhoneNode = (struct PhoneNode *)PhoneList -> lh_Head ; i < NumPhoneEntries && PhoneNode -> VanillaNode . ln_Succ ; i++, PhoneNode = (struct PhoneNode *)PhoneNode -> VanillaNode . ln_Succ)
	{
		if(Phonebook[i] -> Count != -1)
		{
			SPrintf(PhoneNode -> LocalName,"%3ld - %s",Phonebook[i] -> Count + 1,Phonebook[i] -> Header -> Name);

			if(Phonebook[i] -> NodeGroup)
				SPrintf(Phonebook[i] -> NodeGroup -> LocalName,"%3ld - %s",Phonebook[i] -> Count + 1,Phonebook[i] -> Header -> Name);
		}
		else
		{
			SPrintf(PhoneNode -> LocalName,"      %s",Phonebook[i] -> Header -> Name);

			if(Phonebook[i] -> NodeGroup)
				SPrintf(Phonebook[i] -> NodeGroup -> LocalName,"      %s",Phonebook[i] -> Header -> Name);
		}

		Phonebook[i] -> Node = PhoneNode;

		PhoneNode -> Entry = Phonebook[i];
	}
}

	/* DeletePhoneList(struct List *PhoneList):
	 *
	 *	Delete the entries listed in the Amiga List
	 *	created by the routine above.
	 */

VOID __regargs
DeletePhoneList(struct List *PhoneList)
{
	FreeVecPooled(PhoneList);
}

	/* Compare(struct PhoneEntry **A,struct PhoneEntry **B):
	 *
	 *	Comparison subroutine required by the QuickSort
	 *	call below.
	 */

STATIC int __stdargs
Compare(struct PhoneEntry **A,struct PhoneEntry **B)
{
		/* Has entry A been selected? */

	if((*A) -> Count == -1)
	{
			/* If entry B isn't selected either, compare the
			 * names lexically, else entry B is supposed
			 * to be `smaller' than entry A.
			 */

		if((*B) -> Count == -1)
			return(Strcoll((*A) -> Header -> Name,(*B) -> Header -> Name));
		else
			return(1);
	}
	else
	{
			/* If entry B isn't selected, entry A is supposed
			 * to be `smaller' than entry B, else return
			 * the difference between both entries.
			 */

		if((*B) -> Count == -1)
			return(-1);
		else
			return((*A) -> Count - (*B) -> Count);
	}
}

STATIC int __stdargs
CompareInverse(struct PhoneEntry **B,struct PhoneEntry **A)
{
	if((*A) -> Count == -1)
	{
		if((*B) -> Count == -1)
			return(Strcoll((*A) -> Header -> Name,(*B) -> Header -> Name));
		else
			return(1);
	}
	else
	{
		if((*B) -> Count == -1)
			return(-1);
		else
			return((*A) -> Count - (*B) -> Count);
	}
}

STATIC int __stdargs
CompareName(struct PhoneEntry **A,struct PhoneEntry **B)
{
	return(Strcoll((*A) -> Header -> Name,(*B) -> Header -> Name));
}

STATIC int __stdargs
CompareNumber(struct PhoneEntry **A,struct PhoneEntry **B)
{
	return(Strcoll((*A) -> Header -> Number,(*B) -> Header -> Number));
}

STATIC int __stdargs
CompareComment(struct PhoneEntry **A,struct PhoneEntry **B)
{
	return(Strcoll((*A) -> Header -> Comment,(*B) -> Header -> Comment));
}

STATIC int __stdargs
CompareInverseName(struct PhoneEntry **B,struct PhoneEntry **A)
{
	return(Strcoll((*A) -> Header -> Name,(*B) -> Header -> Name));
}

STATIC int __stdargs
CompareInverseNumber(struct PhoneEntry **B,struct PhoneEntry **A)
{
	return(Strcoll((*A) -> Header -> Number,(*B) -> Header -> Number));
}

STATIC int __stdargs
CompareInverseComment(struct PhoneEntry **B,struct PhoneEntry **A)
{
	return(Strcoll((*A) -> Header -> Comment,(*B) -> Header -> Comment));
}

	/* SortPhoneEntries():
	 *
	 *	Sorts the current phone list array in ascending order.
	 */

VOID __regargs
SortPhoneEntries(struct List *List,WORD How,BOOL ReverseOrder)
{
	int (* __stdargs SortFunc)(struct PhoneEntry **B,struct PhoneEntry **A);

	switch(How)
	{
		case SORT_NAME:

			if(ReverseOrder)
				SortFunc = CompareInverseName;
			else
				SortFunc = CompareName;

			break;

		case SORT_COMMENT:

			if(ReverseOrder)
				SortFunc = CompareInverseComment;
			else
				SortFunc = CompareComment;
			break;

		case SORT_NUMBER:

			if(ReverseOrder)
				SortFunc = CompareInverseNumber;
			else
				SortFunc = CompareNumber;
			break;

		case SORT_SELECTION:

			if(ReverseOrder)
				SortFunc = CompareInverse;
			else
				SortFunc = Compare;

			break;

		default:

			SortFunc = CompareName;
			break;
	}

	if(!List)
		qsort((APTR)Phonebook,NumPhoneEntries,sizeof(struct PhoneEntry *),SortFunc);
	else
	{
		LONG			 Count;
		struct PhoneNode	*Node;

		for(Count = 0, Node = (struct PhoneNode *)List -> lh_Head ; Node -> VanillaNode . ln_Succ ; Node = (struct PhoneNode *)Node -> VanillaNode . ln_Succ)
			Count++;

		if(Count)
		{
			struct PhoneEntry **Table;

			if(Table = (struct PhoneEntry **)AllocVecPooled(sizeof(struct PhoneEntry *) * Count,MEMF_ANY))
			{
				for(Count = 0, Node = (struct PhoneNode *)List -> lh_Head ; Node -> VanillaNode . ln_Succ ; Node = (struct PhoneNode *)Node -> VanillaNode . ln_Succ)
					Table[Count++] = Node -> Entry;

				qsort((APTR)Table,Count,sizeof(struct PhoneEntry *),SortFunc);

				for(Count = 0, Node = (struct PhoneNode *)List -> lh_Head ; Node -> VanillaNode . ln_Succ ; Node = (struct PhoneNode *)Node -> VanillaNode . ln_Succ)
				{
					Node -> Entry = Table[Count++];

					strcpy(Node -> LocalName,Node -> Entry -> Node -> LocalName);
				}

				FreeVecPooled(Table);
			}
		}
	}
}

	/* FreeTimeDateNode(struct TimeDateNode *Node):
	 *
	 *	Free the memory allocated for a TimeDateNode and
	 *	the associated data table.
	 */

VOID __regargs
FreeTimeDateNode(struct TimeDateNode *Node)
{
	FreeVecPooled(Node -> Table);

	FreeVecPooled(Node);
}

	/* FreeTimeDateList(struct List *List):
	 *
	 *	Free a list of TimeDateNodes.
	 */

VOID __regargs
FreeTimeDateList(struct List *List)
{
	if(List)
	{
		struct Node *Node;

		while(Node = RemHead(List))
			FreeTimeDateNode((struct TimeDateNode *)Node);
	}
}

	/* CopyTimeDateList(struct List *From,struct List *To,BYTE SkipFirst):
	 *
	 *	Copies the TimeDateNode list from one location into
	 *	another.
	 */

VOID __regargs
CopyTimeDateList(struct List *From,struct List *To,BYTE SkipFirst)
{
	struct TimeDateNode *FromNode,*ToNode;

	FromNode = (struct TimeDateNode *)From -> lh_Head;

	if(SkipFirst)
		FromNode = (struct TimeDateNode *)FromNode -> VanillaNode . ln_Succ;

	while(FromNode -> VanillaNode . ln_Succ)
	{
		if(ToNode = (struct TimeDateNode *)AllocVecPooled(sizeof(struct TimeDateNode),MEMF_ANY))
		{
			CopyMem(FromNode,ToNode,sizeof(struct TimeDateNode));

			ToNode -> VanillaNode . ln_Name = ToNode -> Buffer;

			if(ToNode -> Table = (struct TimeDate *)AllocVecPooled(sizeof(struct TimeDate) * FromNode -> Table[0] . Count,MEMF_ANY))
			{
				CopyMem(FromNode -> Table,ToNode -> Table,sizeof(struct TimeDate) * FromNode -> Table[0] . Count);

				AddTail(To,&ToNode -> VanillaNode);
			}
			else
				FreeVecPooled(ToNode);
		}

		FromNode = (struct TimeDateNode *)FromNode -> VanillaNode . ln_Succ;
	}
}

	/* AdaptTimeDateNode(struct TimeDateNode *Node):
	 *
	 *	Adapt the title and comment of a TimeDateNode.
	 */

VOID __regargs
AdaptTimeDateNode(struct TimeDateNode *Node)
{
	STRPTR Comment = Node -> Header . Comment[0] ? Node -> Header . Comment : LocaleString(MSG_TERMPHONE_NO_COMMENT_TXT);

	if(Node -> Header . Month == -1)
	{
		if(Node -> Header . Day == -1)
			strcpy(Node -> Buffer,LocaleString(MSG_TERMPHONE_STANDARD_SETTINGS_TXT));
		else
			SPrintf(Node -> Buffer,LocaleString(MSG_TERMPHONE_DAYS_TXT),Comment);
	}
	else
		SPrintf(Node -> Buffer,"%2ld %s » %s",Node -> Header . Day,LocaleString(MSG_TERMPHONE_JAN_TXT + Node -> Header . Month),Comment);
}

	/* TimeCompare(struct TimeDate *A,struct TimeDate *B):
	 *
	 *	Comparison routine required by SortTimeTable().
	 */

STATIC int __stdargs
TimeCompare(struct TimeDate *A,struct TimeDate *B)
{
	return(((LONG)A -> Time) - ((LONG)B -> Time));
}

	/* SortTimeTable(struct TimeDateNode *Node):
	 *
	 *	Sort the time table associated with a
	 *	TimeDateNode in ascending order.
	 */

VOID __regargs
SortTimeTable(struct TimeDateNode *Node)
{
	qsort((APTR)Node -> Table,Node -> Table[0] . Count,sizeof(struct TimeDate),TimeCompare);
}

	/* BuildTimeList(struct TimeDateNode *Node):
	 *
	 *	Build a read-to-display time table list from a TimeDateNode.
	 */

struct List * __regargs
BuildTimeList(struct TimeDateNode *Node)
{
	struct List *List;

	if(List = (struct List *)AllocVecPooled(sizeof(struct List) + Node -> Table[0] . Count * sizeof(struct TimeNode),MEMF_ANY | MEMF_CLEAR))
	{
		struct TimeNode	*Time = (struct TimeNode *)(List + 1);
		UBYTE		 LocalBuffer[40];
		LONG		 i;

		NewList(List);

		for(i = 0 ; i < Node -> Table[0] . Count ; i++)
		{
			Time[i] . VanillaNode . ln_Name	= Time[i] . Name;
			Time[i] . Time			= Node -> Table[i] . Time;

			FormatTime(LocalBuffer,Time[i] . Time / 6,(Time[i] . Time % 6) * 10,-1);

			LocalBuffer[19] = 0;

			strcpy(Time[i] . Name,LocalBuffer);

			AddTail(List,&Time[i]);
		}
	}

	return(List);
}

	/* ResizeTimeDateNode(struct TimeDateNode *Node,LONG Count,UBYTE Time):
	 *
	 *	Resize the time table associated with a TimeDateNode.
	 */

BYTE __regargs
ResizeTimeDateNode(struct TimeDateNode *Node,LONG Count,UBYTE Time)
{
	if(Count != Node -> Table[0] . Count)
	{
		struct TimeDate *Table;

		if(Table = (struct TimeDate *)AllocVecPooled(sizeof(struct TimeDate) * Count,MEMF_ANY | MEMF_CLEAR))
		{
			LONG i;

			CopyMem(Node -> Table,Table,sizeof(struct TimeDate) * Count);

			if(Count > Node -> Table[0] . Count)
			{
				for(i = Node -> Table[0] . Count ; i < Count ; i++)
				{
					CopyMem(&Node -> Table[0],&Table[i],sizeof(struct TimeDate));

					Table[i] . Time = Time;
				}
			}

			for(i = 0 ; i < Count ; i++)
				Table[i] . Count = Count;

			FreeVecPooled(Node -> Table);

			Node -> Table = Table;

			return(TRUE);
		}
		else
			return(FALSE);
	}
	else
		return(TRUE);
}

	/* DeleteTimeDateNode(struct TimeDateNode *Node,LONG Index):
	 *
	 *	Delete a single timetable entry from a TimeDateNode.
	 */

BYTE __regargs
DeleteTimeDateNode(struct TimeDateNode *Node,LONG Index)
{
	struct TimeDate	*Table;
	LONG		 Count = Node -> Table[0] . Count - 1;

	if(Table = (struct TimeDate *)AllocVecPooled(sizeof(struct TimeDate) * Count,MEMF_ANY | MEMF_CLEAR))
	{
		LONG i,j;

		for(i = j = 0 ; i < Node -> Table[0] . Count ; i++)
		{
			if(i != Index)
			{
				CopyMem(&Node -> Table[i],&Table[j],sizeof(struct TimeDate));

				Table[j++] . Count = Count;
			}
		}

		FreeVecPooled(Node -> Table);

		Node -> Table = Table;

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* CreateTimeDateNode(BYTE Month,BYTE Day,STRPTR Comment,LONG Count):
	 *
	 *	Create a new TimeDateNode including time table entries.
	 */

struct TimeDateNode * __regargs
CreateTimeDateNode(BYTE Month,BYTE Day,STRPTR Comment,LONG Count)
{
	struct TimeDateNode *Node;

	if(Node = (struct TimeDateNode *)AllocVecPooled(sizeof(struct TimeDateNode),MEMF_ANY | MEMF_CLEAR))
	{
		if(Node -> Table = (struct TimeDate *)AllocVecPooled(sizeof(struct TimeDate) * Count,MEMF_ANY | MEMF_CLEAR))
		{
			Node -> VanillaNode . ln_Name			= Node -> Buffer;

			Node -> Header . Month				= Month;
			Node -> Header . Day				= Day;
/*
			Node -> Table[0] . PayPerUnit[DT_FIRST_UNIT]	= 23;
			Node -> Table[0] . SecPerUnit[DT_FIRST_UNIT]	=  6 * 60;
			Node -> Table[0] . PayPerUnit[DT_NEXT_UNIT]	= 23;
			Node -> Table[0] . SecPerUnit[DT_NEXT_UNIT]	=  6 * 60;
*/
			Node -> Table[0] . PayPerUnit[DT_FIRST_UNIT]	= 0;
			Node -> Table[0] . SecPerUnit[DT_FIRST_UNIT]	= 0;
			Node -> Table[0] . PayPerUnit[DT_NEXT_UNIT]	= 0;
			Node -> Table[0] . SecPerUnit[DT_NEXT_UNIT]	= 0;
			Node -> Table[0] . Time				= DT_GET_TIME( 8,0);
			Node -> Table[0] . Count			= Count;

			if(Count > 1)
			{
/*
				Node -> Table[1] . PayPerUnit[DT_FIRST_UNIT]	= 23;
				Node -> Table[1] . SecPerUnit[DT_FIRST_UNIT]	= 12 * 60;
				Node -> Table[1] . PayPerUnit[DT_NEXT_UNIT]	= 23;
				Node -> Table[1] . SecPerUnit[DT_NEXT_UNIT]	= 12 * 60;
*/
				Node -> Table[1] . PayPerUnit[DT_FIRST_UNIT]	= 0;
				Node -> Table[1] . SecPerUnit[DT_FIRST_UNIT]	= 0;
				Node -> Table[1] . PayPerUnit[DT_NEXT_UNIT]	= 0;
				Node -> Table[1] . SecPerUnit[DT_NEXT_UNIT]	= 0;
				Node -> Table[1] . Time				= DT_GET_TIME(18,0);
				Node -> Table[1] . Count			= Count;
			}

			strcpy(Node -> Header . Comment,Comment);

			AdaptTimeDateNode(Node);

			return(Node);
		}
		else
			FreeVecPooled(Node);
	}

	return(NULL);
}

	/* RemPhoneEntry(struct PhoneNode *Node,LONG Num):
	 *
	 *	Remove a given entry from the phone book.
	 */

VOID __regargs
RemPhoneEntry(struct PhoneNode *Node,LONG Num)
{
	struct PhoneEntry	*Entry;
	LONG			 i;

	if(Node)
	{
		for(i = 0 ; i < NumPhoneEntries ; i++)
		{
			if(Phonebook[i] -> Node == Node || Phonebook[i] -> NodeGroup == Node)
			{
				Num = i;
				break;
			}
		}
	}

	Entry = Phonebook[Num];

	for(i = Num ; i < NumPhoneEntries ; i++)
		Phonebook[i] = Phonebook[i + 1];

	Phonebook[NumPhoneEntries--] = NULL;

	if(Entry -> Config)
		DeleteConfiguration(Entry -> Config);

	FreeTimeDateList((struct List *)&Entry -> TimeDateList);

		// Is this entry member of a group?

	if(Entry -> NodeGroup)
	{
			// Disconnect it

		Remove((struct Node *)Entry -> NodeGroup);

		FreeVecPooled(Entry -> NodeGroup);
	}

	FreeVecPooled(Entry);
}

	/* NewPhoneEntry():
	 *
	 *	Create a new phone book entry with default values and
	 *	add it to the array. Expand the phone book if necessary.
	 */

BYTE
NewPhoneEntry()
{
	struct PhoneEntry	**PrivatePhonebook;
	LONG			 PrivatePhoneSize,i;

		/* The phone book is filled `to the brim', so let's expand
		 * it.
		 */

	if(NumPhoneEntries + 1 > PhoneSize)
	{
			/* Allocate another phone book. */

		if(PrivatePhonebook = CreatePhonebook(PhoneSize + 1,&PrivatePhoneSize,FALSE))
		{
				/* Copy the data. */

			if(Phonebook && PhoneSize)
			{
				for(i = 0 ; i < PhoneSize ; i++)
					PrivatePhonebook[i] = Phonebook[i];

					/* Remove the old phonebook. */

				DeletePhonebook(Phonebook,PhoneSize,FALSE);
			}

				/* Assign the new pointers. */

			Phonebook = PrivatePhonebook;
			PhoneSize = PrivatePhoneSize;
		}
		else
			return(FALSE);
	}

		/* Allocate another entry and add it to the phone book. */

	if(Phonebook[NumPhoneEntries] = (struct PhoneEntry *)AllocVecPooled(sizeof(struct PhoneEntry) + sizeof(struct PhoneHeader),MEMF_ANY | MEMF_CLEAR))
	{
		if(Phonebook[NumPhoneEntries] -> Config = CreateConfiguration(FALSE))
		{
			struct TimeDateNode *TimeDateNode;

			Phonebook[NumPhoneEntries] -> Header = (struct PhoneHeader *)(Phonebook[NumPhoneEntries] + 1);

			strcpy(Phonebook[NumPhoneEntries] -> Header -> Name,LocaleString(MSG_TERMPHONE_UNUSED_ENTRY_TXT));

			Phonebook[NumPhoneEntries] -> Count = -1;

			Phonebook[NumPhoneEntries] -> Header -> ID = PhonebookID++;

			GetSysTime(&Phonebook[NumPhoneEntries] -> Header -> Creation);

			NewList((struct List *)&Phonebook[NumPhoneEntries] -> TimeDateList);

			if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
				AddTail((struct List *)&Phonebook[NumPhoneEntries] -> TimeDateList,&TimeDateNode -> VanillaNode);

			NumPhoneEntries++;

			return(TRUE);
		}

		FreeVecPooled(Phonebook[NumPhoneEntries]);

		Phonebook[NumPhoneEntries] = NULL;
	}

	return(FALSE);
}

	/* MergePhonebook(PhonebookHandle *BookHandle):
	 *
	 *	Merges the current phonebook with new data.
	 */

struct PhoneEntry ** __regargs
MergePhonebook(PhonebookHandle *BookHandle)
{
	struct PhoneEntry	**PrivatePhonebook;
	LONG			 PrivatePhoneSize = 0;

	if(PrivatePhonebook = CreatePhonebook(NumPhoneEntries + BookHandle -> NumPhoneEntries,&PrivatePhoneSize,FALSE))
	{
		LONG i,Count = 0;

		for(i = 0 ; i < NumPhoneEntries ; i++)
			PrivatePhonebook[Count++] = Phonebook[i];

		for(i = 0 ; i < BookHandle -> NumPhoneEntries ; i++)
		{
			PrivatePhonebook[Count] = BookHandle -> Phonebook[i];

			PrivatePhonebook[Count] -> Header -> ID = PhonebookID++;

			Count++;
		}

		DeletePhonebook(Phonebook,PhoneSize,FALSE);

		Phonebook	= PrivatePhonebook;
		PhoneSize	= PrivatePhoneSize;
		NumPhoneEntries	= Count;
	}

	return(PrivatePhonebook);
}

	/* CreatePhonebook(LONG Size,LONG *AllocSize,BYTE CreateEntries):
	 *
	 *	Create a new phone entry array (so-called phone book).
	 */

struct PhoneEntry ** __regargs
CreatePhonebook(LONG Size,LONG *AllocSize,BYTE CreateEntries)
{
	struct PhoneEntry **PhoneEntry = NULL;

	if(Size)
	{
			/* Round the number of phone entries to a
			 * multiple of eight.
			 */

		*AllocSize = (Size + 7) & ~7;

			/* Create the list of pointers. */

		if(PhoneEntry = (struct PhoneEntry **)AllocVecPooled(*AllocSize * sizeof(struct PhoneEntry *),MEMF_ANY | MEMF_CLEAR))
		{
				/* And create some entries if necessary. */

			if(CreateEntries)
			{
				BYTE Success = TRUE;
				LONG i;

				for(i = 0 ; Success && i < Size ; i++)
				{
					if(PhoneEntry[i] = (struct PhoneEntry *)AllocVecPooled(sizeof(struct PhoneEntry) + sizeof(struct PhoneHeader),MEMF_ANY | MEMF_CLEAR))
					{
						if(PhoneEntry[i] -> Config = CreateConfiguration(FALSE))
						{
							PhoneEntry[i] -> Header = (struct PhoneHeader *)(PhoneEntry[i] + 1);

							GetSysTime(&PhoneEntry[i] -> Header -> Creation);

							NewList((struct List *)&PhoneEntry[i] -> TimeDateList);
						}
						else
							Success = FALSE;
					}
					else
						Success = FALSE;
				}

				if(!Success)
				{
					for(i = 0 ; i < *AllocSize ; i++)
					{
						if(PhoneEntry[i])
						{
							if(PhoneEntry[i] -> Config)
								DeleteConfiguration(PhoneEntry[i] -> Config);

							FreeVecPooled(PhoneEntry[i]);
						}
					}

					FreeVecPooled(PhoneEntry);

					return(NULL);
				}
			}
		}
	}

	return(PhoneEntry);
}

	/* DeletePhonebook(struct PhoneEntry **PhoneBook,LONG Size,BYTE FreeEntries):
	 *
	 *	Deallocates a given phone book and its entries if necessary.
	 */

VOID __regargs
DeletePhonebook(struct PhoneEntry **Phonebook,LONG Size,BYTE FreeEntries)
{
	if(Phonebook)
	{
		if(FreeEntries)
		{
			LONG i;

			for(i = 0 ; i < Size ; i++)
			{
				if(Phonebook[i])
				{
					FreeTimeDateList((struct List *)&Phonebook[i] -> TimeDateList);

					if(Phonebook[i] -> Config)
						DeleteConfiguration(Phonebook[i] -> Config);

					if(Phonebook[i] -> NodeGroup)
					{
						Remove((struct Node *)Phonebook[i] -> NodeGroup);
						FreeVecPooled(Phonebook[i] -> NodeGroup);

						if(!Phonebook[i] -> ThisGroup -> GroupList . mlh_Head -> mln_Succ)
						{
							Remove((struct Node *)Phonebook[i] -> ThisGroup);
							FreeVecPooled(Phonebook[i] -> ThisGroup);
						}
					}

					FreeVecPooled(Phonebook[i]);
				}
			}
		}

		FreeVecPooled(Phonebook);
	}
}

	/* DeletePhoneGroupNode(PhoneGroupNode *GroupNode):
	 *
	 *	Delete all PhoneNodes stored in a phone group node list.
	 */

VOID __regargs
DeletePhoneGroupNode(PhoneGroupNode *GroupNode)
{
	struct PhoneNode *Node;

	while(Node = (struct PhoneNode *)RemHead((struct List *)&GroupNode -> GroupList))
	{
		Node -> Entry -> ThisGroup = NULL;
		Node -> Entry -> NodeGroup = NULL;

		FreeVecPooled(Node);
	}

	FreeVecPooled(GroupNode);
}

	/* DeletePhoneGroupList(struct MinList *List):
	 *
	 *	Delete a phone group node and its associated data.
	 */

VOID __regargs
DeletePhoneGroupList(struct MinList *List)
{
	PhoneGroupNode *Node;

	while(Node = (PhoneGroupNode *)RemHead((struct List *)List))
		DeletePhoneGroupNode(Node);
}

	/* InsertSorted(struct List *List,struct Node *Node):
	 *
	 *	Insert a node into a linked list whilst keeping the
	 *	list sorted in alphabetic order.
	 */

STATIC VOID __regargs
InsertSorted(struct List *List,struct Node *Node)
{
	struct Node	*Other;
	STRPTR		 Name;

	Name = Node -> ln_Name;

	for(Other = List -> lh_Head ; Other -> ln_Succ ; Other = Other -> ln_Succ)
	{
		if(Strcoll(Name,Other -> ln_Name) <= 0)
		{
			if(Other == List -> lh_Head)
				AddHead(List,Node);
			else
				Insert(List,Node,Other -> ln_Pred);

			return;
		}
	}

	AddTail(List,Node);
}

	/* CreatePhoneGroup(STRPTR Name,struct MinList *List):
	 *
	 *	Create a phone group with given name, unless the named group
	 *	already exists, in which case the existing group will be returned.
	 */

PhoneGroupNode * __regargs
CreatePhoneGroup(STRPTR Name,struct MinList *List)
{
	PhoneGroupNode *Node;

	for(Node = (PhoneGroupNode *)List -> mlh_Head ; Node -> Node . ln_Succ ; Node = (PhoneGroupNode *)Node -> Node . ln_Succ)
	{
		if(!Stricmp(Node -> Node . ln_Name,Name))
			return(Node);
	}

	if(Node = (PhoneGroupNode *)AllocVecPooled(sizeof(PhoneGroupNode),MEMF_ANY|MEMF_CLEAR))
	{
		Node -> Node . ln_Name = Node -> LocalName;

		strcpy(Node -> LocalName,Name);

		NewList((struct List *)&Node -> GroupList);

		InsertSorted((struct List *)List,(struct Node *)Node);
	}

	return(Node);
}

	/* AddGroupEntry(PhoneGroupNode *NewGroup,struct PhoneNode *Node):
	 *
	 *	Add an entry to a phone group. If the entry is already a member of
	 *	a different group, remove it from the other group and add it to the
	 *	given group. Do nothing if the entry is already a member of the
	 *	given group.
	 */

struct PhoneNode * __regargs
AddGroupEntry(PhoneGroupNode *NewGroup,struct PhoneNode *Node)
{
	if(Node -> Entry -> ThisGroup)
	{
		if(Node -> Entry -> ThisGroup == NewGroup)
			return(Node -> Entry -> NodeGroup);
		else
			Remove((struct Node *)Node -> Entry -> NodeGroup);
	}
	else
	{
		struct PhoneNode *Other;

		if(Other = (struct PhoneNode *)AllocVecPooled(sizeof(struct PhoneNode),MEMF_ANY|MEMF_CLEAR))
		{
			Other -> VanillaNode . ln_Name = Other -> LocalName;

			Other -> Entry = Node -> Entry;

			strcpy(Other -> LocalName,Node -> LocalName);

			Node -> Entry -> NodeGroup = Other;
		}
		else
			return(NULL);
	}

	InsertSorted((struct List *)&NewGroup -> GroupList,(struct Node *)Node -> Entry -> NodeGroup);

	Node -> Entry -> ThisGroup = NewGroup;

	return(Node -> Entry -> NodeGroup);
}
