//===========================================================================//
// File:     notation.cpp                                                    //
// Title:    Definition of NotationFile classes.                             //
// Project:  Munga                                                           //
// Author:   Ken Olsen                                                       //
// Purpose:  Provide general purpose access to data stored in a formatted    //
//           text file.                                                      //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/07/95  KEO  Converted from Tool Architecture I.                        //
// 02/07/95  KEO  Add AppendEntry methods.                                   //
// 02/17/95  KEO  Change page and entry lists to make nomenclature.          //
// 03/11/95  KEO  Remove all instances of strtok from class.                 //
// 03/24/95  KEO  Add methods to read and write text in memory.              //
// 03/31/95  KEO  Add methods for lab only mark.                             //
// 05/23/95  KEO  Add setpage and appendpage and allow setentry(,,null).     //
// 11/11/95  KEO  Add operation modes and correct small bugs.                //
//---------------------------------------------------------------------------//
// Copyright (c) 1994-1995 Virtual World Entertainment, Inc.                 //
//   All rights reserved worldwide.                                          //
// This unpublished source code is PROPRIETARY and CONFIDENTIAL.             //
//===========================================================================//

#include <munga.hpp>

#if !defined(NOTATION_HPP)
	#include <notation.hpp>
#endif

#if !defined(NAMELIST_HPP)
	#include <namelist.hpp>
#endif

//#############################################################################
//##############    NotationFile    ###########################################
//#############################################################################

NotationFile::NotationFile(
	const char *filename,
	Enumeration operation_modes
)
{
	Check_Pointer(this);
	//do not check filename here

	fileName = NULL;
	firstNotePage = lastNotePage = NULL;
	pageCount = cleanPageCount = 0;
	dirtyFlag = False;
	saveNotePage = NULL;
	operationModes = operation_modes;

	if (filename && *filename)
	{
		Check_Pointer(filename);
		ReadFile(filename);
	}
	Check(this);
}

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

	NotePage
		*next = firstNotePage;

	if (dirtyFlag)
	{
		if (fileName && *fileName && !WriteFile())
		{
			cerr << "NotationFile:  Error writing file '" << fileName <<
				"' - changes lost!" << endl;
		}
	}

	while (next)
	{
		Check(next);
		firstNotePage = next->nextNotePage;
		Unregister_Object(next);
		delete next;
		next = firstNotePage;
	}
	if (fileName)
	{
		Unregister_Pointer(fileName);
		delete fileName;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::PageExists(const char *pagename)
{
	// let FindNotePage() check this and pagename

	NotePage
		*notepage;

	return FindNotePage(pagename, &notepage);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	NotationFile::EntryCount(const char *pagename)
{
	// let FindNotePage() check this and pagename

	NotePage
		*notepage;

	if (FindNotePage(pagename, &notepage))
	{
		Check(notepage);
		return ((operationModes&CleanEntryListMode)?notepage->cleanNotationCount:notepage->notationCount);
	}
	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NotationFile::NotePage*
	NotationFile::SetPage(
		const char *pagename,
		const char *comment
	)
{
	// let FindNotePage() check this and pagename
	// let AppentPage() or SetComment() check comment

	NotePage
		*notepage;

	SetDirty();
	if (!FindNotePage(pagename, &notepage))
	{
		notepage = AppendPage(pagename, comment);
	}
	else
	{
		notepage->SetComment(comment);
	}
	return notepage;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NotationFile::NotePage*
	NotationFile::AppendPage(
		const char *pagename,
		const char *comment
	)
{
	Check(this);
	Check_Pointer(pagename);
	// let SetComment() check comment

	NotePage
		*notepage;

	SetDirty();
	notepage = new NotePage(this);
	Register_Object(notepage);
	notepage->SetName(pagename, &cleanPageCount);
	notepage->SetComment(comment);
	AppendNotePage(notepage);

	return notepage;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	NotationFile::GetEntry(
		const char *pagename,
		const char *entryname,
		const char **contents
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);
	Check_Pointer(contents);

	NotePage
		*notepage;
	Notation
		*notation;

	//---------------
	// return string
	//---------------
	if (FindNotePage(pagename, &notepage))
	{
		Check(notepage);
		if (notepage->FindNote(entryname, &notation))
		{
			Check(notation);
			*contents = notation->GetEntry();
			return True;
		}
	}
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	NotationFile::GetEntry(
		const char *pagename,
		const char *entryname,
		int *value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);
	Check_Pointer(value);

	const char
		*contents;

	//----------------
	// return integer
	//----------------
	if (GetEntry(pagename, entryname, &contents))
	{
		Check_Pointer(contents);
		*value = atoi(contents);
		return True;
	}
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	NotationFile::GetEntry(
		const char *pagename,
		const char *entryname,
		Scalar *value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);
	Check_Pointer(value);

	const char
		*contents;

	//---------------
	// return scalar
	//---------------
	if (GetEntry(pagename, entryname, &contents))
	{
		Check_Pointer(contents);
		*value = atof(contents);
		return True;
	}
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	NotationFile::GetEntry(
		const char *pagename,
		const char *entryname,
		double *value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);
	Check_Pointer(value);

	const char
		*contents;

	//---------------
	// return double
	//---------------
	if (GetEntry(pagename, entryname, &contents))
	{
		Check_Pointer(contents);
		*value = atof(contents);
		return True;
	}
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	NotationFile::GetLogicalEntry(
		const char *pagename,
		const char *entryname,
		Logical *value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);
	Check_Pointer(value);

	const char
		*contents;

	//----------------
	// return Logical
	//----------------
	if (GetEntry(pagename, entryname, &contents))
	{
		Check_Pointer(contents);
		//--------------------------------------------------------
		// these stricmp()'s must not affected by IgnoreCaseModes
		//--------------------------------------------------------
		*value = (!stricmp(contents, "true") || !stricmp(contents, "yes") || atoi(contents) != 0);
		return True;
	}
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::SetEntry(
		const char *pagename,
		const char *entryname,
		const char *contents
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);
	if (contents)  { Check_Pointer(contents); }

	NotePage
		*notepage;
	Notation
		*notation;

	SetDirty();
	//---------------
	// assign string
	//---------------
	if (!FindNotePage(pagename, &notepage))
	{
		notepage = AppendPage(pagename);
	}
	notation = new Notation;
	Register_Object(notation);
	notation->SetName(entryname, &(notepage->cleanNotationCount));
	notation->SetEntry(contents);
	notepage->UpdateNote(notation);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::SetEntry(
		const char *pagename,
		const char *entryname,
		int value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[12];
	ostrstream
		stream(contents, sizeof(contents));

	stream << value << ends;

	SetEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::SetEntry(
		const char *pagename,
		const char *entryname,
		Scalar value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[32];
	ostrstream
		stream(contents, sizeof(contents));

	stream << value << ends;

	SetEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::SetEntry(
		const char *pagename,
		const char *entryname,
		double value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[64];
	ostrstream
		stream(contents, sizeof(contents));

	stream << value << ends;

	SetEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::SetLogicalEntry(
		const char *pagename,
		const char *entryname,
		Logical value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[6];
	ostrstream
		stream(contents, sizeof(contents));

	if (value)
	{
		stream << "True" << ends;
	}
	else
	{
		stream << "False" << ends;
	}

	SetEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::AppendEntry(
		const char *pagename,
		const char *entryname,
		const char *contents
	)
{
	Check(this);
	Check_Pointer(pagename);
	if (entryname) { Check_Pointer(entryname); }
	if (contents)  { Check_Pointer(contents); }

	NotePage
		*notepage;
	Notation
		*notation;

	SetDirty();
	//---------------
	// assign string
	//---------------
	if (!FindNotePage(pagename, &notepage))
	{
		notepage = AppendPage(pagename);
	}
	notation = new Notation;
	Register_Object(notation);
	notation->SetName(entryname, &(notepage->cleanNotationCount));
	notation->SetEntry(contents);
	notepage->AppendNote(notation);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::AppendEntry(
		const char *pagename,
		const char *entryname,
		int value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[12];
	ostrstream
		stream(contents, sizeof(contents));

	stream << value << ends;

	AppendEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::AppendEntry(
		const char *pagename,
		const char *entryname,
		Scalar value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[32];
	ostrstream
		stream(contents, sizeof(contents));

	stream << value << ends;

	AppendEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::AppendEntry(
		const char *pagename,
		const char *entryname,
		double value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[64];
	ostrstream
		stream(contents, sizeof(contents));

	stream << value << ends;

	AppendEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::AppendLogicalEntry(
		const char *pagename,
		const char *entryname,
		Logical value
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	static char
		contents[6];
	ostrstream
		stream(contents, sizeof(contents));

	if (value)
	{
		stream << "True" << ends;
	}
	else
	{
		stream << "False" << ends;
	}

	AppendEntry(pagename, entryname, contents);
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList*
	NotationFile::MakeEntryList(
		const char *pagename,
		const char *entryname_prefix
	)
{
	Check(this);
	Check_Pointer(pagename);
	//do not check entryname_prefix here

	NotePage
		*notepage;

	//--------------------------------------------------
	// return namelist of matching entries and contents
	//--------------------------------------------------
	if (FindNotePage(pagename, &notepage))
	{
		return MakeEntryList(notepage, entryname_prefix);
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList*
	NotationFile::MakeEntryList(
		NotePage *notepage,
		const char *entryname_prefix
	)
{
	Check(this);
	Check(notepage);
	//do not check entryname_prefix here

	const char
		*name_prefix = entryname_prefix?entryname_prefix:"\0";
	Check_Pointer(name_prefix);
	const int
		length = strlen(name_prefix);
	NameList
		*namelist;
	Notation
		*notation;
	const char
		*name;
	Logical
		prefix_matches,
		add_entry;

	//--------------------------------------------------
	// return namelist of matching entries and contents
	//--------------------------------------------------
	namelist = new NameList;
	//do not register namelist
	notation = notepage->firstNotation;
	while (notation)
	{
		Check(notation);
		name = notation->GetName();
		if (name)
		{
			Check_Pointer(name);
			if (operationModes&IgnoreEntryCaseMode)
			{
				prefix_matches = (!strnicmp(name, name_prefix, length));
			}
			else
			{
				prefix_matches = (!strncmp(name, name_prefix, length));
			}
		}
		else
		{
			prefix_matches = (entryname_prefix == NULL);
		}
		if (operationModes&CleanEntryListMode)
		{
			add_entry = (notation->cleanNotation && prefix_matches);
		}
		else if (name)
		{
			add_entry = (prefix_matches);
		}
		else if (operationModes&SkipBlanksListMode)
		{
			add_entry = False;
		}
		else
		{
			//------------------------------------------------------
			// HACK:  NameList::AddEntry() should accept name==NULL
			//------------------------------------------------------
			add_entry = False;
			//add_entry = (prefix_matches);
		}
		if (add_entry)
		{
			if (notation->GetEntry())
				{ Check_Pointer(notation->GetEntry()); }
			namelist->AddEntry(name, (void *)notation->GetEntry());
		}
		notation = notation->nextNotation;
	}
	return namelist;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NameList*
	NotationFile::MakePageList(const char *pagename_prefix)
{
	Check(this);
	//do not check pagename_prefix here

	const char
		*name_prefix = pagename_prefix?pagename_prefix:"\0";
	Check_Pointer(name_prefix);
	const int
		length = strlen(name_prefix);
	NameList
		*namelist;
	NotePage
		*notepage;
	const char
		*name;
	Logical
		prefix_matches,
		add_page;

	//-----------------------------------------------------
	// return namelist of matching pagenames and notepages
	//-----------------------------------------------------
	namelist = new NameList;
	//do not register namelist
	notepage = firstNotePage;
	while (notepage)
	{
		Check(notepage);
		name = notepage->GetName();
		if (name)
		{
			Check_Pointer(name);
			if (operationModes&IgnorePageCaseMode)
			{
				prefix_matches = (!strnicmp(name, name_prefix, length));
			}
			else
			{
				prefix_matches = (!strncmp(name, name_prefix, length));
			}
		}
		else
		{
			prefix_matches = (pagename_prefix == NULL);
		}
		if (operationModes&CleanPageListMode)
		{
			add_page = (notepage->cleanPage && prefix_matches);
		}
		else if (name)
		{
			add_page = (prefix_matches);
		}
		else if (operationModes&SkipBlanksListMode)
		{
			add_page = False;
		}
		else
		{
			//------------------------------------------------------
			// HACK:  NameList::AddEntry() should accept name==NULL
			//------------------------------------------------------
			add_page = False;
			//add_page = (pagename_prefix==NULL);
		}
		if (add_page)
		{
			namelist->AddEntry(name, (void *)notepage);
		}
		notepage = notepage->nextNotePage;
	}
	return namelist;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AlphaNameList*
	NotationFile::MakeAlphaEntryList(
		const char *pagename,
		const char *entryname_prefix
	)
{
	Check(this);
	Check_Pointer(pagename);
	//do not check entryname_prefix here

	NotePage
		*notepage;

	//-------------------------------------------------------
	// return alphanamelist of matching entries and contents
	//-------------------------------------------------------
	if (FindNotePage(pagename, &notepage))
	{
		return MakeAlphaEntryList(notepage, entryname_prefix);
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AlphaNameList*
	NotationFile::MakeAlphaEntryList(
		NotePage *notepage,
		const char *entryname_prefix
	)
{
	Check(this);
	Check(notepage);
	//do not check entryname_prefix here

	const char
		*name_prefix = entryname_prefix?entryname_prefix:"\0";
	Check_Pointer(name_prefix);
	const int
		length = strlen(name_prefix);
	AlphaNameList
		*namelist;
	Notation
		*notation;
	const char
		*name;
	Logical
		prefix_matches,
		add_entry;

	//-------------------------------------------------------
	// return alphanamelist of matching entries and contents
	//-------------------------------------------------------
	namelist = new AlphaNameList;
	//do not register namelist
	notation = notepage->firstNotation;
	while (notation)
	{
		Check(notation);
		name = notation->GetName();
		if (name)
		{
			Check_Pointer(name);
			if (operationModes&IgnoreEntryCaseMode)
			{
				prefix_matches = (!strnicmp(name, name_prefix, length));
			}
			else
			{
				prefix_matches = (!strncmp(name, name_prefix, length));
			}
		}
		else
		{
			prefix_matches = (entryname_prefix == NULL);
		}
		if (operationModes&CleanEntryListMode)
		{
			add_entry = (notation->cleanNotation && prefix_matches);
		}
		else if (name)
		{
			add_entry = (prefix_matches);
		}
		else if (operationModes&SkipBlanksListMode)
		{
			add_entry = False;
		}
		else
		{
			//------------------------------------------------------
			// HACK:  NameList::AddEntry() should accept name==NULL
			//------------------------------------------------------
			add_entry = False;
			//add_entry = (entryname_prefix==NULL);
		}
		if (add_entry)
		{
			if (notation->GetEntry())
				{ Check_Pointer(notation->GetEntry()); }
			namelist->AddEntry(name, (void *)notation->GetEntry());
		}
		notation = notation->nextNotation;
	}
	return namelist;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AlphaNameList*
	NotationFile::MakeAlphaPageList(const char *pagename_prefix)
{
	Check(this);
	//do not check pagename_prefix here

	const char
		*name_prefix = pagename_prefix?pagename_prefix:"\0";
	Check_Pointer(name_prefix);
	const int
		length = strlen(name_prefix);
	AlphaNameList
		*namelist;
	NotePage
		*notepage;
	const char
		*name;
	Logical
		prefix_matches,
		add_page;

	//----------------------------------------------------------
	// return alphanamelist of matching pagenames and notepages
	//----------------------------------------------------------
	namelist = new AlphaNameList;
	//do not register namelist
	notepage = firstNotePage;
	while (notepage)
	{
		Check(notepage);
		name = notepage->GetName();
		if (name)
		{
			Check_Pointer(name);
			if (operationModes&IgnorePageCaseMode)
			{
				prefix_matches = (!strnicmp(name, name_prefix, length));
			}
			else
			{
				prefix_matches = (!strncmp(name, name_prefix, length));
			}
		}
		else
		{
			prefix_matches = (pagename_prefix == NULL);
		}
		if (operationModes&CleanPageListMode)
		{
			add_page = (notepage->cleanPage && prefix_matches);
		}
		else if (name)
		{
			add_page = (prefix_matches);
		}
		else if (operationModes&SkipBlanksListMode)
		{
			add_page = False;
		}
		else
		{
			//------------------------------------------------------
			// HACK:  NameList::AddEntry() should accept name==NULL
			//------------------------------------------------------
			add_page = False;
			//add_page = (pagename_prefix==NULL);
		}
		if (add_page)
		{
			namelist->AddEntry(name, (void *)notepage);
		}
		notepage = notepage->nextNotePage;
	}
	return namelist;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::DeleteEntry(
		const char *pagename,
		const char *entryname
	)
{
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(entryname);

	NotePage
		*notepage;

	//---------------------------
	// if found, delete Notation
	//---------------------------
	if (FindNotePage(pagename, &notepage))
	{
		Check(notepage);
		SetDirty();
		notepage->DeleteNote(entryname);
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::DeletePage(const char *pagename)
{
	Check(this);
	Check_Pointer(pagename);

	NotePage
		*notepage,
		*prev;

	//----------------------------
	// if found, delete Note_Page
	//----------------------------
	if (FindNotePage(pagename, &notepage, &prev))
	{
		DeletePage(notepage, prev);
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::ReadFile(const char *filename)
{
	Check(this);
	Check_Pointer(filename);

	ifstream
		notefile(filename);
	char
		buffer[512];
	Logical
		was_empty = IsEmpty();

	//-------------------------------
	// store first notation filename
	//-------------------------------
	if (!fileName)
	{
		fileName = new char[strlen(filename) + 1];
		Register_Pointer(fileName);
		strcpy(fileName, filename);
	}

	//--------------------
	// read notation file
	//--------------------
	if (notefile)
	{
		//Dump( "--- read notation file ---" );

		while (notefile.getline(buffer, sizeof(buffer)))
		{
			//------------------------------
			// process line of NotationFile
			//------------------------------
			Read(buffer);
		}
		notefile.close();
		Read(NULL);	// (reset static variable)

		//Dump( "--- end ---" );
	}
	if (was_empty)
	{
		dirtyFlag = False;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::ReadText(
		char *start,
		long length
	)
{
	Check(this);
	Check_Pointer(start);

	char
		*stop,
		*buffer;
	Logical
		was_empty = IsEmpty();

	//--------------------------------
	// read notation file from memory
	//--------------------------------
	stop = start + length;
	while (start < stop)
	{
		//---------------------------------------------------------
		// this must preceed Read() which does not preserve buffer
		//---------------------------------------------------------
		buffer = start;
		start = strchr(buffer, '\0') + 1;

		//------------------------------
		// process line of NotationFile
		//------------------------------
		Read(buffer);
	}
	Read(NULL);	// (reset static variable)
	if (was_empty)
	{
		dirtyFlag = False;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::WriteFile(const char *filename)
{
	Check(this);
	//do not check filename

	ofstream output;

	if (filename && *filename)
	{
		Check_Pointer(filename);
		output.open(filename);
	}
	else if (fileName && *fileName)
	{
		Check_Pointer(fileName);
		output.open(fileName);
	}
	else
	{
		return False;	// you tried to WriteFile with no default fileName!
	}

	if (output)
	{
		Write(output);
		output.close();
		if (filename && *filename)
		{
			if (fileName)
			{
				Unregister_Pointer(fileName);
				delete fileName;
			}
			fileName = new char[strlen(filename) + 1];
			Register_Pointer(fileName);
			strcpy(fileName, filename);
		}
		dirtyFlag = False;
		return True;
	}
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	NotationFile::SizeOfText()
{
	Check(this);

	ofstream
		dummy;

	return Write(dummy, Size_Only);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	NotationFile::WriteText(
		char *buffer,
		long size
	)
{
	Check(this);
	Check_Pointer(buffer);
	Verify( size >= 0 );

	ostrstream
		output(buffer, size);
	long
		actual_size;

	if ((actual_size = Write(output, Text_Memory)) <= size)
	{
		dirtyFlag = False;
		return actual_size;
	}

	return -1L;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::Abort()
{
	Check(this);
	dirtyFlag = False;
	this->~NotationFile();
	//this->NotationFile(); //does not compile!
	*this = NotationFile();
	Check(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::DeleteStandardPages()
{
	Check(this);

	NotePage
		*prev = NULL,
		*notepage = firstNotePage,
		*next;
	const char
		*name;

	while (notepage)
	{
		Check(notepage);
		next = notepage->nextNotePage;
		name = notepage->pageName;
		if (*name != '_' && strcmp(name, "LAB_ONLY") != 0)
		{
			DeletePage(notepage, prev);
		}
		else
		{
			prev = notepage;
		}
		notepage = next;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::MarkLabOnly()
{
	Check(this);

	if (!IsMarkedLabOnly())
	{
		SetPage("LAB_ONLY","\t// this file has not been approved for release");
		AppendEntry("LAB_ONLY", NULL, (char *)NULL);	// (blank line)
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::AppendNotePage(NotePage *notepage)
{
	Check(this);
	Check(notepage);

	//---------------------------------
	// append NotePage to NotationFile
	//---------------------------------
	if (!lastNotePage)
	{
		firstNotePage = notepage;
	}
	else
	{
		Check(lastNotePage);
		lastNotePage->nextNotePage = notepage;
	}
	lastNotePage = notepage;
	lastNotePage->nextNotePage = NULL;
	if (notepage->cleanPage)
	{
		++cleanPageCount;
	}
	++pageCount;
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::FindNotePage(
		const char *pagename,
		NotePage **notepage
	)
{
	//-----------------------------------------------------------------
	// HACK:  This routine should be changed to support NULL pagename.
	//-----------------------------------------------------------------
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(notepage);

	//-------------------------------------------
	// check to see if same as previous NotePage
	//-------------------------------------------
	if (saveNotePage &&
		saveNotePage->pageName &&
		((operationModes&IgnorePageCaseMode &&
		!stricmp(saveNotePage->pageName, pagename)) ||
		(!(operationModes&IgnorePageCaseMode) &&
		!strcmp(saveNotePage->pageName, pagename))))
	{
		Check(saveNotePage);
		*notepage = saveNotePage;
		return True;
	}
	else
	{
		//------------------------------
		// search for matching NotePage
		//------------------------------
		NotePage
			*notepg = firstNotePage;

		while (notepg)
		{
			Check(notepg);
			if (notepg->pageName &&
					((operationModes&IgnorePageCaseMode &&
					!stricmp(notepg->pageName, pagename)) ||
					(!(operationModes&IgnorePageCaseMode) &&
					!strcmp(notepg->pageName, pagename))))
			{
				*notepage = saveNotePage = notepg;
				return True;
			}
			notepg = notepg->nextNotePage;
		}
	}
	//-----------
	// not found
	//-----------
	*notepage = NULL;
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::FindNotePage(
		const char *pagename,
		NotePage **notepage,
		NotePage **prevpage
	)
{
	//-----------------------------------------------------------------
	// HACK:  This routine should be changed to support NULL pagename.
	//-----------------------------------------------------------------
	Check(this);
	Check_Pointer(pagename);
	Check_Pointer(notepage);
	Check_Pointer(prevpage);

	NotePage
		*notepg = firstNotePage,
		*prev = NULL;

	//------------------------------
	// search for matching NotePage
	//------------------------------
	while (notepg)
	{
		Check(notepg);
		if (notepg->pageName &&
				((operationModes&IgnorePageCaseMode &&
				!stricmp(notepg->pageName, pagename)) ||
				(!(operationModes&IgnorePageCaseMode) &&
				!strcmp(notepg->pageName, pagename))))
		{
			*notepage = notepg;
			*prevpage = prev;
			return True;
		}
		prev = notepg;
		notepg = notepg->nextNotePage;
	}
	//-----------
	// not found
	//-----------
	*notepage = *prevpage = NULL;
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::DeletePage(
		NotePage *notepage,
		NotePage *prev
	)
{
	Check(this);
	Check(notepage);
	//do not check prev here

	//---------------------------
	// remove & delete Note_Page
	//---------------------------
	SetDirty();

	if (prev)
	{
		Check(prev);
		prev->nextNotePage = notepage->nextNotePage;
	}
	else
	{
		firstNotePage = notepage->nextNotePage;
	}
	if (lastNotePage == notepage)
	{
		lastNotePage = prev;
	}
	if (notepage == saveNotePage)
	{
		saveNotePage = NULL;
	}
	if (notepage->cleanPage)
	{
		--cleanPageCount;
	}
	--pageCount;
	Unregister_Object(notepage);
	delete notepage;

	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::Read(char *buffer)
{
	Check(this);
	//do not check buffer here

	static NotePage
		*notepage = NULL;
	Notation
		*notation;
	char
		*token,
		*entry,
		*p;
	Logical
//		blank_line,
		comment_line;

	if (!buffer)
	{
		//------------------------------
		// reset notepage for next read
		//------------------------------
		notepage = NULL;
		return;
	}
	Check_Pointer(buffer);

	//--------------------------------
	// find first non-blank character
	//--------------------------------
	p = buffer;
	while (*p == ' ' || *p == '\t')
	{
		++p;
	}

	//--------------------
	// begin new NotePage
	//--------------------
	if (*p == '[')
	{
		token = p+1;
		if ((p = strchr(token, ']')) != NULL)
		{
			*p = '\0';
			++p;
		}
		notepage = AppendPage(token, p);
//		cerr << "[" << notepage->GetName() << "]" << endl;	// TESTING!
//		notepage->Write( cerr );	// TESTING!
		return;
	}

	//---------------------------------
	// detect blank lines and comments
	//---------------------------------
//	blank_line = (*p == '\0');
	comment_line = Comment_Line(p);

	//---------------
	// read Notation
	//---------------
	token = p;
	if ((p = strchr(token, '=')) != NULL)
	{
		*p = '\0';
		entry = ++p;
//		if (*entry == '\0')
//		{
//			entry = NULL;
//		}
	}
	else
	{
		entry = NULL;
	}
	if (*token == '\0')
	{
		token = NULL;
	}

	//------------------------
	// remove trailing blanks
	//------------------------
	if (token && !comment_line)
	{
		p = strchr(token, '\0') - 1;
		while (*p == ' ' || *p == '\t')
		{
			*p = '\0';
			if (--p < token)
			{
				break;
			}
		}
	}

	if (!notepage)
	{
		notepage = new NotePage(this);
		Register_Object(notepage);
		AppendNotePage(notepage);
	}
	notation = new Notation;
	Register_Object(notation);
	notation->SetName(token, &(notepage->cleanNotationCount));
	notation->SetEntry(entry);
	notepage->AppendNote(notation);
//	cerr << token << "=" << entry << endl;	// TESTING!
//	notation->Write( cerr );	// TESTING!
//	notepage->Write( cerr );	// TESTING!

	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	NotationFile::Write(
		ostream &stream,
		WriteMode mode
	)
{
	Check(this);

	NotePage
		*notepg = firstNotePage;
	long
		size = 0;
			
	while (notepg)
	{
		Check(notepg);
		size += notepg->WriteNote(stream, mode);
		notepg = notepg->nextNotePage;
	}
	return size;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::TestInstance() const
{
	//do not Check(this);
	if (fileName)       { Check_Pointer(fileName); }
	if (firstNotePage)  { Check_Signature(firstNotePage); }
	if (lastNotePage)   { Check_Signature(lastNotePage); }
	if (saveNotePage)   { Check_Signature(saveNotePage); }
	return True;
}

//#############################################################################
//##############    NotationFile::Notation    #################################
//#############################################################################

NotationFile__Notation::~NotationFile__Notation()
{
	Check(this);

	if (notationName)
	{
		Check_Pointer(notationName);
		Unregister_Pointer(notationName);
		delete notationName;
	}
	if (notationEntry)
	{
		Check_Pointer(notationEntry);
		Unregister_Pointer(notationEntry);
		delete notationEntry;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::Notation::SetName(
		const char *entryname,
		long *clean_notation_count
	)
{
	Check(this);
	//do not check entryname here
	Check_Pointer(clean_notation_count);

	if (notationName)
	{
		if (cleanNotation)
		{
			cleanNotation = False;
			--(*clean_notation_count);
		}
		Check_Pointer(notationName);
		Unregister_Pointer(notationName);
		delete notationName;
	}
	if (entryname)	// (do not add "&& *entryname")
	{
		Check_Pointer(entryname);
		notationName = new char[strlen(entryname) + 1];
		Register_Pointer(notationName);
		strcpy(notationName, entryname);
		cleanNotation = (*notationName && !Comment_Line(notationName));
	}
	else
	{
		notationName = NULL;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::Notation::SetEntry(const char *contents)
{
	Check(this);
	//do not check contents here

	if (notationEntry)
	{
		Check_Pointer(notationEntry);
		Unregister_Pointer(notationEntry);
		delete notationEntry;
	}
	if (contents)	// (do not add "&& *contents")
	{
		Check_Pointer(contents);
		notationEntry = new char[strlen(contents) + 1];
		Register_Pointer(notationEntry);
		strcpy(notationEntry, contents);
	}
	else
	{
		notationEntry = NULL;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	NotationFile::Notation::WriteNotation(
		ostream &stream,
		NotationFile::WriteMode mode
	) const
{
	Check(this);

//	Logical
//		comment_line = True;
	long
		size = 0;

	if (notationName)
	{
		Check_Pointer(notationName);
		if (mode)
		{
			stream << notationName;
		}
		if (mode != NotationFile::Disk_File)
		{
			size += strlen(notationName);
		}
//		comment_line = Comment_Line(notationName);
//		if (!comment_line)
//		{
//			stream << "=";
//		}
	}
	if (notationEntry)
	{
		Check_Pointer(notationEntry);
//		if (comment_line)
//		{
//			stream << "=";
//		}
		if (mode)
		{
			stream << "=" << notationEntry;
		}
		if (mode != NotationFile::Disk_File)
		{
			size += strlen(notationEntry) + 1;
		}
	}
	if (mode == NotationFile::Disk_File)
	{
		stream << endl;
	}
	else if (mode)
	{
		stream << ends;
	}
	if (mode != NotationFile::Disk_File)
	{
		size += 1;
	}

	return size;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::Notation::TestInstance() const
{
	//do not Check(this);
	if (nextNotation)   { Check_Signature(nextNotation); }
	if (notationName)   { Check_Pointer(notationName); }
	if (notationEntry)  { Check_Pointer(notationEntry); }
	return True;
}

//#############################################################################
//##############    NotationFile::NotePage    #################################
//#############################################################################

NotationFile__NotePage::NotationFile__NotePage(NotationFile *notation_file)
{
	Check_Pointer(this);
	Check(notation_file);

	notationFile = notation_file;
	nextNotePage = NULL;
	firstNotation = lastNotation = NULL;
	notationCount = cleanNotationCount = 0;
	pageName = pageComment = NULL;
	cleanPage = False;
}

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

	NotationFile::Notation
		*notation = firstNotation;

	while (notation)
	{
		Check(notation);
		firstNotation = notation->nextNotation;
		Unregister_Object(notation);
		delete notation;
		notation = firstNotation;
	}
	if (pageName)
	{
		Check_Pointer(pageName);
		Unregister_Pointer(pageName);
		delete pageName;
	}
	if (pageComment)
	{
		Check_Pointer(pageComment);
		Unregister_Pointer(pageComment);
		delete pageComment;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::NotePage::SetName(
		const char *pagename,
		long *clean_page_count
	)
{
	Check(this);
	//do not check pagename here
	Check_Pointer(clean_page_count);

	if (pageName)
	{
		if (cleanPage)
		{
			cleanPage = False;
			--(*clean_page_count);
		}
		Check_Pointer(pageName);
		Unregister_Pointer(pageName);
		delete pageName;
	}
	if (pagename)	// (do not add "&& *pagename")
	{
		Check_Pointer(pagename);
		pageName = new char[strlen(pagename) + 1];
		Register_Pointer(pageName);
		strcpy(pageName, pagename);
		cleanPage = (*pageName && (*pageName != '!') && strcmp(pageName, "LAB_ONLY"));
	}
	else
	{
		pageName = NULL;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::NotePage::SetComment(const char *comment)
{
	Check(this);
	//do not check comment here

	if (pageComment)
	{
		Check_Pointer(pageComment);
		Unregister_Pointer(pageComment);
		delete pageComment;
	}
	if (comment && *comment)
	{
		Check_Pointer(comment);
		pageComment = new char[strlen(comment) + 1];
		Register_Pointer(pageComment);
		strcpy(pageComment, comment);
	}
	else
	{
		pageComment = NULL;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::NotePage::AppendNote(NotationFile::Notation *note)
{
	Check(this);
	Check(note);

	//-----------------------------
	// append Notation to NotePage
	//-----------------------------
	if (!lastNotation)
	{
		firstNotation = note;
	}
	else
	{
		Check(lastNotation);
		lastNotation->nextNotation = note;
	}
	lastNotation = note;
	lastNotation->nextNotation = NULL;
	if (note->cleanNotation)
	{
		++cleanNotationCount;
	}
	++notationCount;
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::NotePage::FindNote(
		const char *entryname,
		NotationFile::Notation **note
	)
{
	NotationFile::Notation
		*prev;

	return FindNote(entryname, note, &prev);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::NotePage::FindNote(
		const char *entryname,
		NotationFile::Notation **note,
		NotationFile::Notation **prev
	)
{
	//------------------------------------------------------------------
	// HACK:  This routine should be changed to support NULL entryname.
	//------------------------------------------------------------------
	Check(this);
	Check_Pointer(entryname);
	Check_Pointer(note);
	Check_Pointer(prev);

	NotationFile::Notation
		*notation = firstNotation,
		*previous = NULL;

	//------------------------------
	// search for matching Notation
	//------------------------------
	while (notation)
	{
		Check(notation);
		if (notation->notationName &&
				((notationFile->operationModes&NotationFile::IgnoreEntryCaseMode &&
				!stricmp(notation->notationName, entryname)) ||
				(!(notationFile->operationModes&NotationFile::IgnoreEntryCaseMode) &&
				!strcmp(notation->notationName, entryname))))
		{
			*note = notation;
			*prev = previous;
			return True;
		}
		previous = notation;
		notation = notation->nextNotation;
	}
	//-----------
	// not found
	//-----------
	*note = *prev = NULL;
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::NotePage::UpdateNote(NotationFile::Notation *note)
{
	Check(this);
	Check(note);

	NotationFile::Notation
		*notation,
		*prev;

	//------------------------------
	// search for matching Notation
	//------------------------------
	if (FindNote(note->notationName, &notation, &prev))
	{
		Check(notation);

		//------------------
		// replace Notation
		//------------------
		if (prev)
		{
			Check(prev);
			prev->nextNotation = note;
		}
		else
		{
			firstNotation = note;
		}
		if ((note->nextNotation = notation->nextNotation) == NULL)
		{
			lastNotation = note;
		}
		Unregister_Object(notation);
		delete notation;
	}
	else
	{
		//-----------------
		// append Notation
		//-----------------
		AppendNote(note);
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NotationFile::NotePage::DeleteNote(const char *entryname)
{
	Check(this);
	Check_Pointer(entryname);

	NotationFile::Notation
		*notation,
		*prev;

	//------------------------------
	// search for matching Notation
	//------------------------------
	if (FindNote(entryname, &notation, &prev))
	{
		Check(notation);

		//--------------------------
		// remove & delete Notation
		//--------------------------
		if (prev)
		{
			Check(prev);
			prev->nextNotation = notation->nextNotation;
		}
		else
		{
			firstNotation = notation->nextNotation;
		}
		if (lastNotation == notation)
		{
			lastNotation = prev;
		}
		if (notation->cleanNotation)
		{
			--cleanNotationCount;
		}
		--notationCount;
		Unregister_Object(notation);
		delete notation;
	}
	return;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	NotationFile::NotePage::WriteNote(
		ostream &stream,
		NotationFile::WriteMode mode
	) const
{
	Check(this);

	NotationFile::Notation
		*notation = firstNotation;
	long
		size = 0;

	if (pageName)
	{
		Check_Pointer(pageName);
		if (mode)
		{
			stream << "[" << pageName << "]";
		}
		if (mode != NotationFile::Disk_File)
		{
			size += strlen(pageName) + 2;
		}
	}
	if (pageComment)
	{
		Check_Pointer(pageComment);
		if (mode)
		{
			stream << pageComment;
		}
		if (mode != NotationFile::Disk_File)
		{
			size += strlen(pageComment);
		}
	}
	if (pageName || pageComment)
	{
		if (mode == NotationFile::Disk_File)
		{
			stream << endl;
		}
		else if (mode)
		{
			stream << ends;
		}
		if (mode != NotationFile::Disk_File)
		{
			size += 1;
		}
	}
	while (notation)
	{
		Check(notation);
		size += notation->WriteNotation(stream, mode);
		notation = notation->nextNotation;
	}
	return size;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	NotationFile::NotePage::TestInstance() const
{
	//do not Check(this);
	if (nextNotePage)   { Check_Signature(nextNotePage); }
	if (firstNotation)  { Check_Signature(firstNotation); }
	if (lastNotation)   { Check_Signature(lastNotation); }
	if (pageName)       { Check_Pointer(pageName); }
	return True;
}

//#############################################################################
//##############    TestClass()    ############################################
//#############################################################################

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

//===========================================================================//
