/*******************************************************************************
 *	GenBoap.cc	Boap output
 *			T.Barnaby,	BEAM Ltd,	2/5/03
 *******************************************************************************
 */
#define	DEBUG	1

#define	DODATA		0

#include <stdio.h>
#include <stdarg.h>
#include <strings.h>
#include <GenBoap.h>
#include <bidl.h>

void GenBoap::produceIntC(Node* n){
	BIter		i;
	BString		s;
	BString		c;
	BList<Node*>*	nl;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileIntC.writeLine(BString("namespace ") + omodule + " {\n");
		ofileIntC.indentMore();

		ofileIntC.writeLine(BString("const BUInt32 apiVersion = ") + oapiVersion + ";\n\n");
		
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntC(n->nodes()[i]);
		}
		ofileIntC.indentLess();
		ofileIntC.writeLine("}\n");
		break;
	case Node::TINTERFACE:
		ointerface = n->name();
		// Client
		ofileIntC.writeLine(BString("class ") + ointerface + " : public BoapClientObject {\n");
		ofileIntC.writeLine("public:\n");
		ofileIntC.indentMore();
		ofileIntC.writeLine(BString("\t") + ointerface + "(BString name = \"\");\n");
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntC(n->nodes()[i]);
		}
		ofileIntC.indentLess();
		ofileIntC.writeLine("private:\n");
		ofileIntC.writeLine("};\n");
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntC(n->nodes()[i]);
		}
		break;
#if DODATA
	case Node::TSTRUCT:
		if((n->nodes().number() == 2) && n->node(1)){
			ofileIntC.writeLine(BString("struct ") + n->name() + " : public " +  n->node(1)->name() + " {\n");
		}
		else {
			ofileIntC.writeLine(BString("struct ") + n->name() + " {\n");
		}
		ofileIntC.indentMore();

		nl = &n->node(0)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			s = getTypeName(nl->get(i)->node(0)) + "\t";
			s = s + nl->get(i)->node(1)->name();
			if(nl->get(i)->node(2) && nl->get(i)->node(2)->nodeType() == Node::TCOMMENT)
				c = BString("\t//") + nl->get(i)->node(2)->name();
			else
				c = "";
			ofileIntC.writeLine(s + c + ";\n");
		}

		ofileIntC.indentLess();
		ofileIntC.writeLine("};\n\n");
		break;
#endif
	case Node::TFUNC:
		s = s + getTypeName(n->node(0)) + " ";
		s = s + n->name() + "(";
		nl = &n->node(1)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			if(i != nl->begin())
				s = s + ", ";
			if(nl->get(i)->node(0)->name() == "in"){
				s = s + getTypeName(nl->get(i)->node(1)) + " ";
			}
			else if(nl->get(i)->node(0)->name() == "inref"){
					s = s + "const " + getTypeName(nl->get(i)->node(1)) + "& ";
			}
			else {
				s = s + getTypeName(nl->get(i)->node(1)) + "& ";
			}
			s = s + nl->get(i)->node(2)->name();
		}
		s = s + ");";
		if(n->node(2) && n->node(2)->nodeType() == Node::TCOMMENT)
			s = s + "\t//" + n->node(2)->name();
		ofileIntC.writeLine(s + "\n");
		break;
	case Node::TCOMMENT:
		s = BString("//") + n->name() + "\n";
		ofileIntC.writeLine(s);
		break;
	case Node::TAPIVERSION:
		oapiVersion = n->name().retInt();
		break;
	default:
		break;
	}
}

void GenBoap::produceIntS(Node* n){
	BIter		i;
	BString		s;
	BString		sa;
	BList<Node*>*	nl;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileIntS.writeLine(BString("namespace ") + omodule + " {\n");
		ofileIntS.indentMore();
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntS(n->nodes()[i]);
		}
		ofileIntS.indentLess();
		ofileIntS.writeLine("}\n");
		break;
	case Node::TINTERFACE:
		ointerface = n->name();
		// Server
		ofileIntS.writeLine(BString("class ") + ointerface + "Service : public BoapServiceObject {\n");
		ofileIntS.writeLine("public:\n");
		ofileIntS.indentMore();
		ofileIntS.writeLine(BString("\t") + ointerface + "Service(BoapServer& server, BString name);\n");
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntS(n->nodes()[i]);
		}
		ofileIntS.indentLess();
		ofileIntS.writeLine("};\n");
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntS(n->nodes()[i]);
		}
		break;
	case Node::TSTRUCT:
		break;
	case Node::TFUNC:
		s = BString("BError do") + n->name() + "(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx);\n";
		ofileIntS.writeLine(s);
		
		s = "virtual ";
		s = s + getTypeName(n->node(0)) + " ";
		s = s + n->name() + "(";
		
		if(ooptions.hasKey("connectionClass")){
			sa = sa + ooptions["connectionClass"] + "* conn";
		}
		
		nl = &n->node(1)->nodes();

		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			if(sa.len())
				sa = sa + ", ";
			if(nl->get(i)->node(0)->name() == "in"){
				sa = sa + getTypeName(nl->get(i)->node(1)) + " ";
			}
			else if(nl->get(i)->node(0)->name() == "inref"){
				sa = sa + "const " + getTypeName(nl->get(i)->node(1)) + "& ";
			}
			else {
				sa = sa + getTypeName(nl->get(i)->node(1)) + "& ";
			}

			sa = sa + nl->get(i)->node(2)->name();
		}
		s = s + sa + ") = 0;\n";
		ofileIntS.writeLine(s);
		break;
	default:
		break;
	}
}

void GenBoap::produceIntT(Node* n){
	BIter		i;
	BString		s;
	BString		sa;
	BList<Node*>*	nl;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileIntT.writeLine(BString("using namespace ") + omodule + ";\n\n");
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntT(n->nodes()[i]);
		}
		break;
	case Node::TINTERFACE:
		ointerface = n->name();
		// Server
		ofileIntT.writeLine(BString("class ") + ointerface + "Server : public " + omodule + "::" + ointerface + "Service {\n");
		ofileIntT.writeLine("public:\n");
		ofileIntT.indentMore();

		if(ooptions.hasKey("serverControl"))
			ofileIntT.writeLine(BString("\t") + ointerface + "Server(Control& control, BoapServer& server, BString name);\n");
		else
			ofileIntT.writeLine(BString("\t") + ointerface + "Server(BoapServer& server, BString name);\n");

		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntT(n->nodes()[i]);
		}
		ofileIntT.indentLess();

		if(ooptions.hasKey("serverControl")){
			ofileIntT.writeLine("private:\n");
			ofileIntT.indentMore();
			ofileIntT.writeLine("Control&\tocontrol;\n");
			ofileIntT.indentLess();
		}

		ofileIntT.writeLine("};\n\n");
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceIntT(n->nodes()[i]);
		}
		break;
	case Node::TSTRUCT:
		break;
	case Node::TFUNC:
		s = "";
		s = s + getTypeName(n->node(0)) + " ";
		s = s + n->name() + "(";

		if(ooptions.hasKey("connectionClass")){
			sa = sa + ooptions["connectionClass"] + "* conn";
		}
		
		nl = &n->node(1)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			if(sa.len())
				sa = sa + ", ";
			if(nl->get(i)->node(0)->name() == "in"){
				sa = sa + getTypeName(nl->get(i)->node(1)) + " ";
			}
			else if(nl->get(i)->node(0)->name() == "inref"){
				sa = sa + "const " + getTypeName(nl->get(i)->node(1)) + "& ";
			}
			else {
				sa = sa + getTypeName(nl->get(i)->node(1)) + "& ";
			}

			sa = sa + nl->get(i)->node(2)->name();
		}
		s = s + sa + ");\n";
		ofileIntT.writeLine(s);
		break;
	default:
		break;
	}
}

void GenBoap::produceImpT(Node* n){
	BString		s;
	BString		sa;
	BIter		i;
	BList<Node*>*	nl;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileImpT.writeLine(BString("using namespace ") + omodule + ";\n\n");
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpT(n->nodes()[i]);
		}
		break;
	case Node::TINTERFACE:
		ointerface = n->name();
		if(ooptions.hasKey("serverControl"))
			ofileImpT.writeLine(ointerface + "Server::" + ointerface + "Server(Control& control, BoapServer& server, BString name) : " + ointerface + "Service(server, name), ocontrol(control){\n");
		else
			ofileImpT.writeLine(ointerface + "Server::" + ointerface + "Server(BoapServer& server, BString name) : " + ointerface + "Service(server, name){\n");
		ofileImpT.writeLine("}\n\n");		
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpT(n->nodes()[i]);
		}
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpT(n->nodes()[i]);
		}
		break;
	case Node::TFUNC:
		s = "";
		s = s + getTypeName(n->node(0)) + " ";
		s = s + ointerface + "Server::" + n->name() + "(";

		if(ooptions.hasKey("connectionClass")){
			sa = sa + ooptions["connectionClass"] + "* conn";
		}

		nl = &n->node(1)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			if(sa.len())
				sa = sa + ", ";
			if(nl->get(i)->node(0)->name() == "in"){
				sa = sa + getTypeName(nl->get(i)->node(1)) + " ";
			}
			else if(nl->get(i)->node(0)->name() == "inref"){
				sa = sa + "const " + getTypeName(nl->get(i)->node(1)) + "& ";
			}
			else {
				sa = sa + getTypeName(nl->get(i)->node(1)) + "& ";
			}
				
			sa = sa + nl->get(i)->node(2)->name();
		}
		s = s + sa + "){\n";
		ofileImpT.writeLine(s);
		ofileImpT.indentMore();
		if(ooptions.hasKey("serverControl")){
			s = "return ocontrol.";
			s += n->name() + "(";

			if(ooptions.hasKey("connectionClass")){
				s += "conn";
				if(nl->number())
					s += ", ";
			}
			
			nl = &n->node(1)->nodes();
			for(nl->start(i); !nl->isEnd(i); nl->next(i)){
				if(i != nl->begin())
					s = s + ", ";

				s = s + nl->get(i)->node(2)->name();
			}
			s = s + ");\n";
			ofileImpT.writeLine(s);
		}
		else {
			if(getTypeName(n->node(0)) == "BError"){
				s = getTypeName(n->node(0)) + "\terr;\n\n";
				ofileImpT.writeLine(s);
				s = "return err;\n";
				ofileImpT.writeLine(s);
			}
			else {
				s = getTypeName(n->node(0)) + "\tret;\n\n";
				ofileImpT.writeLine(s);
				s = "return ret;\n";
				ofileImpT.writeLine(s);
			}
		}
		ofileImpT.indentLess();
		ofileImpT.writeLine("}\n\n");
		break;
	default:
		break;
	}
}

void GenBoap::pushPopVar(FileIndent& file, int push, BString func, Node* type, BString name){
	BIter		i;
	BList<Node*>*	nl;
	Type*		t;
	Type*		dt;
	BString		s;
	BString		swapType;
	
//	printf("GenBoap::pushPopVar: Func(%s) Type(%s) Name(%s)\n", func.retStr(), type->name().retStr(), name.retStr());
	if(t = gtypelist.search(type->name())){
//		printf("DoType: %p Name(%s) Derived(%p)\n", type->name().retStr(), type, t->derived());
		if(dt = t->derived()){
			nl = &dt->node()->nodes();
			for(nl->start(i); !nl->isEnd(i); nl->next(i)){
				pushPopVar(file, push, func, nl->get(i)->node(0), name + "." + nl->get(i)->node(1)->name());
			}
		}
//		printf("Node: %s %s\n", t->node()->name().retStr(), Node::nodeTypeName(t->node()->nodeType()).retStr());
		if(t->node()->name() == "enumeratorList"){
			if(push)
				file.writeLine(func + "(int(" + name + "));\n");
			else
				file.writeLine(func + "((int&)" + name + ");\n");
		}
		else {
			nl = &t->node()->nodes();
			for(nl->start(i); !nl->isEnd(i); nl->next(i)){
				pushPopVar(file, push, func, nl->get(i)->node(0), name + "." + nl->get(i)->node(1)->name());
			}
		}
//		printf("DoType: %p Derived(%p): Done\n", type, t->derived());
	}
	else if(type->nodeType() == Node::TTYPELIST){
		if(push){
			file.writeLine("{\n");
			file.indentMore();
			file.writeLine(BString("BIter i") + opushPopDepth + ";\n");
			file.writeLine(func + "(" + name + ".number());\n");
			s = BString("for(") + name + ".start(i" + opushPopDepth + "); !" + name + ".isEnd(i" + opushPopDepth + "); " + name + ".next(i" + opushPopDepth + ")){\n";
			file.writeLine(s);
			file.indentMore();
			pushPopVar(file, push, func, type->node(0), name + "[i" + opushPopDepth++ + "]");
			opushPopDepth--;
			file.indentLess();
			file.writeLine("}\n");
			file.indentLess();
			file.writeLine("}\n");
		}
		else {
			file.writeLine("{\n");
			file.indentMore();
			file.writeLine(BString("BUInt32 n") + opushPopDepth + ";\n");
			s = getTypeName(type->node(0)) + " v" + opushPopDepth + ";\n";
			file.writeLine(s);
			file.writeLine(name + ".clear();\n");
			file.writeLine(func + "(n" + opushPopDepth + ");\n");
			file.writeLine(BString("while(n") + opushPopDepth + "--){\n");
			file.indentMore();
			pushPopVar(file, push, func, type->node(0), BString("v") + opushPopDepth++);
			opushPopDepth--;
			file.writeLine(name + ".append(v" + opushPopDepth + ");\n");
			file.indentLess();
			file.writeLine("}\n");
			file.indentLess();
			file.writeLine("}\n");
		}
	}
	else if(type->nodeType() == Node::TTYPEDICT){
		BString	typeName = getTypeName(type->node(0));
		
		if(push){
			file.writeLine("{\n");
			file.indentMore();
			file.writeLine(BString("BDict<") + typeName + ">::iterator i" + opushPopDepth + ";\n");
			file.writeLine(func + "(BUInt32(" + name + ".size()));\n");
			s = BString("for(") + name + ".start(i" + opushPopDepth + "); !" + name + ".isEnd(i" + opushPopDepth + "); " + name + ".next(i" + opushPopDepth + ")){\n";
			file.writeLine(s);
			file.indentMore();
			file.writeLine(func + "(" + name + ".key(i" + opushPopDepth + "));\n");
			pushPopVar(file, push, func, type->node(0), name + "[i" + opushPopDepth++ + "]");
			opushPopDepth--;
			file.indentLess();
			file.writeLine("}\n");
			file.indentLess();
			file.writeLine("}\n");
		}
		else {
			file.writeLine("{\n");
			file.indentMore();
			file.writeLine(BString("BUInt32 n") + opushPopDepth + ";\n");
			file.writeLine(BString("BString k") + opushPopDepth + ";\n");
			s = getTypeName(type->node(0)) + " v" + opushPopDepth + ";\n";
			file.writeLine(s);
			file.writeLine(name + ".clear();\n");
			file.writeLine(func + "(n" + opushPopDepth + ");\n");
			file.writeLine(BString("while(n") + opushPopDepth + "--){\n");
			file.indentMore();
			file.writeLine(func + "(k" + opushPopDepth + ");\n");
			pushPopVar(file, push, func, type->node(0), BString("v") + opushPopDepth++);
			opushPopDepth--;
			file.writeLine(name + "[k" + opushPopDepth + "] = v" + opushPopDepth + ";\n");
			file.indentLess();
			file.writeLine("}\n");
			file.indentLess();
			file.writeLine("}\n");
		}
	}
	else if(type->nodeType() == Node::TTYPEARRAY){
		// Check if raw data copy can be used

#ifdef ZAP
		printf("GenBoap::Checking For Raw Type: %s\n", getTypeName(type->node(0)).retStr());
		printf("GenBoap::IsRawType: %d\n", isRawType(type->node(0)));
		printf("\n");
#endif		

		if(isRawType(type->node(0))){
#ifdef ZAP
			printf("Push raw array of type: %s (%s)\n", getTypeName(type->node(0)).retStr(), getTypeSwapList(type->node(0)).retStr());
#endif
			swapType = getTypeSwapList(type->node(0));

			if(push){
				file.writeLine("{\n");
				file.indentMore();

				file.writeLine(func + "(BUInt32(" + name + ".size()));\n");
				file.writeLine(func + "(" + name + ".size() * sizeof(" + getTypeName(type->node(0)) + "), " + name + ".data(), \"" + swapType + "\");\n");

				file.indentLess();
				file.writeLine("}\n");
			}
			else {
				file.writeLine("{\n");
				file.indentMore();

				file.writeLine(BString("BUInt32 n") + opushPopDepth + ";\n");
				file.writeLine(func + "(n" + opushPopDepth + ");\n");
				file.writeLine(name + ".resize(n" + opushPopDepth + ");\n");
				file.writeLine(func + "(n" + opushPopDepth + " * sizeof(" + getTypeName(type->node(0)) + "), " + name + ".data(), \"" + swapType + "\");\n");

				file.indentLess();
				file.writeLine("}\n");
			}
		}
		else {
			if(push){
				file.writeLine("{\n");
				file.indentMore();
				file.writeLine(BString("BUInt32 i") + opushPopDepth + ";\n");
				file.writeLine(func + "(BUInt32(" + name + ".size()));\n");
				s = BString("for(i") + opushPopDepth + " = 0; i" + opushPopDepth + " < " + name + ".size(); i" + opushPopDepth + "++){\n";
				file.writeLine(s);
				file.indentMore();
				pushPopVar(file, push, func, type->node(0), name + "[i" + opushPopDepth++ + "]");
				opushPopDepth--;
				file.indentLess();
				file.writeLine("}\n");
				file.indentLess();
				file.writeLine("}\n");
			}
			else {
				file.writeLine("{\n");
				file.indentMore();
				file.writeLine(BString("BUInt32 n") + opushPopDepth + ";\n");
				s = getTypeName(type->node(0)) + " v" + opushPopDepth + ";\n";
				file.writeLine(s);
				file.writeLine(func + "(n" + opushPopDepth + ");\n");
				file.writeLine(name + ".resize(n" + opushPopDepth + ");\n");
				file.writeLine(BString("for(BUInt32 i = 0; i < n") + opushPopDepth + "; i++){\n");
				file.indentMore();
				pushPopVar(file, push, func, type->node(0), BString("v") + opushPopDepth++);
				opushPopDepth--;
				file.writeLine(name + "[i] = v" + opushPopDepth + ";\n");
				file.indentLess();
				file.writeLine("}\n");
				file.indentLess();
				file.writeLine("}\n");
			}
		}
	}
	else {
		file.writeLine(func + "(" + name + ");\n");
	}
}

void GenBoap::produceFuncC(Node* n){
	BIter		i;
	BString		s;
	BList<Node*>*	nl;
	BString		retType;
	
	retType = getTypeName(n->node(0));
	
	ofileImpC.writeLine("BError\terr;\n");
	ofileImpC.writeLine(retType + "\tret;\n");
	ofileImpC.writeLine("BoapPacketHead\ttxhead;\n");
	ofileImpC.writeLine("BoapPacketHead\trxhead;\n");
	ofileImpC.writeLine("\n");
	ofileImpC.writeLine("olock.lock();\n");
	ofileImpC.writeLine(BString("if(err = connectService(oname)){\n"));
	ofileImpC.indentMore();
	ofileImpC.writeLine("olock.unlock();\n");
	ofileImpC.writeLine(BString("return err;\n"));
	ofileImpC.indentLess();
	ofileImpC.writeLine("}\n");
	
	ofileImpC.writeLine("\n");
	ofileImpC.writeLine(BString("txhead.type = BoapMagic | BoapTypeRpc;\n"));
	ofileImpC.writeLine(BString("txhead.service = oservice;\n"));
	ofileImpC.writeLine(BString("txhead.cmd = ") + ofuncNum + ";\n");
	ofileImpC.writeLine("otx.pushHead(txhead);\n");
	
	nl = &n->node(1)->nodes();
	for(nl->start(i); !nl->isEnd(i); nl->next(i)){
		if((nl->get(i)->node(0)->name() == "in") || (nl->get(i)->node(0)->name() == "inout"))
			pushPopVar(ofileImpC, 1, "otx.push", nl->get(i)->node(1), nl->get(i)->node(2)->name());
	}

	ofileImpC.writeLine("if(err = performCall(otx, orx)){\n");
	ofileImpC.indentMore();
	ofileImpC.writeLine("olock.unlock();\n");
	if(retType == "BError")
		ofileImpC.writeLine("return err;\n");
	else
		ofileImpC.writeLine("throw err;\n");
	ofileImpC.indentLess();
	ofileImpC.writeLine("}\n");
	
	ofileImpC.writeLine("orx.popHead(rxhead);\n");
	ofileImpC.writeLine("orx.pop(ret);\n");
	ofileImpC.writeLine("if((rxhead.type & 0xFF) == BoapTypeRpcReply){\n");
	ofileImpC.indentMore();
	nl = &n->node(1)->nodes();
	for(nl->start(i); !nl->isEnd(i); nl->next(i)){
		if((nl->get(i)->node(0)->name() == "out") || (nl->get(i)->node(0)->name() == "inout"))
			pushPopVar(ofileImpC, 0, "orx.pop", nl->get(i)->node(1), nl->get(i)->node(2)->name());
	}
	ofileImpC.indentLess();
	ofileImpC.writeLine("}\n");
	ofileImpC.writeLine("olock.unlock();\n");
	ofileImpC.writeLine("return ret;\n");
}

void GenBoap::produceImpC(Node* n){
	BString		s;
	BIter		i;
	BList<Node*>*	nl;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileImpC.writeLine(BString("namespace ") + omodule + " {\n");
		ofileImpC.indentMore();
		
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpC(n->nodes()[i]);
		}
		ofileImpC.indentLess();
		ofileImpC.writeLine("}\n");
		break;
	case Node::TINTERFACE:
		ointerface = n->name();
		ofuncNum = 16;
		ofileImpC.writeLine(ointerface + "::" + ointerface + "(BString name) : BoapClientObject(name){\n");
		ofileImpC.indentMore();
		ofileImpC.writeLine(BString("oapiVersion = ") + oapiVersion + ";\n");
		ofileImpC.indentLess();
		ofileImpC.writeLine("}\n");
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpC(n->nodes()[i]);
		}
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpC(n->nodes()[i]);
		}
		break;
	case Node::TFUNC:
		s = s + getTypeName(n->node(0)) + " ";
		s = s + ointerface + "::" + n->name() + "(";
		nl = &n->node(1)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			if(i != nl->begin())
				s = s + ", ";
			if(nl->get(i)->node(0)->name() == "in"){
				s = s + getTypeName(nl->get(i)->node(1)) + " ";
			}
			else if(nl->get(i)->node(0)->name() == "inref"){
				s = s + "const " + getTypeName(nl->get(i)->node(1)) + "& ";
			}
			else {
				s = s + getTypeName(nl->get(i)->node(1)) + "& ";
			}

			s = s + nl->get(i)->node(2)->name();
		}
		s = s + "){\n";
		ofileImpC.writeLine(s);
		ofileImpC.indentMore();
		produceFuncC(n);
		ofileImpC.indentLess();
		ofileImpC.writeLine("}\n\n");
		ofuncNum++;
		break;
	case Node::TAPIVERSION:
		oapiVersion = n->name().retInt();
		break;
	default:
		break;
	}
}

BString GenBoap::getTypeInitialiser(BString typeName){
	if(typeName == "BInt32"){
		return " = 0";
	}
	else if(typeName == "BUInt32"){
		return " = 0";
	}
	else return "";
}

void GenBoap::produceFuncS(Node* n){
	BIter		i;
	BString		s;
	BString		sa;
	BList<Node*>*	nl;
	BString		typeName;
	BString		retType;
	
	retType = getTypeName(n->node(0));
	
	ofileImpS.writeLine("BError\terr;\n");
	ofileImpS.writeLine("BoapPacketHead\trxhead;\n");
	ofileImpS.writeLine("BoapPacketHead\ttxhead;\n");
	ofileImpS.writeLine(retType + "\tret;\n");
	nl = &n->node(1)->nodes();
	for(nl->start(i); !nl->isEnd(i); nl->next(i)){
		typeName = getTypeName(nl->get(i)->node(1));
		s = typeName + "\t";
		s = s + nl->get(i)->node(2)->name() + getTypeInitialiser(typeName) + ";\n";
		ofileImpS.writeLine(s);
	}
	ofileImpS.writeLine("\n");
	ofileImpS.writeLine("rx.popHead(rxhead);\n");
	
	nl = &n->node(1)->nodes();
	for(nl->start(i); !nl->isEnd(i); nl->next(i)){
		if((nl->get(i)->node(0)->name() == "in") || (nl->get(i)->node(0)->name() == "inout"))
			pushPopVar(ofileImpS, 0, "rx.pop", nl->get(i)->node(1), nl->get(i)->node(2)->name());
	}

	s = BString("ret = ") + n->name() + "(";

	if(ooptions.hasKey("connectionClass")){
		sa = sa + "(" + ooptions["connectionClass"] + "*)conn";
	}

	nl = &n->node(1)->nodes();
	for(nl->start(i); !nl->isEnd(i); nl->next(i)){
		if(sa.len())
			sa = sa + ", ";
		sa = sa + nl->get(i)->node(2)->name();
	}
	s = s + sa + ");\n";
	ofileImpS.writeLine(s);
	
	ofileImpS.writeLine("txhead.type = BoapMagic | BoapTypeRpcReply;\n");
	ofileImpS.writeLine("txhead.service = rxhead.service;\n");
	ofileImpS.writeLine("txhead.cmd = rxhead.cmd;\n");
	ofileImpS.writeLine("tx.pushHead(txhead);\n");
	ofileImpS.writeLine("tx.push(ret);\n");
	
	nl = &n->node(1)->nodes();
	for(nl->start(i); !nl->isEnd(i); nl->next(i)){
		if((nl->get(i)->node(0)->name() == "out") || (nl->get(i)->node(0)->name() == "inout"))
			pushPopVar(ofileImpS, 1, "tx.push", nl->get(i)->node(1), nl->get(i)->node(2)->name());
	}

	ofileImpS.writeLine("return err;\n");
}

void GenBoap::produceFuncAdd(Node* n){
	BString		s;
	BIter		i;

	switch(n->nodeType()){
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceFuncAdd(n->nodes()[i]);
		}
		break;
	case Node::TFUNC:
		s = s + "ofuncList.append(BoapFuncEntry(" + ofuncNum + ", ";
		s = s + "(BoapFunc)&" + ointerface + "Service::do" + n->name() + "));\n";
		ofileImpS.writeLine(s);
		ofuncNum++;
		break;
	default:
		break;
	}
}

void GenBoap::produceImpS(Node* n){
	BString		s;
	BIter		i;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileImpS.writeLine(BString("namespace ") + omodule + " {\n");
		ofileImpS.indentMore();
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpS(n->nodes()[i]);
		}
		ofileImpS.indentLess();
		ofileImpS.writeLine("}\n");
		break;
	case Node::TINTERFACE:
		ointerface = n->name();
		ofuncNum = 16;
		ofileImpS.writeLine(ointerface + "Service::" + ointerface + "Service(BoapServer& server, BString name) : BoapServiceObject(server, name){\n");
		ofileImpS.indentMore();
		ofileImpS.writeLine(BString("oapiVersion = ") + oapiVersion + ";\n");
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceFuncAdd(n->nodes()[i]);
		}
		ofileImpS.indentLess();
		ofileImpS.writeLine("}\n");		
		ofuncNum = 16;
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpS(n->nodes()[i]);
		}
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImpS(n->nodes()[i]);
		}
		break;
	case Node::TFUNC:
		s = "BError ";
		s = s + ointerface + "Service::do" + n->name();
		s = s + "(BoapServerConnection* conn, BoapPacket& rx, BoapPacket& tx){\n";
		ofileImpS.writeLine(s);
		ofileImpS.indentMore();
		produceFuncS(n);
		ofileImpS.indentLess();
		ofileImpS.writeLine("}\n\n");
		ofuncNum++;
		break;
	case Node::TAPIVERSION:
		oapiVersion = n->name().retInt();
		break;
	default:
		break;
	}
}

BError GenBoap::produceHeaderIntC(){
	BError	err;
	BString	fileName = ofileName + "C.h";
	BString	fileNameUpper = ofileName + "C_H";

	fileNameUpper.toUpper();

	ofileIntC.printf("/*******************************************************************************\n");
	ofileIntC.printf(" *\t%s\tProduced by Bidl\n", fileName.retStr());
	ofileIntC.printf(" *******************************************************************************\n");
	ofileIntC.printf(" */\n");
	ofileIntC.printf("\n");
	ofileIntC.printf("#ifndef %s\n", fileNameUpper.retStr());
	ofileIntC.printf("#define %s 1\n\n", fileNameUpper.retStr());

	ofileIntC.printf("#include <stdlib.h>\n");
	ofileIntC.printf("#include <stdint.h>\n");
	ofileIntC.printf("#include <Boap.h>\n");
	ofileIntC.printf("#include <BString.h>\n");
	ofileIntC.printf("#include <BList.h>\n");
	ofileIntC.printf("#include <BArray.h>\n");
#ifdef ZAP1
	ofileIntC.printf("#include <BObjDate.h>\n");
	ofileIntC.printf("#include <BObjData.h>\n");
#endif
#if !DODATA
	ofileIntC.printf("#include <%s>\n\n", (ofileName + "D.h").retStr());
#endif
	ofileIntC.printf("\n");
	return err;
}

BError GenBoap::produceHeaderIntS(){
	BError	err;
	BString	fileName = ofileName + "S.h";
	BString	fileNameUpper = ofileName + "S_H";

	fileNameUpper.toUpper();

	ofileIntS.printf("/*******************************************************************************\n");
	ofileIntS.printf(" *\t%s\tProduced by Bidl\n", fileName.retStr());
	ofileIntS.printf(" *******************************************************************************\n");
	ofileIntS.printf(" */\n");
	ofileIntS.printf("\n");
	ofileIntS.printf("#ifndef %s\n", fileNameUpper.retStr());
	ofileIntS.printf("#define %s 1\n\n", fileNameUpper.retStr());

	ofileIntS.printf("#include <stdint.h>\n");
	ofileIntS.printf("#include <Boap.h>\n");
	ofileIntS.printf("#include <BString.h>\n\n");
	ofileIntS.printf("#include <%sC.h>\n", ofileName.retStr());
	ofileIntS.printf("\n");

	if(ooptions.hasKey("connectionClass")){
		ofileIntS.printf("class %s;\n\n", ooptions["connectionClass"].retStr());
	}

	return err;
}

BError GenBoap::produceHeaderIntT(){
	BError	err;
	BString	fileName = ofileName + "T.h";
	BString	fileNameUpper = ofileName + "T_H";

	fileNameUpper.toUpper();

	ofileIntT.printf("/*******************************************************************************\n");
	ofileIntT.printf(" *\t%s\tProduced by Bidl\n", fileName.retStr());
	ofileIntT.printf(" *******************************************************************************\n");
	ofileIntT.printf(" */\n");
	ofileIntT.printf("\n");
	ofileIntT.printf("#ifndef %s\n", fileNameUpper.retStr());
	ofileIntT.printf("#define %s 1\n\n", fileNameUpper.retStr());

	ofileIntT.printf("#include <BString.h>\n");
	ofileIntT.printf("#include <BList.h>\n");
	ofileIntT.printf("#include <BArray.h>\n");
	ofileIntT.printf("#include <%s>\n", (ofileName + "S.h").retStr());
	ofileIntT.printf("\n");

	if(ooptions.hasKey("serverControl")){
		ofileIntT.printf("class Control;\n\n");
	}
	
	return err;
}

BError GenBoap::produceHeaderImpT(){
	BError	err;
	BString	fileName = ofileName + "T.cc";
	BString	fileNameUpper = ofileName + "T_H";

	fileNameUpper.toUpper();

	ofileImpT.printf("/*******************************************************************************\n");
	ofileImpT.printf(" *\t%s\tProduced by Bidl\n", fileName.retStr());
	ofileImpT.printf(" *******************************************************************************\n");
	ofileImpT.printf(" */\n");
	ofileImpT.printf("\n");

	ofileImpT.printf("#include <stdlib.h>\n");
	ofileImpT.printf("#include <stdint.h>\n");
	ofileImpT.printf("#include <%s>\n", (ofileName + "T.h").retStr());
	if(ooptions.hasKey("serverControl")){
		ofileImpT.printf("#include <Control.h>\n");
	}
	ofileImpT.printf("\n");
	return err;
}

BError GenBoap::produceTrailerInt(){
	BError	err;
	
	ofileIntC.printf("#endif\n");
	ofileIntS.printf("#endif\n");
	ofileIntT.printf("#endif\n");
	return err;
}

BError GenBoap::produceHeaderImpC(){
	BError	err;

	ofileImpC.printf("/*******************************************************************************\n");
	ofileImpC.printf(" *\t%s\tProduced by Bidl\n", (ofileName + ".cc").retStr());
	ofileImpC.printf(" *******************************************************************************\n");
	ofileImpC.printf(" */\n");
	ofileImpC.printf("\n");
	ofileImpC.printf("#include <%s>\n", (ofileName + "C.h").retStr());
	ofileImpC.printf("\n");
	return err;
}

BError GenBoap::produceHeaderImpS(){
	BError	err;

	ofileImpS.printf("/*******************************************************************************\n");
	ofileImpS.printf(" *\t%s\tProduced by Bidl\n", (ofileName + ".cc").retStr());
	ofileImpS.printf(" *******************************************************************************\n");
	ofileImpS.printf(" */\n");
	ofileImpS.printf("\n");
	ofileImpS.printf("#include <%s>\n", (ofileName + "C.h").retStr());
	ofileImpS.printf("#include <%s>\n", (ofileName + "S.h").retStr());
	ofileImpS.printf("\n");
	
	return err;
}

BString GenBoap::getTypeSwapList(Node* n){
	BString	typeName;
	Type*	t;
	BIter	i;
	BString	typeList;

	typeName = n->name();
//	printf("isRawType: %d %s\n", n->nodeType(), typeName.retStr());

	if(n->nodeType() == Node::TTYPEARRAY){
		return "";
	}
	else if(n->nodeType() == Node::TTYPEDICT){
		return "";
	}
	else if(n->nodeType() == Node::TTYPE){
		if(typeName == "Int8")		return "1";
		else if(typeName == "UInt8")	return "1";
		else if(typeName == "Int16")	return "2";
		else if(typeName == "UInt16")	return "2";
		else if(typeName == "Int32")	return "4";
		else if(typeName == "UInt32")	return "4";
		else if(typeName == "Bool")	return "4";
		else if(typeName == "String")	return "";
		else if(typeName == "Error")	return "";
		else if(typeName == "Date")	return "";
		else if(typeName == "DateTime")	return "";
		else if(typeName == "Id")	return "";
		else if(typeName == "ObjData")	return "";
		else if(typeName == "TimeStamp")	return "";
		else if(typeName == "Complex")	return "";
		else {
			if(t = gtypelist.search(typeName)){
				if(t->node()){
					return  getTypeSwapList(t->node());
				}
			}
			else
				return "";
		}
	}
	else if(n->nodes().number()){
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			typeList += getTypeSwapList(n->nodes()[i]);
		}

		return typeList;
	}
	return "";
}



GenBoap::GenBoap(){
	ofuncNum = 0;
	opushPopDepth = 0;
	oapiVersion = 0;
	
	setUseBObjects(0);
}

GenBoap::~GenBoap(){
}

BError GenBoap::produce(Node* n, BString fileName){
	BError	err;
	
//	dprintf("GenBoap::produce: %x\n", n);
	ofileName = fileName;
	ofileIntC.open(fileName + "C.h", "w");
	ofileIntS.open(fileName + "S.h", "w");
	ofileImpC.open(fileName + "C.cc", "w");
	ofileImpS.open(fileName + "S.cc", "w");
	ofileIntT.open(fileName + "T.h", "w");
	ofileImpT.open(fileName + "T.cc", "w");
	
	// File Headers
	produceHeaderIntC();
	produceHeaderIntS();
	produceHeaderImpC();
	produceHeaderImpS();
	produceHeaderIntT();
	produceHeaderImpT();
	
	// Generate code
	produceIntC(n);
	produceIntS(n);
	produceImpC(n);
	produceImpS(n);
	
	// Generate Base 
	produceIntT(n);
	produceImpT(n);

	// Generate trailers
	produceTrailerInt();
	ofileImpS.close();
	ofileImpC.close();
	ofileIntS.close();
	ofileIntC.close();
	ofileIntT.close();
	ofileImpT.close();
	return err;
}

