/*  objinit.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>
#include <string.h>
#include <stdlib.h>
#include "textfrag.h"
#include "inconce.h"
#include "domenus.h"
#include "objinit.h"
#include "texutil.h"
#include "sysconst.h"
#include "mkstr.h"

class InitInstances {
	const char * Name ;
	TextFragmentList * Code ;
	ConstStringList * Prior ;
	int CodeEmitted ;
	int RecursiveCheck ;
public:
	InitInstances(const char * Name,
		TextFragmentList * code, ConstStringList * prior);
	~InitInstances();
	TextFragmentList * GetCode() {return Code;}
	int EmitPrior(const char * Name);
	void EmitCode();
	const char * name() const {return Name ;}
};

class InitInstancesList: public SingleList {
public:
	ErrCode Insert(InitInstances *nt) {return SingleList::Insert(nt);}
	ErrCode Append(InitInstances *nt) {return SingleList::Append(nt);}
	InitInstances * Get()   {return (InitInstances *) SingleList::Get();}
	InitInstances * Pop() {return (InitInstances *) SingleList::Pop();}
	InitInstances * GetNFromTop(int N) ;
	InitInstances * GetNthEntry(int N) ;
	InitInstancesList(){;}
	void EmitPrior(const char *, ConstStringList *);
	int EmitPrior(const char *);
	int Size(){return SingleList::Size();}
} ;

class InitInstancesListIterator: public SingleListIterator {
public:
	InitInstancesListIterator(InitInstancesList& df):
		SingleListIterator((SingleList&) df){}
	InitInstances * operator()()
		{return (InitInstances *) Next();}
};

InitInstancesList AllInitCode ;


// C++ balks at too many too many includes in a single file -
// hence this kludge

class InitOut {
	ostream * InitFile ;
	ostream * InitHead ;
	const int FileLimit ;
	int FileIndex ;
	int LastLine ;
public:
	InitOut():InitFile(0),InitHead(0),FileLimit(30),FileIndex(0),
		LastLine(0){}
	void CheckInitFileClose() ;
	~InitOut() {CheckInitFileClose();}
	void CheckInitFileOpen() ;
	void EntryOut(TextFragment * Frag) ;
	void EmitCode(TextFragmentList * Code) ;
};

static InitOut InitOutFiles ;
		

void InitOut::CheckInitFileClose()
{
	if (!InitFile) return ;
	*InitFile << "\n}\n\n" ;
	if (InitFile) InitFile->flush();
	delete InitFile ;
	IncludeOnce(InitHead);
	if (InitHead) InitHead->flush();
	delete InitHead ;
	InitHead = InitFile = 0;
}

static const char * MenuInitRoutine = "void InitAllMenuRoutines()" ;

void InitOut::CheckInitFileOpen()
{
	if (InitFile) return ;
	const char * FileName = "meninit.h" ;
	InitHead = OpenRemFile(FileName);
	IncludeOnce (InitHead,FileName) ;
	*InitHead << MenuInitRoutine << ";\n\n" ;
	IncludeOnce (InitHead);
	InitHead->flush();
	delete InitHead ;
	const char * FileNameinc = "incinit.h";
	InitHead = OpenRemFile(FileNameinc);
	IncludeOnce (InitHead,FileNameinc) ;
	// cerr << "initHead = " << InitHead << "\n" ;
	char * meninit = Concatenate("meninit",CPP_SUFFIX);
	InitFile = OpenRemFile(meninit);
	*InitFile << "#include \"fstinit.h\"\n" ;
	*InitFile << "#include \"incinit.h\"\n" ;
	*InitFile << "#include \"meninit.h\"\n\n" ;
	*InitFile << "#include \"hrdarth.h\"\n\n" ;
	*InitFile << MenuInitRoutine << "\n{\n" ;
	delete meninit ;
}

void InitOut::EntryOut(TextFragment * Frag)
{
	const char * Include = "#include" ;
	if (Frag->Text) if (!strncmp(Frag->Text,Include,strlen(Include))) {
		// cerr << "Frag: " << Frag->Text << "\n" ;
		// cerr << "initHead = " << InitHead << "\n" ;
		*InitHead << Frag->Text << "\n" ;
		return ;
	}
	if (LastLine) if (LastLine != Frag->Line) *InitFile << "\n" ;
	LastLine = Frag->Line ;
	if (Frag->Text) {
		// cerr << "Frag: " << Frag->Text << "\n" ;
		*InitFile << Frag->Text ;
	}
}

void InitOut::EmitCode(TextFragmentList * Code)
{
	CheckInitFileOpen();
	TextFragment * Frag ;
	LastLine = 0 ;
	while (Frag = Code->Get()) {
		if (Frag->Line < 0) {
			Frag->Line = - Frag->Line ;
			cerr << "Warning: embedded name `" << Frag->Text <<
				"' in initialization code.\n" ;
		}
		EntryOut(Frag) ;
		// delete Frag ;
	}
}

static int compare(const void * a, const void * b)
{
    const InitInstances ** A = (const InitInstances **) a ;
    const InitInstances ** B = (const InitInstances **) b ;
    if (!A) if (!B) return 0 ; else return -1 ;
    if (!B) return 1 ;

    const char * Aa = (*A)->name() ;
    const char * Bb = (*B)->name() ;
    if (!Aa) if (!Bb) return 0; else return -1 ;
    if (!Bb) return 1 ;
    return TeXstrcmp(Aa,Bb);
}



void EmitInitCode()
{
	// cout << "In EmitInitCode\n" ;
	int size = AllInitCode.Size() ;
	InitInstances ** list = new InitInstances *[size+1] ;

	InitInstancesListIterator Next(AllInitCode);
	InitInstances * Instance ;

	int index = 0 ;
	while (Instance = Next()) list[index++] = Instance ;
	list[index++] = 0 ;

	qsort((char *) list, size, sizeof(list[0]),compare);

	for (InitInstances ** ins = list; *ins; ins++) (*ins)->EmitCode();

	InitOutFiles.CheckInitFileClose();
	delete list ;
}


int InitInstances::EmitPrior(const char * ThisName)
{
	// cout << "EmitPrior enter\n" ;
	if (strcmp(ThisName,Name)) return 0;
	// cout << "EmitPrior before EmitCode\n" ;
	EmitCode();
	// cout << "EmitPrior before exit\n" ;
	return 1;
}

int InitInstancesList::EmitPrior(const char * ThisName)
{
	// cout << "List::EmitPrior enter\n" ;
	InitInstancesListIterator Next(AllInitCode);
	InitInstances * Instance ;
	// cout << "List::EmitPrior loop\n" ;
	while (Instance = Next()) if(Instance->EmitPrior(ThisName)) return 1 ;
	// cout << "List::EmitPrior exit\n" ;
	return 0;
}

void InitInstancesList::EmitPrior(const char * EmittedFor,
	ConstStringList * Prior)
{
	// cout << "List(" << EmittedFor << ",)::EmitPrior enter\n" ;
	if (!Prior) return ;
	ConstStringListIterator Next(*Prior) ;
	const char * ThisName ;
	// cout << "List(,)::EmitPrior before loop\n" ;
	while (ThisName = Next()) {
		// cout << "Loop start\n" ;
		// cout << "This Name is `" << ThisName << "'\n" ;
		if (!strcmp(ThisName,EmittedFor)) continue ;
		// cout << "List(,)::EmitPrior before call EmitPrior\n" ;
		if (!EmitPrior(ThisName)) cerr << "Object `" << ThisName
			<< "' used in the contructor of `" << EmittedFor
			<< "'\nis not defined.\n" ;
	}
	// cout << "List(,)::EmitPrior exit\n" ;
}

InitInstances::InitInstances(const char * name,
		TextFragmentList * code, ConstStringList * prior)
{
	Name = name ;
	Code = code ;
	Prior = prior ;
	CodeEmitted = 0;
	RecursiveCheck = 0;
}

InitInstances::~InitInstances()
{
	delete Code ;
	delete Prior ;
}

void InitInstances::EmitCode()
{
	// cout << "EmitCode for " << Name << " \n" ;
	if (CodeEmitted) return ;
	if (Prior) {
		if (RecursiveCheck) {
			cerr <<
		"There is a recursive reference in the constructor of `"
			<< Name << "'.\n"  ;
			return ;
		}
		RecursiveCheck = 1 ;
		// cout << "Before EmitPrior\n" ;
		AllInitCode.EmitPrior(Name,Prior);
		// cout << "After EmitPrior\n" ;
		RecursiveCheck = 0;
	}
	InitOutFiles.EmitCode(Code);
	CodeEmitted = 1;
	// cout << "EmitCode exit\n" ;
}

void FoundInitCode(TextFragmentList * List, Value * Name, Value * PriorList)
{
	InitInstances * Instance = new InitInstances(Name->Name,List,
		PriorList ? PriorList->Strings : 0);
	AllInitCode.Append(Instance) ;
}

Value * MakeNameList(Value *Val)
{
	const char * Name = Val->Name ;
	Val->Strings = new ConstStringList;
	Val->Strings->Append(Name) ;
	return Val ;
}


Value * NameListAppend(Value * List, Value * Name)
{
	List->Strings->Append(Name->Name) ;
	return List ;
}

