#include <stream.h>
#include <fstream.h>
#include <string.h>
#include "mknodey.h"
#include "domknode.h"
#include "debug.h"
#include "mknodelx.h"
#include "stattyp.h"

TextFragmentList * StaticInitCode = 0 ;


enum EmitTypeFlags {EmitTypeNull=0, EmitTypeConvert=1, EmitTypePtr=2};
class StaticArray {
	const char * GetTypeName() const ;
public:
	const Compound * Exp ;
	const int Type ;
	const char * Name ;
	StaticArray(int type, const char * name, Compound *exp=0);
	void EmitType(MofStream& hd, EmitTypeFlags = EmitTypeNull) ;
	void EmitStreamValue(MofStream& CppOut, int Index = -1) ;
	void EmitArrayStreamValues(MofStream& CppOut);
	void EmitInitName(MofStream& CppOut) ;
	void EmitInitInstanceName(MofStream& CppOut) ;
	void EmitCodeToEmitArray(MofStream& CppOut) ;
	void EmitCodeToEmit(MofStream& CppOut) ;
	void EmitSetCtorParam(MofStream& CppOut) ;
};

StaticArray::StaticArray(int type, const char * name, Compound *exp):
	Type(type),
	Name(name),
	Exp(exp)
{
}

const char * StaticArray::GetTypeName() const
{
	const char * TypeName = TerminalToCppType(Type);
	if (!TypeName) DbgError("StaticArray::GetEmitType","bad type");
	return TypeName ;
}

void StaticArray::EmitType(MofStream& hd, EmitTypeFlags Flags)
{
	const char * TypeName = GetTypeName();
	if (Flags & EmitTypeConvert) TypeName = GetStatInitName(TypeName);
	hd << TypeName ;
	if (Exp && (Flags&EmitTypePtr)) hd <<  " * " ;
	else hd << " " ;
}

void StaticArray::EmitInitName(MofStream& CppOut)
{
	CppOut << NodeName << "StaticInit" << Name ;
}

void StaticArray::EmitInitInstanceName(MofStream& CppOut)
{
	EmitInitName(CppOut) ;
	CppOut << "_\" << GetName() << \"" ;
}

void StaticArray::EmitSetCtorParam(MofStream& CppOut)
{
	if (Exp) {
		const char * TypeName = GetTypeName();
		const char * StaticInitName = GetStatInitName(TypeName);
		if (strcmp(TypeName,StaticInitName)) 
			CppOut << "(" << GetTypeName() << " * ) " ;
			
	}
	EmitInitInstanceName(CppOut);
	CppOut << "\";\n" ;
}

void StaticArray::EmitStreamValue(MofStream& CppOut, int Index)
{
	CppOut << "OutStringStaticValue(Outs,\"" << GetTypeName() << "\", ";
	CppOut << "&(" << Name ;
	if (Index > -1) CppOut << "[i]" ;
	CppOut << ")) ;\n" ;
}


void StaticArray::EmitArrayStreamValues(MofStream& CppOut)
{
	CppOut << "\tfor (int i = 0 ; i < (" ;
	Exp->ExpOut(&CppOut) ;
	CppOut << ") ; i++) {\n" ;
	CppOut << "\t\tif (i) Outs << \",\";\n" ;
	CppOut << "\t\tOuts << \"\\n\\t\";\n\t\t"; 
	EmitStreamValue(CppOut,0) ;
	CppOut << "\t}\n" ;
}

void StaticArray::EmitCodeToEmit(MofStream& CppOut)
{
	if (Exp) {
		CppOut << "    if (" ;
		Exp->ExpOut(&CppOut);
		CppOut << ") {\n" ;
	}
	EmitCodeToEmitArray(CppOut);
	if (Exp) {
		CppOut << "    } else {\n" ;
		CppOut << "\tOuts << \"static " ;
		EmitType(CppOut,EmitTypeConvert) ;
		CppOut << "*" ;
		EmitInitInstanceName(CppOut) ;
		CppOut << " = 0 ;\\n\" ;\n    }\n" ;
	}
}

void StaticArray::EmitCodeToEmitArray(MofStream& CppOut)
{
	CppOut << "\tOuts << \"static " ;
	EmitType(CppOut,EmitTypeConvert) ;
	EmitInitInstanceName(CppOut) ;
	if (Exp) {
		CppOut << "[] = {\";\n" ;
		EmitArrayStreamValues(CppOut);
		CppOut << "\tOuts << \"\\n\\t};\\n\";\n" ;
	} else  {
		CppOut << " = \";\t\n\t" ; 
		EmitStreamValue(CppOut);
		CppOut << "\n\tOuts << \";\\n\";\n" ;
	}
}

class StaticArrayList: public SingleList {
public:
	ErrCode Insert(StaticArray *nt) {return SingleList::Insert(nt);}
	ErrCode Append(StaticArray *nt) {return SingleList::Append(nt);}
	StaticArray * Get()   {return (StaticArray *) SingleList::Get();}
	StaticArray * Pop() {return (StaticArray *) SingleList::Pop();}
	StaticArray * GetNFromTop(int N) ;
	StaticArray * GetNthEntry(int N) ;
	StaticArrayList(){}
	// ~StaticArrayList();
	int Size(){return SingleList::Size();}
	int AnyChangeable();
	void CheckAtMostOneFirstDefault();
	void OutTeX(OutTokens& Out);
	void BriefOutTeX(OutTokens& Out);
} ;

class StaticArrayListIterator: public SingleListIterator {
public:
	StaticArrayListIterator(StaticArrayList& df):
		SingleListIterator((SingleList&) df){}
	StaticArray * operator()()
		{return (StaticArray *) Next();}
};


StaticArrayList TheStaticArrays;

void FoundStaticArray(int type, const char * name, Compound * exp)
{
	TheStaticArrays.Append(new StaticArray(type,name,exp));
}

void FoundStaticScalar(int type, const char * name)
{
	TheStaticArrays.Append(new StaticArray(type,name));
}


void EmitStaticInitCtorParams(MofStream& Out)
{
	if (!TheStaticArrays.Size()) return ;
	StaticArrayListIterator Next(TheStaticArrays);
	StaticArray * Ary ;
	Out.PushFirstFileSelect(BaseStreamBuf::Off);
	while (Ary=Next()) {
		Out << ",\n\t\t" ;
		Ary->EmitType(Out,EmitTypePtr);
		Ary->EmitInitName(Out);
	}
	Out.PopFirstFileSelect();
}

void EmitStaticInitCtors(MofStream& CppOut)
{
	if (!TheStaticArrays.Size()) return ;
	StaticArrayListIterator Next(TheStaticArrays);
	StaticArray * Ary ;
	CppOut.PushFirstFileSelect(BaseStreamBuf::Off);
	while (Ary=Next()) {
		CppOut << ",\n\t" << Ary->Name << "(" ;
		Ary->EmitInitName(CppOut);
		CppOut << ")" ;
	}
	CppOut.PopFirstFileSelect();
}
static void EmitStaticFunc(const char * f_name, int DecFlag, MofStream& hd)
{
	if (!TheStaticArrays.Size()) return ;
	hd.PushSecondFileSelect(BaseStreamBuf::Off);
	if (DecFlag) hd << "\tvirtual " ;
	hd << "ErrCode " ;
	if (!DecFlag) hd << NodeName << "::" ;
	hd << f_name << "(OutTokens& Out)" ;
	if (DecFlag) hd << ";\n" ;
	hd.PopSecondFileSelect();
}

const char * EmitStaticCtorParameters = "EmitStaticCtorParameters" ;
const char * EmitStaticInit = "EmitStaticInit" ;

void EmitStaticEmitCtorFunc(int DecFlag, MofStream& hd)
{
	EmitStaticFunc(EmitStaticCtorParameters,DecFlag,hd);
}

void EmitStaticEmitFunc(int DecFlag, MofStream& hd)
{
	EmitStaticFunc(EmitStaticInit,DecFlag,hd);
}


void EmitFuncBody(const char * f_name, MofStream& CppOut, int CtorFlag)
{
	if (!TheStaticArrays.Size()) return ;
	CppOut.PushSecondFileSelect(BaseStreamBuf::Off);
	EmitStaticFunc(f_name,0,CppOut);
	CppOut << "\n{\n" ;
	StaticArrayListIterator Next(TheStaticArrays);
	StaticArray * Ary ;
	CppOut << "\tostream& Outs = *(Out.GetStream());\n" ;
	while (Ary=Next()) if (CtorFlag) {
		CppOut << "\tOuts << \",\\n\\t" ;
		Ary->EmitSetCtorParam(CppOut);
	}
	else Ary->EmitCodeToEmit(CppOut);
	CppOut << "\treturn OK;\n}\n\n" ;
	CppOut.PopSecondFileSelect();
}

void EmitStaticEmitCtorFuncBody(MofStream& CppOut)
{
	EmitFuncBody(EmitStaticCtorParameters,CppOut,1);
}

void EmitStaticEmitFuncBody(MofStream& CppOut)
{
	EmitFuncBody(EmitStaticInit,CppOut,0);
}

void EmitStaticInitVariables(MofStream& hd)
{
	if (!TheStaticArrays.Size()) return ;
	StaticArrayListIterator Next(TheStaticArrays);
	StaticArray * Ary ;
	while (Ary=Next()) {
		hd << "\t" ;
		Ary->EmitType(hd,EmitTypePtr);
		hd << Ary->Name << " ;\n" ;
	}
	EmitStaticEmitFunc(1,hd);
	EmitStaticEmitCtorFunc(1,hd);
}

