#pragma once

#include "namelist.h"
#include "scalar.h"

class NotationFile;
class NotationFile__Notation;
class NotationFile__NotePage;
class NameList;
class AlphaNameList;

inline Logical Comment_Line(const char *p)	{ return ((*p == ';') || (*p == '#') || (*p == '/' && *(p+1) == '/')); }

class NotationFile SIGNATURED
{
	friend class NotationFile__NotePage;
	friend class NotationFile__Notation;

public:
	typedef NotationFile__Notation	Notation;
	typedef NotationFile__NotePage	NotePage;

private:
	char *fileName;
	NotePage *firstNotePage, *lastNotePage;
	long pageCount, cleanPageCount;
	Logical dirtyFlag;
	NotePage *saveNotePage;
	Enumeration operationModes;

	enum WriteMode
	{
		//--------------------------------------------
		// NOTE:  These numbers are not arbitrary!
		// See code before changing or adding values.
		//--------------------------------------------
		Size_Only = 0,
		Disk_File = 1,
		Text_Memory = 2,
		Binary_Memory = 3	// not implemented yet
	};

public:

#if 0
	enum Mode
	{
		// Access Modes:
		EXCLUSIVE = 0,
		SHARED = 1,
		// Error Modes:
		QUIET = 0,
		FATAL = 2,
		// Default Modes:
		DEFAULT = (EXCLUSIVE | QUIET)
	};
#endif

	enum OperationModes
	{
		RawOperationModes		=	0x0000,
		SkipBlanksListMode		=	0x0001,
		CleanPageListMode		=	0x0002,
		CleanEntryListMode		=	0x0004,
		IgnorePageCaseMode		=	0x0008,
		IgnoreEntryCaseMode		=	0x0010,
		DefaultOperationModes	=	(SkipBlanksListMode),
		CleanListMode			=	(CleanPageListMode | CleanEntryListMode),
		IgnoreCaseMode			=	(IgnorePageCaseMode | IgnoreEntryCaseMode),
		RelaxedOperationModes	=	(CleanListMode | IgnoreCaseMode)
	};

	NotationFile(const char *filename = NULL, Enumeration operation_modes = DefaultOperationModes);
	~NotationFile();

	Enumeration GetModes()	{ Check(this); return operationModes; }
	Enumeration PutModes(Enumeration operation_modes)	{ Check(this); return (operationModes = operation_modes); }
	Enumeration SetMode(Enumeration operation_modes)	{ Check(this); return (operationModes |= operation_modes); }
	Enumeration ClearMode(Enumeration operation_modes)	{ Check(this); return (operationModes &= ~operation_modes); }
	Logical IsDirty() const		{ Check(this); return dirtyFlag; }
	Logical IsEmpty() const		{ Check(this); return (pageCount == 0L); }
	const char *GetFileName() const	{ Check(this); return fileName; }
	long PageCount() const		{ Check(this); return ((operationModes & CleanPageListMode) ? cleanPageCount:pageCount); }
	Logical PageExists(const char *pagename);
	long EntryCount(const char *pagename);

	NotePage* SetPage(const char *pagename, const char *comment = NULL);
	NotePage* AppendPage(const char *pagename, const char *comment = NULL);

	int GetEntry(const char *pagename, const char *entryname, const char **contents);
	int GetEntry(const char *pagename, const char *entryname, int *value);
	int GetEntry(const char *pagename, const char *entryname, Scalar *value);
	int GetEntry(const char *pagename, const char *entryname, double *value);
	int GetLogicalEntry(const char *pagename, const char *entryname, Logical *value);
	
	void SetEntry(const char *pagename, const char *entryname, int value);
	void SetEntry(const char *pagename, const char *entryname, Scalar value);
	void SetEntry(const char *pagename, const char *entryname, double value);
	void SetEntry(const char *pagename, const char *entryname, const char *contents);
	void SetLogicalEntry(const char *pagename, const char *entryname, Logical value);
	
	void AppendEntry(const char *pagename, const char *entryname, const char *contents);
	void AppendEntry(const char *pagename, const char *entryname, int value);
	void AppendEntry(const char *pagename, const char *entryname, Scalar value);
	void AppendEntry(const char *pagename, const char *entryname, double value);
	void AppendLogicalEntry(const char *pagename, const char *entryname, Logical value);

	NameList* MakeEntryList(const char *pagename, const char *entryname_prefix = NULL);
	NameList* MakeEntryList(NotationFile::NotePage *notepage, const char *entryname_prefix = NULL);
	NameList* MakePageList(const char *pagename_prefix = NULL);
	
	AlphaNameList* MakeAlphaEntryList(const char *pagename, const char *entryname_prefix = NULL);
	AlphaNameList* MakeAlphaEntryList(NotationFile::NotePage *notepage, const char *entryname_prefix = NULL);
	AlphaNameList* MakeAlphaPageList(const char *pagename_prefix = NULL);

	void DeleteEntry(const char *pagename, const char *entryname);
	void DeletePage(const char *pagename);
	void ReadFile(const char *filename);
	void ReadText(char *start, long length);
	Logical WriteFile(const char *filename = NULL);
	long SizeOfText();
	long WriteText(char *buffer, long size);
	void Abort();

	void DeleteStandardPages();
	void MarkLabOnly();
	Logical IsMarkedLabOnly()	{ Check(this); return PageExists("LAB_ONLY"); }

private:
	void AppendNotePage(NotePage *notepage);
	Logical FindNotePage(const char *pagename, NotePage **notepage);
	Logical FindNotePage(const char *pagename, NotePage **notepage, NotePage **prevpage);
	void SetDirty()	{ Check(this); dirtyFlag = true; }
	void DeletePage(NotePage *notepage, NotePage *prev);
	void Read(char *buffer);
	long Write(std::ostream &stream, WriteMode mode = Disk_File);

public:
	Logical TestInstance() const;
	static Logical TestClass();
};

class NotationFile__Notation SIGNATURED
{
	friend class NotationFile;
	friend class NotationFile__NotePage;

private:
	NotationFile::Notation *nextNotation;
	char *notationName, *notationEntry;
	Logical cleanNotation;

	NotationFile__Notation()
	{
		nextNotation = NULL;
		notationName = notationEntry = NULL;
		cleanNotation = false;
	}
	~NotationFile__Notation();

	void SetName(const char *entryname, long *clean_notation_count);
	void SetEntry(const char *contents);
	const char* GetName() const		{ Check(this); return notationName; }
	const char* GetEntry() const	{ Check(this); return notationEntry; }
	long WriteNotation(std::ostream &stream, NotationFile::WriteMode mode) const;
	Logical TestInstance() const;
};

class NotationFile__NotePage SIGNATURED
{
	friend class NotationFile;

private:
	NotationFile *notationFile;
	NotationFile::NotePage *nextNotePage;
	NotationFile::Notation *firstNotation, *lastNotation;
	long notationCount, cleanNotationCount;
	char *pageName, *pageComment;
	Logical cleanPage;

	NotationFile__NotePage(NotationFile *notation_file);
	~NotationFile__NotePage();

	void SetName(const char *pagename, long *clean_page_count);
	void SetComment(const char *comment);
	const char *GetName() const		{ Check(this); return pageName; }
	void AppendNote(NotationFile::Notation *note);
	Logical FindNote(const char *entryname, NotationFile::Notation **note);
	Logical FindNote(const char *entryname, NotationFile::Notation **note, NotationFile::Notation **prev);
	void UpdateNote(NotationFile::Notation *note);
	void DeleteNote(const char *entryname);
	long WriteNote(std::ostream &stream, NotationFile::WriteMode mode) const;
	Logical TestInstance() const;
};