/*******************************************************************************
 *	GenData.cc	BEAM Data output
 *			T.Barnaby,	BEAM Ltd,	12/9/03
 *******************************************************************************
 */
#define	DEBUG	1

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

void GenData::produceInt(Node* n){
	BIter		i;
	BString		s;
	BList<Node*>*	nl;
	BString		typeName;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileInt.writeLine(BString("namespace ") + omodule + " {\n");
		ofileInt.indentMore();
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceInt(n->nodes()[i]);
		}
		ofileInt.indentLess();
		ofileInt.writeLine("}\n");
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceInt(n->nodes()[i]);
		}
		break;
	case Node::TSTRUCT:
		typeName = BString("") + n->name();
		
		if((n->nodes().number() == 2) && n->node(1)){
			ofileInt.writeLine(BString("class ") + typeName + " : public " +  getTypeName(n->node(1), 1) + " {\n");
		}
		else {
			ofileInt.writeLine(BString("class ") + typeName + " {\n");
		}

		ofileInt.writeLine(BString("class ") + typeName + " {\n");
		ofileInt.writeLine("public:\n");
		ofileInt.indentMore();
		ofileInt.writeLine(typeName + "();\n");
		ofileInt.indentLess();
		ofileInt.writeLine("public:\n");
		ofileInt.indentMore();

		nl = &n->node(0)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			s = getTypeName(nl->get(i)->node(0), 1) + "\t";
			s = s + nl->get(i)->node(1)->name();
			ofileInt.writeLine(s + ";\n");
		}

		ofileInt.indentLess();
		ofileInt.writeLine("};\n\n");
		break;
	case Node::TENUM:
		s = "";

		if(n->node(0)){
			s = s + n->node(0)->name() + " ";
		}
		
		nl = &n->node(1)->nodes();
		for(nl->start(i); !nl->isEnd(i); nl->next(i)){
			if(s.len())
				s += ", ";
			s += nl->get(i)->name();
		}
		ofileInt.writeLine(BString("enum { " + s + "};\n");
		break;
	default:
		break;
	}
}

void GenData::produceImp(Node* n){
	BString		s;
	BIter		i;
	BList<Node*>*	nl;
	BString		typeName;
	BString		parentTypeName;

	switch(n->nodeType()){
	case Node::TMODULE:
		omodule = n->name();
		ofileImp.writeLine(BString("namespace ") + omodule + " {\n");
		ofileImp.indentMore();
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImp(n->nodes()[i]);
		}
		ofileImp.indentLess();
		ofileImp.writeLine("}\n");
		break;
	case Node::TLIST:
		for(n->nodes().start(i); !n->nodes().isEnd(i); n->nodes().next(i)){
			produceImp(n->nodes()[i]);
		}
		break;
	case Node::TTYPEDOMAIN:
		otypeDomain = atoi(n->name());
		break;
	case Node::TSTRUCT:
		typeName = BString("") + n->name();
		parentTypeName = "";
		if((n->nodes().number() == 2) && n->node(1)){
			parentTypeName = getTypeName(n->node(1), 1);
		}
		
		ofileImp.writeLine("\n");
		ofileImp.writeLine(BString("// ") + typeName + " Object Implementation\n");
		
	
		if((n->nodes().number() == 2) && n->node(1)){
			ofileImp.writeLine(typeName + "::" + typeName + "(){\n");
		}
		else {
			ofileImp.writeLine(typeName + "::" + typeName + "(){\n");
		}
		ofileImp.indentMore();
		ofileImp.indentLess();
		ofileImp.writeLine("}\n");
		
		otypeNum++;
		break;
	default:
		break;
	}
}

BError GenData::produceHeaderInt(){
	BError	err;
	BString	fileName = ofileName + "D.h";
	BString	fileNameUpper = ofileName + "D_H";

	fileNameUpper.toUpper();

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

	ofileInt.printf("#include <Boap.h>\n\n");

	if(getUseBObjects()){
		ofileInt.printf("#include <BObjData.h>\n");
		ofileInt.printf("#include <BObjInt.h>\n");
		ofileInt.printf("#include <BObjString.h>\n");
		ofileInt.printf("#include <BObjDate.h>\n");
		ofileInt.printf("#include <BObjDateTime.h>\n");
		ofileInt.printf("#include <BObjListVar.h>\n");
	}

	ofileInt.printf("\n");
	return err;
}
BError GenData::produceTrailerInt(){
	BError	err;
	
	ofileInt.printf("#endif\n");
	return err;
}

BError GenData::produceHeaderImp(){
	BError	err;

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

BError GenData::produceTrailerImp(){
	BError	err;
	
	// Produce Python Init
	ofileImp.writeLine("\n// Python module init\n");
	ofileImp.writeLine("#include <Python.h>\n");
	ofileImp.writeLine("\n");
	ofileImp.writeLine("static PyMethodDef methods[] = {\n");
	ofileImp.indentMore();
	ofileImp.writeLine("{NULL, NULL}\n");
	ofileImp.indentLess();
	ofileImp.writeLine("};\n");
	ofileImp.writeLine(BString("extern \"C\" void initlib") + omodule + "(){\n");
	ofileImp.indentMore();
	ofileImp.writeLine(BString("Py_InitModule(\"lib") + omodule + "\", methods);\n");
	ofileImp.indentLess();
	ofileImp.writeLine("}\n");
	return err;
}

GenData::GenData(){
	otypeDomain = 0;
	otypeNum = 1;
}

GenData::~GenData(){
}

BError GenData::produce(Node* n, BString fileName){
	BError	err;
	
//	dprintf("GenData::produce: %p\n", n);

	ofileName = fileName;
	ofileInt.open(ofileName + "D.h", "w");
	ofileImp.open(ofileName + "D.cc", "w");
	
	// File Headers
	produceHeaderInt();
	produceHeaderImp();
	
	// Generate code
	produceInt(n);
	produceImp(n);

	// Generate trailers
	produceTrailerInt();
	produceTrailerImp();

	ofileImp.close();
	ofileInt.close();

	return err;
}
