/*  domenus.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>
#include <string.h>
#include <stdlib.h>

#include "auxlist.h"
#include "stdyac.h"
#include "stdyacp.h"
#include "strgck.h"
#include "yacio.h"
#include "text.h"
#include "text2.h"
#include "textfrag.h"
#include "mendir.h"
#include "spellfil.h" 
#include "debug.h"
#include "hlpfil.h"
#include "domenus.h"
#include "parammen.h"
#include "y.tab.h"
#include "inconce.h"
#include "objinit.h"
#include "menuprsy.h"
#include "menustr.h"
#include "menmain.h"
#include "texspec.h"
#include "mkstr.h"
#include "cgidbg.h"
#include "sysconst.h"

char * UsrInc = 0;
int RemoteFileFlag = 1;
int LocalFileFlag = 1;

static void ProcessPrologue();

void NoUserCopyright()
{
	user_copyright = 0 ;
	ProcessPrologue();
}

void SetUserCopyright()
{
	user_copyright = 1 ;
	ProcessPrologue();
}

const char * RemFile(const char * name)
{
	if (RemoteFileFlag) return name ;
#ifdef __NT_VC__
	return Concatenate(name,".gbg") ;
#else
	return "/dev/null" ;
#endif
}

const char * LocFile(const char * name)
{
	if (LocalFileFlag) return name ;
#ifdef __NT_VC__
	return Concatenate(name,".gbg") ;
#else
	return "/dev/null" ;
#endif
}


ostream * Herr ; 	// Help file error messages written separately
				// on a temporary basis
static ostream * FileOut ; 	// C++ data structures

// static ostream* LocalOut ;	// Local header file

// static ostream* RemoteOut ;	// Remote header file
// static ostream* DspRemoteOut ;	// DSP Remote header file

static ostream* MenuOut ;	// Menu definitions to include prior to refernces

// static ostream* RemPhonyOut;//File of phone routine definitions for debugging
static ostream* LocPhonyOut ;// File of phone routine definitions for debugging

static char XXSpellFileName[] = "spell.names" ;
char * SpellFileName = XXSpellFileName ;

static const char * MenuLineInitParam = "" ; // "class MenuLine *";
// static const char * MenuInitParam = "class Menu *" ;

class CmdList {
	const char * Cmd ;
	const char * ConflictName ;
	ConstStringList * Strings ;
public:
	CmdList(const char * cmd, const char * cnf, ConstStringList * Strs) 
		{ Cmd = cmd ; ConflictName = cnf; Strings = Strs ; }
	
	const char * GetCmd() { return Cmd; }
	const char * GetConflictName() { return ConflictName; }
	ConstStringList * GetStrList() {return Strings; }
} ;


class ListOfLists: public SingleList {
public:
	ErrCode Insert(CmdList *nt) {return SingleList::Insert(nt);}
	ErrCode Append(CmdList  *nt) {return SingleList::Append(nt);}
	CmdList  * Get()   {return (CmdList  *) SingleList::Get();}
	ListOfLists(){;}
	int Size(){return SingleList::Size();}
	void Dump(const char * Where) ;
} ;



class ListOfListsIterator: public SingleListIterator {
public:
	ListOfListsIterator(ListOfLists& df):
		SingleListIterator((SingleList&) df){}
	CmdList * operator()()
		{return (CmdList *) Next();}
};


TextList AllHelpText;

ListOfLists ConflictLists ;
MenuItemList AllMenuItems ;


ConstStringList LocalReferences ;
ConstStringList LocalRemoteReferences ;
ConstStringList * RemoteReferences ;

ConstStringList MenuReferences ;
ConstStringList MenuDefinitions ;

ConstStringList DynamicMenuReferences ;
ConstStringList DynamicMenuDefinitions ;

ConstStringList MultipleUseMenuDefinitions ;

ConstStringList UserMenuCmds ;

ConstStringList LocalInitRoutines ;
ConstStringList RemoteInitRoutines ;
ConstStringList MenuLocalInitRoutines ;
ConstStringList MenuRemoteInitRoutines ;
ConstStringList RemoteOptionsInitRoutines ;
ConstStringList HelpFileNames ;

ConstStringList UserHistoryLocalCmds ;
ConstStringList UserHistoryRemoteCmds ;
ConstStringList UserLocalCmds ;
ConstStringList UserLocalRemoteCmds ;
ConstStringList UserRemoteCmds ;

ParameterizedActionList LocalActionCalls;
ParameterizedActionList LocalRemoteActionCalls; 
/*
 * Local Remote actions are put in LocalRemoteAction Calls and LocalActionCalls
 */
ParameterizedActionList RemoteActionCalls;


// ConstStringList DefinedDynMenus ;
ConstStringList DefinedEntries ;

int ParameterizedAction::IsIdentical(ParameterizedAction * Check)
{
	if (strcmp(Name,Check->GetName())) return 1;
	CompoundListIterator NextA(*List);
	CompoundListIterator NextB(*(Check->GetList()));
	Compound * ItemA;
	Compound * ItemB;
	for (;;) {
		ItemA = NextA();
		ItemB = NextB();
		if (!(ItemA && ItemB)) break;
		if(ItemA->Type != ItemB->Type) return 0;
		switch (ItemA->Type) {
case TypeInt:
			if (ItemA->Item.Int != ItemB->Item.Int) return 0 ;
			break ;
case TypeDouble:
			if (ItemA->Item.Dbl->DblValue !=
				ItemB->Item.Dbl->DblValue) return 0 ;
			break ;
case TypeName:
case TypeString:
			if (strcmp(ItemA->Item.Str,ItemB->Item.Str)) return 0 ;
			break ;
case TypeStackIndex:
			return 0;
case TypeParameterizedAction: 
case TypeText:
case TypeTerm:
case TypeCompound:
case TypeDescription:
case TypeList:
case TypeParamFunc:
case TypeSize:
case TypeScaledType:
default:
			DbgError("IsIdentical", "Invalid type");
		}
	}
	if (ItemA || ItemB) return 0;
	return 1;
}


const char ** CommandListToArray(ParameterizedActionList& Cmds)
{
	ParameterizedAction ** SpecTab =
		new ParameterizedAction * [Cmds.Size()] ;
	ParameterizedAction * Spec ;
	ParameterizedActionListIterator Next(Cmds);
	int i = 0;
	while (Spec = Next()) {
		SpecTab[i++] = Spec ;
		if (Spec->wait_flag) {
			cerr << "Spec wait = " << (void *) Spec << "\n" ;
			cerr << "SpecTab[" << i-1 << "].wait_flag = " <<
				SpecTab[i-1]->wait_flag << "\n" ;
		}
	}

	// complain about identical names with identical parameters
	int j ;
	int k ;
	for (j = 0 ; j < i-1 ; j ++)
		for (k = j+1; k < i; k++) 
			if (SpecTab[j]->IsIdentical(SpecTab[k])) {
				cerr << "There are two instances of action `" <<
					SpecTab[j]->GetName() <<
					"' with identical parameters.\n" ;
			}

	// eliminate duplicate names
	int Size = i;
	for (j = 0 ; j < i-1 ; j ++)
		for (k = j+1; k < i; k++) 
			if (!strcmp(SpecTab[j]->GetName(),
				SpecTab[k]->GetName())) {
				SpecTab[j] = 0;
				Size--;
				break ;
	}
	const char ** Return = new const char * [Size];
	k = 0 ;
	for (j = 0 ; j < i; j++) if (SpecTab[k]) Return[k++] =
		SpecTab[k]->GetName();
	delete SpecTab ;
	return Return ;
}

void NewHelpFileName( const char * Name)
{
	ConstStringListIterator Next(HelpFileNames);
	const char * Prev ;
	while (Prev = Next()) if (!strcmp(Name,Prev)) return ;
	HelpFileNames.Append(Name);
	const char * FullName ;
	FILE * Test = OpenHelpFile(Name, &FullName);
	if (Test) {
		fclose (Test) ;
		return ;
	}
	*Herr << "Warning: Can't file help file\n\t`" << FullName << "'\n\t" ;
	OutWhereEnd(*Herr);
}

void MenuItem::Dump(const char * Where)
{
	cout << "MenuItem::Dump at `"<< Where << "':\n" <<
		"\tCommand: " << Command <<
		"\n\tAction: " << Action->GetName() <<
		"\n\tActionType: " << ActionType <<
		"\n\tInit: " << Init->Name <<
		"\n\tInitActionType : " << InitActionType <<
		"\n\tMenuString : " << MenuString << "\n\n" ;
}

void EmitEmbeddedReference(const char * Name,int)
{
	cerr << "**************EmitEmbeddedReference(" << Name << "called.\n" ;
}

Value * MakeNullValue()
{
	return new Value((char *)0) ;
}

ConstStringList RemoteIncludeFiles ;
ConstStringList LocalIncludeFiles ;

void FoundInclude(int Type, const char * FileName)
{
	// cout << "FoundInclude(" << Type << ", " << FileName << ")\n" ;
	if (Type == INCLUDE_LOCAL) LocalIncludeFiles.Append(FileName);
	else if (Type == INCLUDE_REMOTE) RemoteIncludeFiles.Append(FileName);
	else DbgError("FoundInclude","bad type");
}


Value * FoundItemRef(Value *Cmd)
{
	if (!Cmd) return 0;
	MenuItemListIterator Next(AllMenuItems);
	MenuItem * Item ;
	while (Item = Next()) if (!strcmp(Item->GetCommand(),Cmd->Name)) {
		// Item->Dump("FoundItemRef");
		return new Value(new MenuItem(*Item)) ;
	}
	cerr << "Reference to unknown MenuLine `" << Cmd->Name << "'" ;
	OutWhereEnd();
	return 0;
}


ConstStringList * TempCmdList = 0;

int IsKnownCommand(const char * Name)
{
	const char * Ref ;
	ConstStringListIterator TempNext(*TempCmdList);
	while (Ref = TempNext()) if (!strcmp(Ref,Name)) return 1;
	ConstStringListIterator Next(LocalReferences);
	while (Ref = Next()) if (!strcmp(Ref,Name)) return 1;
	ConstStringListIterator LocRemNext(LocalRemoteReferences);
	while (Ref = LocRemNext()) if (!strcmp(Ref,Name)) return 1;
	ConstStringListIterator RemNext(*RemoteReferences);
	while (Ref = RemNext()) if (!strcmp(Ref,Name)) return 1;
	return 0;
}

const char * MakeHelpTextName(const char * Suffix)
{
	static int Count = 1 ;
	static char * BaseName = "HelpTextList_" ;

	int CountChar = 0;
	if (Suffix) CountChar = strlen(Suffix) ;
	else for (int temp = Count; temp; temp /= 10) CountChar++;
	int length = strlen(BaseName);
	char * name = new char[length+1+CountChar];
	strcpy(name,BaseName);
	if (Suffix) strcat(name,Suffix);
	else {
		char buf[32] ;
		sprintf(buf,"%d",Count++);
		strcat(name,buf);
	}
	return name ;
}


HelpText * MakeHelpList (TextFragmentList * Text, const char * Suffix)
{
	int TeXDirectory = 1 ;
	if (!Text) return 0;
	const char ** List = new const char * [Text->Size()+1] ;
	const char ** TeXList = 0;
	if (TeXDirectory) TeXList = new const char * [Text->Size()+1] ;
	const char * HelpName = MakeHelpTextName(Suffix);
	*FileOut << "static const char * " << HelpName << "[] = {\n" ;
	int Index = 0;
	TextFragment * Frag ;
	// cerr << "Text named `" << HelpName << "'\n" ;
	while (Frag=Text->Get()) {
		if (IsWhiteSpace(Frag->Text)) {
			delete (char *) Frag->Text ;
			Frag->Text = 0 ;
			delete Frag ;
			Frag = 0 ;
			continue ;
		}
		char * TeXTemp = 0;
		if (TeXDirectory) TeXTemp = Concatenate(Frag->Text) ;
		const char * Temp = QuoteQuote(Frag->Text) ;
		// cerr << "Temp is `" << Temp << "'.\n" ;
		char * DeleteTemp = 0 ;
		if (Frag->Line < 0) {
			if (TeXDirectory) {
				char * t = TeXTemp ;
				TeXTemp = Concatenate(
					"{\\tt ",TeXTemp,"}\\index{",
					TeXTemp,"}");
				delete t ;
				t = 0 ;
			}
			if(Suffix) {
				cerr << "Embedded name `" << Temp <<
					"' in defined help text" ;
				OutWhereEnd();
			} else {
				if (!IsKnownCommand(Temp)) {
					cerr <<
					"Reference to unknown command name `$"
							<< Temp << "'" ; 
					OutWhereEnd();
				}
				DeleteTemp = new char[strlen(Temp)+3];
				*DeleteTemp = '`' ;
				strcpy(DeleteTemp+1,Temp);
				strcat(DeleteTemp,"'") ;
				Temp = DeleteTemp ;
			}
		} else {
			if (TeXDirectory) {
				char * t = TeXTemp ;
				TeXTemp = TeXString(TeXTemp) ;
				delete  t ;
				t = 0 ;
			}
			AddToSpellFile(Temp);
		}
		*FileOut << "\t\"" << Temp << "\",\n" ;
		List[Index] = Concatenate(Temp) ;
		if (TeXDirectory) TeXList[Index] = TeXTemp ;
		Index++ ;
		// cerr << "TeXTemp:\"" << TeXTemp << "\")\n" ;
		// cerr << "Temp:\"" << Temp << "\")\n" ;
		delete (char *) Frag->Text ;
		Frag->Text = 0 ;
		delete Frag ;
		Frag = 0 ;
		delete DeleteTemp ;
		DeleteTemp = 0 ;
	}
	*FileOut << "\t0\n};\n" ;
	List[Index] = 0;
	if (TeXDirectory) TeXList[Index] = 0;
	if (Suffix) return 0;
	return new HelpText(HelpName,List,TeXList);
}

ConstStringList TheDefinedHelpTextNames ;
ConstStringList TheDefinedHelpReferences ;
		
void FoundHelpDef(const char * Name, TextFragmentList * Text)
{
	MakeHelpList(Text,Name);
	TheDefinedHelpTextNames.Append(Name);
}

	



Value * FirstConflict(Value * Name)
{
	ConstStringList * Lst = new ConstStringList ;
	Lst->Append(Name->Name) ;
	// delete Name;
	return new Value(Lst) ;
}

Value * StartItemList()
{
	MenuItemList * MeItLi = new MenuItemList ;
	return new Value(MeItLi) ;
}



Value * MakeValue (int n)
{
	Value * Ret = new Value (n);
	return Ret ;
}

Value * MakeValue (const char * Str)
{
	// cout << "MakeValue:: " << Str << "\n" ;
	Value * Ret = new Value (Str) ;
	return Ret ;
}

static int IsOptionsCommand(const char * Name)
{
	MenuItemListIterator Next(AllMenuItems);
	MenuItem * Check ;
	while (Check = Next()) if (!strcmp(Name,Check->GetAction()->GetName()))
		return Check->GetActionType()==REMOTE_OPTIONS ;
	ConstStringListIterator Another(RemoteOptionsInitRoutines);
	const char * AnothersName ;
	while (AnothersName = Another()) if (!strcmp(Name,AnothersName))
		return 1 ;
	return 0;
}

const char * MenuLine = "MenuLine" ;

const char * DummyDummy = "Dummy_Menu_Menu_LineMenuLine" ;

static void DoDynMenuPhonyOut(const char * TheString)
{
	// if (ConstIsInList(TheString,&DefinedDynMenus)) return ;

	static int FirstTime = 1 ;
	if (FirstTime) {
		*FileOut << "MenuLine " << DummyDummy << "[] = {\n" ;
		*FileOut <<
		    "\t{\"dummy\",\"Unimplemented Menu: DO NOT SELECT!\"},\n" ;
		*FileOut << "\t{0}\n};\n\n" ;

		*MenuOut << "extern MenuLine "<< DummyDummy <<"[];\n" ;
		FirstTime = 0 ;
		
	}

	*MenuOut << "static DynamicMenu " << TheString << "\n\t(\"" <<
		TheString <<
		" Dummy Menu (not in yet) \",\n\t" << DummyDummy <<",  0);\n\n" ;
}
	
	


static void DoLocPhonyOut(const char * TheString)
{
	if (ConstIsInList(TheString,&DefinedEntries)) return ;
	*LocPhonyOut << "static void " << TheString << "(" <<
		MenuLineInitParam << ")\n" ;
	*LocPhonyOut << "{\n\tLogDumMsg(\"" << TheString <<
		" called\");\n}\n\n";
}	

void DefinedName(Value * Name) 
{
	DefinedEntries.Append(Name->Name) ;
}

void MainEntry(Value *A, Value *B)
{
	MainEntrys.Append( new MainEntryPair(A->Name,B->Name)) ;
}


void CheckForIdentity (const char * Msg, const char * Name, MenuItem * base,
	MenuItem * check)
{
	MenuType ParentType = base->GetParentType();
	if (ParentType.IsHistory()) return ;
	MenuType CheckParentType = check->GetParentType();
	if (CheckParentType.IsHistory()) return ;
	if (ParentType.IsSelect()) if (CheckParentType.IsSelect()) return ;

	if (base->GetActionType() != check->GetActionType())
		cerr << Msg << " name `" << Name <<
		"' is used for more than one type (Local,Remote or Menu).\n";
	if (strcmp(base->GetAction()->GetName(),check->GetAction()->GetName()))
		cerr << Msg << " name `" << Name <<
		"' is used for two different " << CPP_SUFFIX << " names: `" <<
		base->GetAction()->GetName()
		<< "' and `" << check->GetAction()->GetName() << "'\n" ;

	ParentType = base->GetParentType();
	if (ParentType.IsMultiUse()) {
		ParentType = check->GetParentType();
		if (ParentType.IsMultiUse()) return ;
	}

	if (strcmp(base->GetCommand(),check->GetCommand()))
		cerr << Msg << " name `" << Name <<
		"' is used for two different user names: `" <<
		base->GetCommand() <<"' and `" << check->GetCommand() << "'\n" ;
	if (strcmp(base->GetMenuString(),check->GetMenuString()))
		cerr << "Warning: " << Msg << " name `" << Name <<
		"' is used with two different user prompts:\n\"" <<
		base->GetMenuString() << "\",\n\"" << check->GetMenuString()
		<< "\".\n" ;
}


static void CheckForFileNameIdentity (const char * Name, const char * cmd1,
	const char * cmd2)
{
	if (!strcmp(cmd1,cmd2)) return ;
	cerr << "The same help file name `" << Name <<
	"' is used with\ntwo commands: `" <<  cmd1 << "' and `" <<
	cmd2 << "'.\n" ;
}

static int CheckTypes (MenuItem * base, MenuItem * check)
{
	int Base = base->GetActionType();
	int Check = check->GetActionType();
	if (Base == Check) return 1 ;
	if (Base == HELP_FILE || Check == HELP_FILE) return 0;
	return 1;
}

const char * MsgType (MenuItem * base)
{
	switch (base->GetActionType()) {
case LOCAL :
case REMOTE :
case LOCAL_REMOTE :
case REMOTE_OPTIONS :
		return "C++ routine " ;
case DYNAMIC_MENU:
		return "C++ dynamic menu" ;
case NEW_MENU:
		return "C++ menu " ;
case HELP_FILE:
		return "Help file " ;
default:
		DbgError("CheckTypes","invalid type");
	}
	return 0 ;
}

static void CheckSameUsage()
{
	MenuItem * base ;
	MenuItem * check ;
	MenuItem ** TheMenuItems = (MenuItem**) ListToArray ((SingleList *)
		&AllMenuItems);
	for (MenuItem ** Base = TheMenuItems ; *Base ; Base++) {
		for (MenuItem ** Check = Base+1 ; *Check ; Check++)
			if(!strcmp((base= *Base)->GetCommand(),
				(check= *Check)->GetCommand())) 
					CheckForIdentity ("User",
						base->GetCommand(),base,check) ;
		for ( Check = Base+1 ; *Check ; Check++)
			if(!strcmp((base= *Base)->GetAction()->GetName(),
				(check= *Check)->GetAction()->GetName())) 
					if (CheckTypes(base,check))
					CheckForIdentity (MsgType(base),
						base->GetAction()->GetName(),
						base,check);
	}
}

static int IsDuplicateReference(const char * TheString,
		ConstStringList& RemoteReferences)
{
	const char * Str ;
	ConstStringListIterator Next(RemoteReferences) ;
	while (Str = Next()) if (!strcmp(Str,TheString)) return 1 ;
	return 0 ;
}

void AddInitToList(ParameterizedAction * act, int Type)
{

	const char * Init = act->Name ;
	switch (Type) {
case MENU_REMOTE:
case REMOTE_OPTIONS:
case MENU_REMOTE_OPTIONS:
case REMOTE:
		RemoteActionCalls.CheckAndAppend(act);
		break ;
case LOCAL_REMOTE:
		LocalRemoteActionCalls.CheckAndAppend(act);
case MENU_LOCAL:
case LOCAL:
		LocalActionCalls.CheckAndAppend(act);
		break ;
default:
		DbgError("AddInitToList","bad type 1");
	}
	switch (Type) {
case MENU_REMOTE:
		if (!IsDuplicateReference(Init,RemoteInitRoutines)) {
			MenuRemoteInitRoutines.Append(Init) ;
			// DoRemPhonyOut(Init) ;
		}
		break ;
case REMOTE_OPTIONS:
case MENU_REMOTE_OPTIONS:
		if (!IsDuplicateReference(Init,RemoteInitRoutines)) 
			MenuRemoteInitRoutines.Append(Init) ;
		if (!IsDuplicateReference(Init,RemoteOptionsInitRoutines)) 
			RemoteOptionsInitRoutines.Append(Init) ;
		break ;
case MENU_LOCAL:
		if (!IsDuplicateReference(Init,LocalInitRoutines)) {
			MenuLocalInitRoutines.Append(Init) ;
			DoLocPhonyOut(Init) ;
		}
		break ;
case REMOTE:
		if (!IsDuplicateReference(Init,RemoteInitRoutines)) {
			RemoteInitRoutines.Append(Init) ;
			// DoRemPhonyOut(Init) ;
		}
		break ;
case LOCAL:
		if (!IsDuplicateReference(Init,LocalInitRoutines)) {
			LocalInitRoutines.Append(Init) ;
			DoLocPhonyOut(Init) ;
		}
		break ;
case LOCAL_REMOTE:
default:
		DbgError("AddInitToList","bad type 2");
	}
}

void CheckForDups(MenuItemList * List,const char * MenuName)
{
	// cout << "CheckForDups\n" ;
	MenuItem ** TheItems = (MenuItem **) ListToArray((SingleList *) List);
	for (MenuItem ** Base = TheItems; *Base; Base++) {
		const char * Init = 0;
/*
 *		cout << "(*Base)->GetCommand() = `" <<
 *			(*Base)->GetCommand() << "'\n" ;
 */
		ParameterizedAction * ActInit = (*Base)->GetInit() ;
		if (ActInit) Init = ActInit->Name ;
		if (Init) AddInitToList(ActInit, (*Base)->GetInitAction()) ;
		const char * Action = (*Base)->GetAction()->GetName();
		for (MenuItem ** Check = Base+1; *Check; Check++) {
/*
 *			cout << "(*Check)->GetCommand() = `" <<
 *				(*Check)->GetCommand() << "'\n" ;
 */
			const char * CheckAction =
				(*Check)->GetAction()->GetName();
			if (strcmp(Action,CheckAction)) continue ;
			ParameterizedAction * CheckInit = (*Check)->GetInit() ;
			const char * InitName = 0;
			if (CheckInit) InitName = CheckInit->GetName();
			int NullFlag = 0 ;
			if ((*Base)->GetActionType()==HELP_FILE) continue ;
			if (!Init || !InitName) NullFlag=1;
			else if (strcmp(Init,InitName)) continue ;
			if ((*Base)->GetActionType()==DYNAMIC_MENU) continue ;
			cerr << MsgType(*Base) << "`" <<
				(*Base)->GetAction()->GetName()
				<< "' occurs twice in menu `" 
				<< MenuName  << "'\n";
			if (NullFlag) cerr << "One or both of these has no "
				<< "initialization routine.\n" ;
			else cerr << "They also have indentical initalization "
				<< "routines: `" << Init <<"'.\n" ;
			cerr << "The definition of this menu ends" ;
			OutWhereEnd();
		}
	}
	// delete TheItems ;
}

static void OutMissing(int& Missing, const char * String, int QuoteString=0)
{
	if (String) if (*String) {
		for (int i = 0 ; i < Missing; i++) *FileOut << ", 0" ;
		*FileOut << ", " ;
		if (QuoteString) *FileOut << "\"" << String  << "\"" ;
		else *FileOut << String ;
		Missing = 0 ;
		return ;
	}
	Missing++ ;
}


static void NewMenuItems (MenuType Type, Value * ItemList,
	MenuItemList * DoAppend = 0)
{
	MenuItemListIterator Next(*(ItemList->List));
	MenuItem * AnItem ;
	while (AnItem = Next()) {
		AnItem->SetParentType(Type);
		// if (!strcmp(AnItem->GetCommand(),Template)) continue ;
		AllMenuItems.Append(AnItem) ;
		AllHelpText.Append(AnItem->GetMenuHelp()) ;
		if (DoAppend) DoAppend->Append(AnItem) ;
	}
}


void MenuAddition(Value *Menu, Value * ItemList)
{
	const char * MenuName = Menu->Name ;
	MenuDefListIterator Next(TheMenuDefs) ;
	MenuDef * TheDef ;
	while (TheDef = Next()) {
		if (!strcmp(MenuName,TheDef->Name)) break ;
	}
	if (!TheDef) {
		cerr << "There is no menu `" << MenuName <<"' to add to.\n" ;
		return ;
	}
	NewMenuItems(TheDef->Type,ItemList,TheDef->Items);
}

static void MenuTypeProcessing(MenuType Type, const char * Name)
{
	int Return = 0 ;
	if (Type.IsMultiUse()) {
		if (IsDuplicateReference(Name,MultipleUseMenuDefinitions)) {
			cerr << "Multiple Use Menu " << Name <<
				" is defined twice" ;
			OutWhereEnd();
		} else MultipleUseMenuDefinitions.Append(Name) ;
		Return = 1 ;
	}
	if (Type.IsDynamic()) {
		if (IsDuplicateReference(Name,DynamicMenuDefinitions)) {
			cerr << "Dynamic Menu " << Name << " is defined twice" ;
			OutWhereEnd();
		} else DynamicMenuDefinitions.Append(Name) ;
	} else {
		if (IsDuplicateReference(Name,MenuDefinitions)) {
			cerr << "Dynamic Menu " << Name << " is defined twice" ;
			OutWhereEnd();
		} else MenuDefinitions.Append(Name) ;
	}
}
	

void Menu(MenuType Type,Value * Name, Value * Init, Value * Title, Value * Body)
{
	// delete TempCmdList ;
	TempCmdList = 0;
	AddToSpellFile(Title->Name) ;
	MenuTypeProcessing(Type, Name->Name) ;
	CheckForDups(Body->List,Name->Name) ;
	MenuDef * NewMenu = new MenuDef(Name->Name,Init->Init,Title->Name,
		Body->List,Type);
	TheMenuDefs.Append(NewMenu);

	if (Type.IsOrphan()) TheOrphans.Append(NewMenu);

	ClassHasMembers(Name->Name);
	NewMenuItems(Type,Body);
}

struct MenuLineTemplate {
	const char * MenuName ;
	MenuItem * ItemTemplate ;
	MenuLineTemplate(const char * name, MenuItem * Item)
		{MenuName = name; ItemTemplate = Item;}
};

class MenuLineTemplateList: public SingleList {
public:
	ErrCode Insert(MenuLineTemplate *nt) {return SingleList::Insert(nt);}
	ErrCode Append(MenuLineTemplate *nt) {return SingleList::Append(nt);}
	void AddNewTemplate(MenuLineTemplate *nt) ;
	MenuLineTemplate * GetMenuLine( const char * MenuName) ;
	MenuLineTemplate * Get()
		{return (MenuLineTemplate *) SingleList::Get();}
	MenuLineTemplate * Pop()
		{return (MenuLineTemplate *) SingleList::Pop();}
	MenuLineTemplate * GetNFromTop(int N) ;
	MenuLineTemplate * GetNthEntry(int N) ;
	MenuLineTemplateList(){;}
	int Size(){return SingleList::Size();}
} ;

class MenuLineTemplateListIterator: public SingleListIterator {
public:
	MenuLineTemplateListIterator(MenuLineTemplateList& df):
		SingleListIterator((SingleList&) df){}
	MenuLineTemplate * operator()()
		{return (MenuLineTemplate *) Next();}
};

MenuLineTemplate * MenuLineTemplateList::GetMenuLine( const char * MenuName)
{
	MenuLineTemplateListIterator Next(*this);
	MenuLineTemplate * Template ;
	while (Template = Next()) if (!strcmp(MenuName,Template->MenuName))
		return Template ;
	return 0 ;
}

void MenuLineTemplateList::AddNewTemplate(MenuLineTemplate *nt)
{
	if (GetMenuLine(nt->MenuName)) {
		cerr << "Two item templates defined for menu `" <<
			nt->MenuName << "'\n"  ;
		return ;
	}
	Append(nt);
}


MenuLineTemplateList TheMenuLineTemplates ;


char * MakeCallStructName(int CurrentActionType, const char * Name)
{
	const char * Suf ;
	static const char * LocSuf = "_Loc_Call_Struct" ;
	static const char * RemSuf = "_Rem_Call_Struct" ;
	switch (CurrentActionType) {
case MENU_REMOTE:
case REMOTE_OPTIONS:
case MENU_REMOTE_OPTIONS:
case REMOTE:

			Suf = RemSuf ;
			break ;
default:
			Suf = LocSuf ;
			break ;
	}
	// char * Buf = new char[strlen(Suf) + strlen(Name)+2];
	// strcpy (Buf,"&");
	// strcat (Buf,Name);
	// return strcat (Buf,Suf);
 	return Concatenate("&",Name,Suf);
}

static void OutputMenu(MenuDef * CurrentMenu)
{
	if (!CurrentMenu->IsDynamicMenu()) {
		*FileOut << MenuLine << " " << CurrentMenu->Name <<
			MenuLine << "[] = {\n" ;
	}
	MenuItemListIterator Next(*(CurrentMenu->Items));
	MenuItem * AnItem ;
	while (AnItem = Next()) {
		if (SkipAnItem(AnItem,CurrentMenu->Name)) continue ;
		int TemplateFlag = 0;
		// AnItem->Dump("Loop head");
		const char * UserCommand = AnItem->GetCommand() ;
		if (!strcmp(UserCommand,Template)) {
			TemplateFlag = 1 ;
			TheMenuLineTemplates.AddNewTemplate(
				new MenuLineTemplate(CurrentMenu->Name,
				AnItem));
			if (!CurrentMenu->Type.IsDynamic()) cerr <<
				"Item template defined for menu `" <<
				CurrentMenu->Name <<
				"' which is not a dynamic menu.\n" ;
		} else *FileOut << "	{\"" << UserCommand << 
		"\",\n\t\"" << AnItem->GetMenuString() << "\",\n\t"  ;
		AddToSpellFile( AnItem->GetMenuString()) ;
		AddToSpellFile( AnItem->GetCommand()) ;
		const char * TheString = AnItem->GetAction()->GetName() ;
		const char * TheHelpFile = 0;
		int CurrentActionType = AnItem->GetActionType();
		switch (CurrentActionType) {
case LOCAL_REMOTE :
			UserLocalRemoteCmds.Append(UserCommand);
			if (!IsDuplicateReference(TheString,
				LocalRemoteReferences)) {
				LocalActionCalls.Append( AnItem->GetAction());

				LocalRemoteReferences.Append(TheString);
 				DoLocPhonyOut(TheString) ;
				LocalRemoteActionCalls.Append(
					AnItem->GetAction());
			}
case LOCAL :
			if (AnItem->IsHistory())
				UserHistoryLocalCmds.Append(UserCommand);
			else if (CurrentActionType == LOCAL)
				UserLocalCmds.Append(UserCommand);
 			if (!TemplateFlag) *FileOut <<
				"MenuTypeCommand, (void *) &"
 				<< TheString << "_Loc_Call_Struct" ;
			if (CurrentActionType != LOCAL_REMOTE) {
				if (!IsDuplicateReference(TheString,
					LocalReferences)) {
					LocalActionCalls.Append(
						AnItem->GetAction());

					LocalReferences.Append(TheString);
 					DoLocPhonyOut(TheString) ;
				}
			}
			break ;
case REMOTE :
case REMOTE_OPTIONS :
			if (AnItem->IsHistory())
				UserHistoryRemoteCmds.Append(UserCommand);
				else UserRemoteCmds.Append(UserCommand);

			if (!TemplateFlag) *FileOut <<
				"MenuTypeCommand, (void *) &" <<
				TheString << "_Rem_Call_Struct" ;
			if (!IsDuplicateReference(TheString,
				*RemoteReferences)) {
				RemoteActionCalls.Append(
					AnItem->GetAction());
				RemoteReferences->Append(TheString);
			} 
			break ;
case DYNAMIC_MENU:
			if(!IsDuplicateReference(TheString,
				DynamicMenuReferences))
				DynamicMenuReferences.Append(TheString) ;
case NEW_MENU:
			UserMenuCmds.Append(UserCommand);
			if(!strcmp(TheString,CurrentMenu->Name)) cerr <<
				"Menu `" << CurrentMenu->Name <<
				"' is self referencing.\n" ;
			if (CurrentActionType==NEW_MENU)
				MenuReferences.Append(TheString);
			if (!TemplateFlag) {
				*FileOut << "MenuTypeMenu, " ;
				if (strcmp(TheString,"Orphan"))
					*FileOut << "(void *) &" ;
				*FileOut << TheString ;
			}
			break ;
case HELP_FILE:
			TheDefinedHelpReferences.Append(TheString);		
			if (!TemplateFlag) *FileOut << "MenuTypeHelp, 0" ;
			TheHelpFile = TheString ;
			break ;
default:	
			DbgError("OutputMenu","Error invalid type.") ;
			break ;
		}
		if (TemplateFlag) continue ;
		ListOfListsIterator ListNext(ConflictLists) ;
		int ConflictSet = 0;
		CmdList * Cmds ;
		while (Cmds = ListNext()) {
			const char * Str ;
			ConstStringListIterator StrNext(*(Cmds->GetStrList())) ;
			while (Str = StrNext())  {
				if (!strcmp(AnItem->GetCommand(),Str)) {
					*FileOut << ",	" <<
					Cmds->GetConflictName() ;
					ConflictSet = 1 ;
					goto LoopExit ;
				}
			}
		}
LoopExit:
		int Missing = 0 ;
		if (!ConflictSet) Missing++ ;

		if (AnItem->GetInit()) {
			char * Temp = MakeCallStructName(
				CurrentActionType, AnItem->GetInit()->Name);
			OutMissing(Missing,Temp);
			delete Temp;
		} else Missing++;

		const char * HelpTextName = 0;
		if (AnItem->GetMenuHelp()) HelpTextName =
			AnItem->GetMenuHelp()->Name;
		if (HelpTextName) OutMissing(Missing,HelpTextName);
		else if (AnItem)
			OutMissing(Missing,AnItem->GetMenuHelpReference());
		else OutMissing(Missing,0);

		if (!TheHelpFile) TheHelpFile = AnItem->GetHelpFileName();
		OutMissing(Missing,TheHelpFile,1);

		*FileOut << "},\n" ;
	}

	const char * MenuName = CurrentMenu->Name ;

	if (!CurrentMenu->Type.IsDynamic()) {
		*FileOut << "\t{0}\n};\n\n" ;
		*MenuOut << "extern " << MenuLine << " " << MenuName << MenuLine
		<< "[];\n" ;
	}
	
	InitValue * Init = CurrentMenu->Init ;

	// *MenuOut << "static " ;
	*MenuOut << "static " ;
	if (CurrentMenu->Type.IsDynamic()) *MenuOut << "Dynamic" ;
	*MenuOut << "Menu " << MenuName << "\n\t(\"" <<
		CurrentMenu->Title << "\"," ;
	if (CurrentMenu->Type.IsDynamic()) *MenuOut << " 0" ;
	else *MenuOut << MenuName << MenuLine ;
	*MenuOut << ",\n\t" << CurrentMenu->Type.IsMultiUse() ;
	if (Init) if (Init->Action) if (Init->Action->Name)
		*MenuOut << ", " << MakeCallStructName(Init->Type,
			Init->Action->Name) ;
		// << Init->Action->Name << "_Init_Call_Struct" ;
	*MenuOut << ");\n\n" ;

	if (Init) AddInitToList(Init->Action,Init->Type) ;
	// AllMenuItems.Dump("at Menu ctor exit");
}

static void OutputOrphan(MenuDef * Orphan)
{
	*MenuOut << "\t{\"" << Orphan->Name << "\", &" <<
		Orphan->Name << "},\n" ;
}

static void OutputOrphanTable()
{
	*MenuOut << "OrphanedMenus TheOrphanedMenus[] = {\n" ;
	MenuDefListIterator Next(TheOrphans);
	MenuDef * CurrentMenu ;
	while (CurrentMenu = Next()) OutputOrphan(CurrentMenu);
	*MenuOut << "\t{0,0}\n};\n\n" ;
}

static void OutputAllMenus()
{
	MenuDefListIterator Next(TheMenuDefs);
	MenuDef * CurrentMenu ;
	while (CurrentMenu = Next()) {
/*
 *		cout << "About to output menu: `" << CurrentMenu->Name <<
 *			"'\n" ;
 */
		OutputMenu(CurrentMenu);
	}
	OutputOrphanTable();
}

void MenuItemList::Dump(const char * Where)
{
	cerr << "Menu list has " << Size() << " entries at " << Where << ".\n" ;
	MenuItemListIterator Next(*this);
	MenuItem * Item ;
	while (Item = Next()) Item->Dump(Where);
}

void Conflict (Value * ConflictName, Value * Command,Value * List) 
{
	*FileOut << "static char * " << ConflictName->Name << "[]={\n" ;
	const char * String ;
	ConstStringListIterator Next(*(List->Strings));
	while (String = Next()) *FileOut << "	\"" << String << "\",\n" ;
	*FileOut << "0};\n\n" ;
	ConflictLists.Append (new CmdList (Command->Name,ConflictName->Name,
		List->Strings)) ;
}

struct CheckUses {
	const char ** Names ;
	const char * Type ;
	CheckUses (char *T, const char **N) {Type=T; Names = N;} ;
} ;

static void CheckDuplicate(const char ** MenuNames,const char * AnObject)
{
	for (const char ** Base = MenuNames ; *Base ; Base++)
	{
		for (const char ** Check = Base+1 ; *Check; Check++) 
		if (!strcmp(*Check,*Base)) cerr <<
		"Two defintions of " << AnObject << " `" << *Base << "'.\n" ;
	}
}

static void CheckDuplicateMenus(const char ** MenuNames)
{
	CheckDuplicate(MenuNames,"menu");
}

int IsOrphan(const char * Base)
{
	MenuDefListIterator Next(TheOrphans);
	MenuDef * TheMenu ;
	while (TheMenu = Next()) if (!strcmp(TheMenu->Name,Base)) return 1;
	return 0;
}

void CheckForMainMenu(const char ** MenuNames,const char ** MenuRefs,
	MainEntryPair ** MainRefs)
{
	for (MainEntryPair ** MainBase=MainRefs;*MainBase ; MainBase++) {
		for (const char ** Base = MenuNames ; *Base ; Base++)
			if (!strcmp(*Base,(*MainBase)->MenuName)) break ;
		if (!*Base) cerr << "Main menu `" << (*MainBase)->MenuName <<
			"' not found.\n" ;
	}

	// const char * TheMainMenu = 0 ;
	for (const char ** Base = MenuNames ; *Base ; Base++)
	{
		for (const char ** Check = MenuRefs; *Check ; Check++)
			if (!strcmp(*Check,*Base)) break ;
		if (*Check) continue ;
		for (MainBase = MainRefs ; *MainBase ; MainBase++)
			if (!strcmp(*Base,(*MainBase)->MenuName)) break ;
		if (*MainBase) continue ;
		if (IsOrphan(*Base)) continue ;
/*
 *		if (!TheMainMenu) {
 *			TheMainMenu = *Base ;
 *			continue ;
 *		} else if (!strcmp(TheMainMenu,*Base)) continue ;
 *
 *		cerr << "Both `" << *Base << "' and `" << TheMainMenu <<
 *			"' are unreferenced menus.\n" ;
 */
 		cerr << "`" << *Base << "' is an unreferenced menu.\n" ;
		cerr<<"There cannot be any unreferenced non `Orphan' menus.\n" ;
	}
	if (!*MainRefs) cerr << "THERE IS NO LIST OF MAIN MENUS.\n" ;
	// if(TheMainMenu)*FileOut<<"AllMenus TheMenus ("<<TheMainMenu<< ");\n";
	for (MainBase = MainRefs ; *MainBase ; MainBase++)
		*FileOut << "AllMenus " << (*MainBase)->AllMenus <<
		 "(" << (*MainBase)->MenuName << ");\n" ;
}

enum CheckType {CheckReferenceType,CheckDefineType};

void CheckReferences(const char ** MenuNames,const char ** MenuRefs,
	const char * AnObject, CheckType Type)
{
	const char * StartSentence ;
	const char * EndSentence ;
	switch(Type) {
case CheckDefineType :
		StartSentence = "Referenced " ;
		EndSentence = "' never defined.\n" ;
		break ;
case CheckReferenceType :
		StartSentence = "Defined " ;
		EndSentence = "' never referenced.\n" ;
		break ;
default:
		DbgError("CheckReferences","bad type");
	}
	
	for (const char ** Base = MenuRefs ; *Base ; Base++) {
		for (const char ** Check = MenuNames; *Check ; Check++)
			if (!strcmp(*Check,*Base)) break ;
		if (!*Check) cerr << StartSentence << AnObject << " `" <<
			*Base << EndSentence ;
	}
}

void CheckMenuReferences(const char ** MenuNames,const char ** MenuRefs)
{
	CheckReferences(MenuNames, MenuRefs, "menu",CheckDefineType);
}

void CheckDisjointNameSpaces(const char * Msg, CheckUses ** SeparateNames)
{
    for (CheckUses ** Base = SeparateNames ; *Base ; Base++ ) {
	// LogOut << (*Base)->Type << " base\n" ;
	for (CheckUses ** Check = Base+1 ; *Check ; Check++ )
		// LogOut << (*Check)->Type << " check\n" ;
		for (const char ** base = (*Base)->Names; *base;base++)
		for (const char ** check = (*Check)->Names; *check ; check++) {
		if (!strcmp(*base,*check)) cerr <<  Msg << " name `" << *base <<
		"' is used as both a " << (*Base)->Type << " and a " <<
		(*Check)->Type << ".\n" ;
	}
    }
}
		
int CheckUsed(const char * Str,CheckUses ** SeparateNames)
{
	for (CheckUses ** Base = SeparateNames ; *Base ; Base++ )
		for (const char ** base = (*Base)->Names; *base;base++) 
			if (!strcmp(*base,Str)) return 0 ;
	return 1;
}


void CheckAllConflictsReferenced(CheckUses ** SeparateNames)
{
	ListOfListsIterator ListNext(ConflictLists) ;
	CmdList * Cmds ;
	while (Cmds = ListNext()) {
		const char * Str ;
		ConstStringListIterator StrNext(*(Cmds->GetStrList())) ;
		while (Str = StrNext())
			if(CheckUsed(Str,SeparateNames)) cerr << "Command `"
			<< Str << "' from conflict list `" <<
			Cmds->GetConflictName() <<
			"' is not referenced in any menu.\n" ;
	}

}



static void CheckAllHelpRefs()
{
	const char ** HelpDefs = ConstListToArray((ConstSingleList *) 
		&TheDefinedHelpTextNames) ;
	CheckDuplicate(HelpDefs,"help text") ;

	const char ** HelpRefs = ConstListToArray((ConstSingleList *) 
		&TheDefinedHelpReferences) ;
	CheckReferences(HelpDefs,HelpRefs,"help text",CheckDefineType);
	CheckReferences(HelpRefs,HelpDefs,"help text",CheckReferenceType);
	delete HelpDefs;
	delete HelpRefs;
}

static int IsDefinedMenu(const char * String)
{
	MenuDefListIterator Next(TheMenuDefs);
	MenuDef * CurrentMenu ;
	while (CurrentMenu = Next()) if (!strcmp(CurrentMenu->Name,
		String)) return 1;
	return 0;
}


static void OutDummyDynMenu()
{
	const char * String ;
	ConstStringListIterator Next(DynamicMenuReferences);
	while(String = Next()) {
		if (IsDefinedMenu(String)) continue ;
		DoDynMenuPhonyOut(String);
	} 
}

static void OutMenuDefine(ostream * Out, int Index, const char * Name)
{
	*Out << "#define DEF_" << Name << " " << Index << "\n" ;
}

MenuItem DefaultItemTemplate ((char *) 0, (ParameterizedAction *) 0, REMOTE,
		0, 0, (char *) 0, (char *) 0,
		(HelpText *) 0, (char *) 0) ;

static void OutMenuTemplate(const char * MenuName)
{
	MenuLineTemplate * TheTemplate =
		TheMenuLineTemplates.GetMenuLine(MenuName) ;
	MenuItem * OutItem ;
	if (TheTemplate) OutItem = TheTemplate->ItemTemplate ;
	else {
		OutItem = &DefaultItemTemplate ;
		cerr << "No menu item template for dynamic menu `" <<
			MenuName << "'.\n" ;
	}
	// OutItem->Dump("Loop head");
	*MenuOut << "	{ 0,\n\t\"" << OutItem->GetMenuString() <<
		"\",\n\t"  ;
	
	int CurrentActionType = OutItem->GetActionType();
	const char * TheString = OutItem->GetAction()->GetName() ;

	const char * TheHelpFile = 0;


	switch (CurrentActionType) {
case LOCAL_REMOTE :
		if (!IsDuplicateReference(TheString,LocalRemoteReferences)) {
			LocalActionCalls.Append(OutItem->GetAction());
			LocalRemoteActionCalls.Append(OutItem->GetAction());
			DoLocPhonyOut(TheString) ;
			LocalRemoteReferences.Append(TheString);
		}
case LOCAL :

 		*MenuOut << "MenuTypeCommand, (void *) &" << TheString <<
			"_Loc_Call_Struct" ;
		if (CurrentActionType != LOCAL_REMOTE) {
			if (!IsDuplicateReference(TheString,LocalReferences)) {
				LocalActionCalls.Append(OutItem->GetAction());
				LocalReferences.Append(TheString);
				DoLocPhonyOut(TheString) ;
			}
		}
		break ;
case REMOTE :
case REMOTE_OPTIONS :

		*MenuOut << "MenuTypeCommand, (void *) &" << TheString <<
			"_Rem_Call_Struct" ;
		if (!IsDuplicateReference(TheString,
		     *RemoteReferences)) {
			RemoteActionCalls.Append(OutItem->GetAction());
			if (CurrentActionType==REMOTE) {
				RemoteReferences->Append(TheString);
			}
		}
		break ;
case DYNAMIC_MENU:
		if(!IsDuplicateReference(TheString,
			DynamicMenuReferences))
			DynamicMenuReferences.Append(TheString) ;
case NEW_MENU:
		if(!strcmp(TheString,MenuName)) cerr <<
			"Menu `" << MenuName <<
			"' is self referencing.\n" ;
		if (CurrentActionType==NEW_MENU)
			MenuReferences.Append(TheString);
		*MenuOut << "MenuTypeMenu, " ;
		if (strcmp(TheString,"Orphan")) *MenuOut << "(void *) &" ;
		*MenuOut << TheString ;
		break ;
case HELP_FILE:
		TheDefinedHelpReferences.Append(TheString);		
		*MenuOut << "MenuTypeHelp, 0" ;
		TheHelpFile = TheString ;
		break ;
default:	
		DbgError("OutputMenu","Error invalid type.") ;
		break ;
	}
	ListOfListsIterator ListNext(ConflictLists) ;
	int ConflictSet = 0;
	CmdList * Cmds ;
	while (Cmds = ListNext()) {
		const char * Str ;
		ConstStringListIterator StrNext(*(Cmds->GetStrList())) ;
		while (Str = StrNext())  {
			if (!strcmp(OutItem->GetCommand(),Str)) {
				*MenuOut << ",	" <<
				Cmds->GetConflictName() ;
				ConflictSet = 1 ;
				goto LoopExit ;
			}
		}
	}
LoopExit:
	int Missing = 0 ;
	if (!ConflictSet) Missing++ ;

	if (OutItem->GetInit()) {
		char *Temp=MakeCallStructName(CurrentActionType,
			OutItem->GetInit()->Name);
		OutMissing(Missing,Temp);
		delete Temp;
	} else Missing++;

	const char * HelpTextName = 0;
	if (OutItem->GetMenuHelp()) HelpTextName = OutItem->GetMenuHelp()->Name;
	if (HelpTextName) OutMissing(Missing,HelpTextName);
	else OutMissing(Missing,OutItem->GetMenuHelpReference());

	if (!TheHelpFile) TheHelpFile = OutItem->GetHelpFileName();
	OutMissing(Missing,TheHelpFile,1);

	*MenuOut << "},\n" ;
}

static void OutDynMenuDef()
{
	ConstStringListIterator Next(DynamicMenuDefinitions);
	const char * Name ;
	ConstStringListIterator NewNext(DynamicMenuDefinitions);
	*MenuOut << "DynamicMenuInfo TheRemoteDynamicMenuTable[] = {\n" ;
	while (Name = NewNext()) {
		*MenuOut << "\t{&" << Name << ",\n\t" ;
		OutMenuTemplate(Name);
		*MenuOut << "\t\"" << Name << "\"\t},\n" ;
	}
	*MenuOut << "\t0\n};\n\n" ;
}

static void OutDynMenuHeaders()
{
	OutDummyDynMenu();
	OutDynMenuDef();
}


static void ProcessPrologue()
{
	Herr = OpenFile("help.err") ;

	char * menudef = Concatenate("menudef",CPP_SUFFIX);
	FileOut = OpenLocFile(menudef) ;
	delete menudef ;
	const char * FileName = "menutab.h";
	MenuOut = OpenLocFile(FileName);
	IncludeOnce(MenuOut,FileName);
	*MenuOut << "#include \"dynmen.h\"\n\n" ;
 	*MenuOut << "#include \"remdyn.h\"\n\n" ;
 	*MenuOut << "#include \"remexe.h\"\n\n" ;
 	// *MenuOut << "#include \"lcrmexe.h\"\n\n" ;


	FileName = "locphony.h" ;
	LocPhonyOut = OpenLocFile(FileName);
	IncludeOnce(LocPhonyOut,FileName);
	*LocPhonyOut << "#include \"cgidbg.h\"\n" ;
	*LocPhonyOut << "#include \"defined.h\"\n\n" ;


	RemoteReferences = new ConstStringList ;

	*FileOut << "#include \"portable.h\"\n" ;
	*FileOut << "#include \"menexe.h\"\n" ;
	*FileOut << "#include \"menutab.h\"\n" ;

	EndOfFileFound = NextFile ;
}

void Process()
{
	if (!ParseMain()) {
		cerr <<"No consistency checks done because of parse failure.\n";
		exit(2) ;
	}
	
	// OutIncludeFileList(DspRemoteOut,RemoteIncludeFiles) ;
	// OutIncludeFileList(RemoteOut,LocalIncludeFiles) ;

	CheckAllHelpRefs();

	OutputAllMenus();
	

	MenuDefinitions.Append("Orphan");
	const char ** MenuNames = ConstListToArray((ConstSingleList *) 
		&MenuDefinitions) ;
	const char ** MenuRefs = ConstListToArray((ConstSingleList *)
		&MenuReferences);
	MainEntryPair ** MainMenus = (MainEntryPair **)
		ListToArray((SingleList *) &MainEntrys) ;

	CheckForMainMenu(MenuNames,MenuRefs,MainMenus);

	if (FileOut) FileOut->flush();
	delete FileOut ;
	FileOut = 0;
	
	ParameterizedActionList * TheLists[3];
	TheLists[0] = &LocalActionCalls ;
	TheLists[1] = 0 ;
 	OutMenuCalls(TheLists,LocFile("menexe.h"));
	TheLists[0] = &LocalRemoteActionCalls ;
	TheLists[1] = &RemoteActionCalls ;
	TheLists[2] = 0 ;
 	OutMenuCalls(TheLists,LocFile("remexe.h"),1);
	TheLists[0] = &LocalRemoteActionCalls ;
	TheLists[1] = 0 ;
 	OutMenuCalls(TheLists,LocFile("lcrmexe.h"),2);

	OutDynMenuHeaders();

	// Do consistency checks
	// 1 - Duplicate Menu Names

	CheckDuplicateMenus(MenuNames) ;

			
	// 2 - All but 1 menu referenced by other menus

	




	// 3 - All referenced menus are defined
	CheckMenuReferences(MenuNames,MenuRefs);


	const char ** DynMenuNames = ConstListToArray((ConstSingleList *) 
		&DynamicMenuDefinitions) ;
	const char ** DynMenuRefs = ConstListToArray((ConstSingleList *)
		&DynamicMenuReferences);
	CheckReferences(DynMenuNames, DynMenuRefs, "dynamic menu",
		CheckDefineType);

	// 4 - Remote local and menus C++ names are all disjoint
	const char ** LocRefs = ConstListToArray((ConstSingleList *)
		&LocalReferences) ;
	const char ** RemRefs = ConstListToArray((ConstSingleList *)
		RemoteReferences) ;
	const char ** LocRemRefs = ConstListToArray((ConstSingleList *)
		&LocalRemoteReferences) ;
	const char ** LocalInitNames = ConstListToArray((ConstSingleList *)
		&LocalInitRoutines);
	const char ** RemoteInitNames = ConstListToArray((ConstSingleList *)
		&RemoteInitRoutines);
	const char ** MenuLocalInitNames = ConstListToArray((ConstSingleList *)
		&MenuLocalInitRoutines);
	const char ** MenuRemoteInitNames = ConstListToArray((ConstSingleList *)
		&MenuRemoteInitRoutines);

	const char ** CommandLists = new const char * [ConflictLists.Size()+1] ;
	// LogOut << "ConflictList size = " << ConflictLists.Size() << "\n" ;
	ListOfListsIterator Next(ConflictLists) ;
	const char ** Ptr = CommandLists ;
	CmdList * CmdL ;
	int Count = 0 ;
	while (CmdL  = Next()) {
/*
 *		LogOut << "ComnmandLists[" << Count++ << "] = `" <<
 *			CmdL->GetConflictName() << "'\n" ;
 */
		*Ptr++ = CmdL->GetConflictName() ;
	}
	*Ptr++ = 0 ;
	const Limit = 11;
	struct CheckUses * SeparateNames[Limit] ;

	int ind=0;
	SeparateNames[ind++] = new CheckUses("menu",MenuNames) ;
	SeparateNames[ind++] = new CheckUses("dynamic menu",DynMenuNames) ;
	SeparateNames[ind++] = new CheckUses("local command", LocRefs);
	SeparateNames[ind++] = new CheckUses("remote command", RemRefs);
	SeparateNames[ind++] = new CheckUses("local-remote command",
		LocRemRefs);
	SeparateNames[ind++] = new CheckUses("conflict list", CommandLists);
	SeparateNames[ind++] = new CheckUses("local initialization routines",
		LocalInitNames) ;
	SeparateNames[ind++] = new CheckUses("remote initialization routines",
		RemoteInitNames) ;
	SeparateNames[ind++] = new CheckUses(
		"local menu initialization routines", MenuLocalInitNames) ;
	SeparateNames[ind++] = new CheckUses(
		"remote menu initialization routines", MenuRemoteInitNames) ;
	SeparateNames[ind++] = 0 ;

	if (ind != Limit ) DbgError("main","Count is off for CheckUse");
	CheckDisjointNameSpaces(CPP_SUFFIX,SeparateNames);


	// 5 - Remote local and menus user names are all disjoint
	// const char ** LocUserRefs = CommandListToArray(UserLocalCmds) ;
	// const char ** RemUserRefs = CommandListToArray(UserRemoteCmds) ;
	const char ** LocUserRefs = ConstListToArray((ConstSingleList *)
		&UserLocalCmds) ;
	const char ** RemUserRefs = ConstListToArray((ConstSingleList *)
		&UserRemoteCmds) ;
	const char ** LocRemUserRefs = ConstListToArray((ConstSingleList *)
		&UserLocalRemoteCmds) ;
	const char ** MenuUserRefs = ConstListToArray((ConstSingleList *)
		&UserMenuCmds) ;

	const char ** UserCommandLists = new const char * [ConflictLists.Size()+1] ;

	ListOfListsIterator UserNext(ConflictLists) ;
	Ptr = UserCommandLists ;
	while (CmdL  = UserNext()) *Ptr++ = CmdL->GetConflictName() ;
	*Ptr = 0 ;
	const UserLimit = 6;
	struct CheckUses * UserSeparateNames[UserLimit] ;

	int ConflictIndex ;
	int Uind=0;
	UserSeparateNames[Uind++] = new CheckUses("menu",MenuUserRefs) ;
	UserSeparateNames[Uind++] = new CheckUses("local command", LocUserRefs);
	UserSeparateNames[Uind++] = new CheckUses("remote command",RemUserRefs);
	UserSeparateNames[Uind++] = new CheckUses("local-remote command",
		LocRemUserRefs);
	UserSeparateNames[ConflictIndex =Uind++] =
			       new CheckUses("conflict list", UserCommandLists);
	UserSeparateNames[Uind++] = 0 ;
	if (Uind != UserLimit || ConflictIndex != UserLimit -2)
		DbgError("main","Count is off for CheckUse (2)");

	CheckDisjointNameSpaces("User",UserSeparateNames);

	// 6 - All commands in Conflict arrays are referenced elsewhere
	UserSeparateNames[ConflictIndex] = 0;
	CheckAllConflictsReferenced(UserSeparateNames) ;


	// 7 - Make sure that any user name that occurs more than
	//     once refers to the same type object and same C++ name
	CheckSameUsage();

	// OutputRemoteSpecial(RemRefs,RemoteInitNames,MenuRemoteInitNames) ;


	IncludeOnce(LocPhonyOut);
	TeXProcess();


	// cout << "After LocPhony once\n" ;
	if (LocPhonyOut) LocPhonyOut->flush();
	delete LocPhonyOut ;
	// delete LocalOut ;
	IncludeOnce(MenuOut);
	// cout << "After MenuOut\n" ;
	EmitInitCode();
	// LogOut << "Final cleanup\n" ;
	// LocalActionCalls.Clear(); ;

/***********************************
	RemoteActionCalls.Clear(); ;
	LocalRemoteActionCalls.Clear(); ;

 	for (CheckUses ** CheckPtr = SeparateNames ; *CheckPtr; CheckPtr++) {
 		delete (*CheckPtr)->Names ;
 		delete *CheckPtr ;
 		*CheckPtr = 0 ;
 	}

 	for (CheckPtr = UserSeparateNames ; *CheckPtr; CheckPtr++) {
 		delete (*CheckPtr)->Names ;
 		delete *CheckPtr ;
 		*CheckPtr = 0 ;
 	}
***********************************/

	// cout << "After EmitInitCode\n" ;
	if (MenuOut) MenuOut->flush();
	delete MenuOut ;
	delete Herr ;
	exit(0);
}


