#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include <fstream.h>
#include <strstream.h>
#include <ctype.h>
#define MAX_INCLUDES	20
#define MAX_PROTOTYPES	20
#define FOR_CXX	0
#define FOR_STK	1

typedef char *charptr;
typedef ostream *ostreamptr;
typedef char *charptrarray[MAX_INCLUDES];

static char *CompoundName=0;
static char *TypedefName=0;
static char *TypdefObjName=0;

static ostream *out;

void genHeaderComment(int argc, char *argv[])
{
	char *s;
	ostrstream oss;
	int i;
	oss << argv[0] << " ";
	for (i=1; i<argc; i++)
		oss << "\"" << argv[i] << "\" ";
	oss << ends;
	char *buffer = oss.str();
	*out << "//" << buffer << endl;
	delete buffer;
}
char *newString(char *src)
{
	char *dest = new char[strlen(src)+1];
	strcpy(dest, src);
	return dest;
}
void genSTkHeader()
{
	//*out << "#ifdef STK_INTERFACE" << endl;
	*out << "#include <stk.h>" << endl;
	*out << "#include <LINK/stkWrapper/LINK_STk.h>" << endl;
	//*out << "#endif" << endl;
	*out << endl;
}

void genObjectInclude(char *full_include_file_path)
{
	*out << "#include <" << full_include_file_path <<  ">" << endl;
	*out << "" << endl;
}

void genIncludes(char *includes[], int incl_count)
{
	int i;
	for (i=0; i<incl_count; i++)
		genObjectInclude(includes[i]);
	*out << "" << endl;
}

void genTemplateIncludes(char *classname, 
			 char *temp_includes[], int temp_incl_count)
{
        int i;
	ostrstream oss;
	oss << "DEFINE_" << classname << "_TEMPLATE" << endl << ends;
	char *define_name = oss.str();
	*out << endl << "#define " << define_name << endl;
	genIncludes(temp_includes, temp_incl_count);
	*out << "#undef " << define_name << endl;
	*out << endl;
}

void genElementOpsPrototype(char *typedef_obj_name)
{
	*out << endl;
	*out << endl;
	*out << "//*******************************************************"
		"**************" << endl;
	*out << "int ElementOps<"
			<< typedef_obj_name <<">::compareItems(const "
			<< typedef_obj_name  << "& e1, const "
			<< typedef_obj_name  << "& e2); " << endl;
	*out << "ostream& ElementOps<"
			<< typedef_obj_name  << ">::displayItem(ostream& os, "
			"const " << typedef_obj_name  << "& e);" << endl;
	*out << endl;
}

void genElementOpsOverload(char *typedef_obj_name)
{
	*out << endl;
	*out << endl;
	*out << "//*******************************************************"
		"**************" << endl;
	*out << "int ElementOps<"
			<< typedef_obj_name <<">::compareItems(const "
			<< typedef_obj_name  << "& e1, const "
			<< typedef_obj_name  << "& e2) " << endl;
	*out << "{" << endl;
	*out << "\tif (e1 && e2)  {" << endl;
	*out << "\t\tif (*e1 == *e2)" << endl;
	*out << "\t\t\treturn 0;" << endl;
	*out << "\t\telse" << endl;
	*out << "\t\t\treturn (*e1<*e2)? -1 : 1;" << endl;
	*out << "\t} else {" << endl;
	*out << "\t\tif (e1 == e2)" << endl;
	*out << "\t\t\treturn 0;" << endl;
	*out << "\t\telse" << endl;
	*out << "\t\t\treturn (e1<e2)? -1 : 1;" << endl;
	*out << "\t}" << endl;
	*out << "}" << endl;
	*out << endl;
	*out << "ostream& ElementOps<"
			<< typedef_obj_name  << ">::displayItem(ostream& os, "
			"const " << typedef_obj_name  << "& e)" << endl;
	*out << "{" << endl;
	*out << "\tif (e)" << endl;
	*out << "\t\tos << *e;" << endl;
	*out << "\telse" << endl;
	*out << "\t\tos << e;" << endl;
	*out << "\treturn os;" << endl;
	*out << "}" << endl;
	*out << "//*******************************************************"
		"**************" << endl;
	*out << "" << endl;
}
void genAttributeTemplates()
{
	*out << endl;
	*out << endl;
//
// This instantiation of AttributeElementOps must be manual for now.
// The specialization of a method must occur in the same compilation 
// unit in which the instantiation occurs.  This was doable automatically
// with ElementOps since the specialization could be easily automated.  Not so
// with AttributeElementOps.  Specializations of the latter's methods are
// found in src/graph/graphTemplate.cpp, so that is where the instantiation
// must be for now.
//
/*
	*out << "//*******************************************************"
		"**************" << endl;
	*out << "//**Note: the displayItem() member of the following    **"
		"**************" << endl;
	*out << "//**      class may be specialized in graphTemplate.cpp **"
		"**************" << endl;
	*out << "//*******************************************************"
		"**************" << endl;
	//*out << "#ifdef __GNUC__" << endl;
	*out << "template class AttributeElementOps<"
				<< TypdefObjName << ">;" << endl;
	//*out << "#endif" << endl;
	*out << endl;
*/
}
void genAttributeFuncs(char *typedef_obj_name)
{
  //*out << "#ifdef __GNUC__" << endl;

	*out << "template int newAttribute(Graph*, String, " << 
				typedef_obj_name << ");" << endl;
	*out << "template int getAttribute(GraphObject*, String, " << 
				typedef_obj_name << "&);" << endl;
	*out << "template int setAttribute(GraphObject*, String, " << 
				typedef_obj_name << ");" << endl;

//	*out << "template int newAttribute(Graph*, String, const " << 
//				typedef_obj_name << "&);" << endl;
//	*out << "template int getAttribute(GraphObject*, String, " << 
//				typedef_obj_name << "&);" << endl;
//	*out << "template int setAttribute(GraphObject*, String, const " << 
//				typedef_obj_name << "&);" << endl;

        //****REPLACE above with below in once static_flag logic is working
        //*out << "template int newAttribute(Graph*, String, " <<
        //                      typedef_obj_name << "&, int);" << endl;
        //*out << "template int getAttribute(GraphObject*, String, " <<
        //                      typedef_obj_name << "&);" << endl;
        //*out << "template int setAttribute(GraphObject*, String, " <<
        //                      typedef_obj_name << "&, int);" << endl;
	//*out << "#endif" << endl;
}
void genOverloadAttributeDestructor()
{
	*out << endl;
	*out << "Attribute<" <<  TypdefObjName << " >::~Attribute()" << endl;
	*out <<	"{" << endl;
        *out << "	cout << \"special ~Attr\" << endl;" << endl;
        *out << "	cout << _name << endl;" << endl;
	*out << "	if (_graph) {" << endl;
	*out << "		if (strncmp(_name, \"x_\", 2) != 0)"<<endl;
        *out << "			resetAttribute(_graph, _name);" << endl;
        *out << "	_graph->removeAttr(this);" << endl;
    	*out << "	}" << endl;
    	*out << "	delete _name;" << endl;
    	*out << "	if (_static_flag == LINK_DYNAMIC) {" << endl;
        *out << "		cout << \"deleted\" << endl;" << endl;
        *out << "		delete _value;" << endl;
	*out <<	"	}" << endl;
	*out <<	"}" << endl;
}
char *rmSpaces(char *s)
{
	char *result = new char[strlen(s)+1], *d = result;
	while (*s) {
		if (!(*s == ' '))
			*d++ = *s++;
		else 
			s++;
	}
	*d = '\0';
	return result;
}
int calcTemplateNesting(char *s)
{
	int result = 0; 
	while (*s) 
		if ((*s++) == '<')
			result++;
	return result;
}
char *validCname(char *s, int flag, int& obj_is_ptr)
{
	char *result, *d; 
	char c = (flag==FOR_CXX) ? '_' : '-';
	char c2 = 'p';

	int i;
	obj_is_ptr=0;
	if (s) {
		for (i=strlen(s)-1; i>0 && s[i]==' '; i--);
		obj_is_ptr = (s[i] == '*');
	}
	if (flag==FOR_CXX) {
 		result = new char[strlen(s)+2];
		d = result;
		*d++ = c;
	} else {
 		result = new char[strlen(s)+1];
		d = result;
	}
	while (*s) {
		if (isalnum(*s))
			*d++ = *s;
		else if ((flag==FOR_CXX) && (*s == '*'))
			*d++ = c2;
		else if ((*s!=' ') && !((*s == '*') && (flag==FOR_STK)))
			*d++ = c;
		s++;
	}
	*d = '\0';
	if (flag == FOR_STK) {
		int j = strlen(result);	   // this to avoid List-List-int---
		while ( (--j>=0) && (result[j] == c))
			result[j] = '\0';
	}
	return result;
}
int genTypedefs(char *holder_classname, char *obj_name, char *obj_name2, 
	        int& template_nesting_depth,
		charptr& typedef_obj_name)
{
	int not_used, obj_is_ptr;
	if (!holder_classname || !obj_name) {
		cerr << "genTypedefs: must be instantiating a template class"
		     << endl;
		exit(2);
	}
	ostrstream oss2;
	oss2 << holder_classname << "<" << obj_name;
	if (obj_name2 != 0)
		oss2 << ", " << obj_name2 << " ";
	oss2 << " >";
	oss2 << ends;
	CompoundName = oss2.str();

	template_nesting_depth = calcTemplateNesting(CompoundName);
	int holder_is_ptr;
	TypedefName = validCname(CompoundName, FOR_CXX, not_used);
	typedef_obj_name = validCname(obj_name, FOR_CXX,obj_is_ptr);
	*out << "typedef " <<obj_name << " " << typedef_obj_name<< ";" << endl;
	*out << "typedef " << CompoundName << " "
					<< TypedefName << ";" << endl;
	return obj_is_ptr;
}

void genRegisterStaticInits(char *typedef_obj_name)
{
	*out << "Tcl_HashTable Register<"
			<< typedef_obj_name << ">::objTable;" << endl;
	*out << endl;
}

void genWrapperStaticInits(char *obj_name, char *typedef_obj_name)
{
	char *stk_name = rmSpaces(obj_name);
	*out << "char *Wrapper<"
			<< typedef_obj_name << ">::name = \""
					<< "<" << stk_name << ">" 
						<< "\";" << endl; 
	delete stk_name;
	*out << "int   Wrapper<"
			<< typedef_obj_name << ">::type = 0;" << endl;
	*out << "" << endl;
}

void genTemplate(char *hname, int gen_node_type, int obj_is_ptr,
		 char *obj_name, char *typedef_obj_name)
{
	if (strcmp(hname, "ElementOps")==0 && obj_is_ptr)
		genElementOpsPrototype(typedef_obj_name);
	if (hname)
		*out << "template class " << CompoundName << ";" << endl;
	if (gen_node_type)
		*out << "template class " << hname << "Node" 
				<< "<" << typedef_obj_name << ">;" << endl;
	*out << endl;
	if (strcmp(hname, "ElementOps")==0 && obj_is_ptr)
		genElementOpsOverload(typedef_obj_name);
	else if (strcmp(hname, "Attribute")==0)
		genAttributeFuncs(typedef_obj_name);
	else if (strcmp(hname, "Register")==0)
		genRegisterStaticInits(typedef_obj_name);
	else if (strcmp(hname, "Wrapper")==0)
		genWrapperStaticInits(obj_name, typedef_obj_name);
	//if (gen_attribute_funcs && obj_is_ptr)  for future deletion of
	//	genOverloadAttributeDestructor(); currently leaked attributes
}

void genPrototypes(char *prototypes[], int prototype_count)
{
	int i;
	for (i=0; i<prototype_count; i++)
		*out << prototypes[i] << endl;
}

void genOutputOperator(char *full_name)
{
	*out << "template ostream& operator<<(ostream&, const ";
	*out << full_name << "&);" << endl;
}

ostream *openFile(int gen_stk_templates, char *file_root, char *dir_name=0)
{
	char *link_path;
	char *file_path;
	ostream *out;
	if (dir_name) {
		if ((link_path=getenv("LINK_BASE")) == 0) {
			cerr << "error: LINK_BASE environment variable "
				"must be set" << endl;
			exit(2);
		}
		ostrstream oss;
		if (gen_stk_templates)
		   oss << link_path<<"/src/"<<dir_name<<"/STkTemplates/";
		else 
		   oss << link_path<<"/src/"<<dir_name << "/linkTemplates/";
		oss << file_root << "Template.cpp" << ends;
	
		file_path = oss.str();
		out = new ofstream(file_path, ios::out);
		delete file_path;
	} else
		out = new ofstream(file_root, ios::out);
	if (!(*out)) {
		cerr << "error: cannot open "<< file_path <<" for output"<<endl;
		exit(1);
	}
	return out;
}

void genWrapperFile(char *holder_classname, char *obj_name, char *obj_name2,
		    int gen_node_type,
		    char *temp_includes[], char *nontemp_includes[],
		    char *prototypes[],
		    int temp_incl_count, int nontemp_incl_count, int prototype_count,
		    int gen_output_op,
		    int &template_nesting_depth)
{
	char *typedef_obj_name;
	genTemplateIncludes(holder_classname, temp_includes, temp_incl_count);
	genIncludes(nontemp_includes, nontemp_incl_count);
	genPrototypes(prototypes, prototype_count);

	int is_ptr = genTypedefs(holder_classname, obj_name, obj_name2, 
				 template_nesting_depth,
				 typedef_obj_name);
	genTemplate(holder_classname, gen_node_type, is_ptr,
			    obj_name, typedef_obj_name);
	if (gen_output_op)
		genOutputOperator(CompoundName);
}

void processArgs(int argc, 
		 char *argv[], 
		 charptr& holder_classname,
		 int &gen_wrapper_class,
		 int &gen_node_type,
		 charptr& obj_name,
		 charptr& obj_name2,
		 charptr& dir_name,
		 charptr& file_name,
		 int &gen_output_op,
		 int &gen_stk_templates,
		 int &temp_incl_count,
		 int &nontemp_incl_count,
		 int &prototype_count,
		 charptrarray& template_includes,
		 charptrarray& nontemp_includes,
		 charptrarray& prototypes)
{
	char *s;
	while(--argc > 0 && (*++argv)[0] == '-') {
	   	s=argv[0]+1;
		switch(*s) {
			case 'c' :  
				--argc;
				holder_classname = newString((++argv)[0]);
				break;
			case 's' :  
				gen_stk_templates = 1;
				break;
			case 'd' :  
				--argc;
				dir_name = newString((++argv)[0]);
				break;
			case 't' :  
				--argc;
                                if (*(s+1) == '2') 
					obj_name2 = newString((++argv)[0]);
				else
					obj_name = newString((++argv)[0]);
				break;
                        case 'o' :
                                --argc;
                                file_name = newString((++argv)[0]);
                                out = new ofstream(file_name, ios::out);
                                break;
			case 'p' :  
				--argc;
				prototypes[prototype_count++] =
						newString((++argv)[0]);
				break;
			case 'i' :  
				if (strcmp(s, "instantiate-node-class")==0)
					gen_node_type = 1;
				else 
				 if (strcmp(s,"instantiate-output-operator")==0)
					gen_output_op = 1;
				break;
			case 'I' :  
				--argc;
                                if (*(s+1) == 'I') {
					template_includes[temp_incl_count++] =
						newString((++argv)[0]);
					s++;
				} else
					nontemp_includes[nontemp_incl_count++] =
						newString((++argv)[0]);
				break;
			default:
				*out << "Illegal option: -" << *s << endl;
				exit(1);
		}
	}
}
void usageMsg(int argc, char *obj_name, char *dir_name, char *file_name)
{
	if ((argc <3) || !obj_name) {
	    cerr << "usage: instantiate -c <template class> " <<
		    "-t <first template parm> " << endl;
	    cerr << "	    [-t2 <second template parm>]" << endl;
	    cerr << "	    [-d <LINK directory> | -o <output file>]" << endl;
	    cerr << "	    [-stk-templates]" << endl;
	    cerr << "	    [-c <holder class name (e.g. List)>]  " << endl;
	    cerr << "	    [-instantiate-node-class]  " << endl;
	    cerr << "	    [-instantiate-output-operator]  " << endl;
	    cerr << "	    [-I <full include path of .h>]" << endl;
	    cerr << "	    [-p <full function prototype>]" << endl;
	    cerr << "	    [-II <full include path of .h for template"
			    " which needs instantiation >]" << endl;
	    cerr << "ex: instantiate -t \"Vertex*\" -c List " 
		 << "-instantiate-node-class "
		    "-I \"LINK/graph/Vertex.h\" -II \"LINK/basic/List.h\""
		<< endl;
	    exit(1);
	}
	if (dir_name && file_name) {
	    cerr << "error: -d and -o options may not be used together"<<endl;
	    exit(3);
	}
}

main(int argc, char *argv[])
{
	char *s;
	char *holder_classname;
	int gen_node_type=0, gen_output_op=0, gen_wrapper_class=0;
	int template_nesting_depth=0;
	char *dir_name=0, *obj_name=0, *obj_name2=0, *file_name=0;
	char *template_includes[MAX_INCLUDES];
	char *nontemp_includes[MAX_INCLUDES];
	char *prototypes[MAX_PROTOTYPES];
	int temp_incl_count=0;
	int nontemp_incl_count=0;
	int prototype_count=0;
	int gen_stk_templates=0;
	char **save_argv = argv;
	int save_argc = argc;
	
	dir_name=0;
	ostrstream the_whole_file;
	out = &the_whole_file;
	processArgs(argc, argv, holder_classname, gen_wrapper_class, 
		    gen_node_type, obj_name, obj_name2, 
		    dir_name, file_name, gen_output_op, 
		    gen_stk_templates,
		    temp_incl_count, nontemp_incl_count, prototype_count,
		    template_includes, nontemp_includes, prototypes);
	usageMsg(argc, obj_name, dir_name, file_name);
	genHeaderComment(save_argc, save_argv);
	if (gen_stk_templates)
		genSTkHeader();
	genWrapperFile(holder_classname, obj_name,  obj_name2, gen_node_type,
			template_includes, nontemp_includes, prototypes,
			temp_incl_count,
			nontemp_incl_count, prototype_count, gen_output_op, 
			template_nesting_depth);
	the_whole_file << ends;
	char *file_string = the_whole_file.str();
	if (dir_name) {
		ostrstream oss;
		if (template_nesting_depth > 0) oss << "_";
		int i;
		for (i=0; i<template_nesting_depth; i++) 
			oss << "z";
		oss << TypedefName << ends;
		char *fname = oss.str();
		out = openFile(gen_stk_templates, fname, dir_name);
		delete fname;
	} else if (file_name)
		out = openFile(gen_stk_templates, file_name);
	else
		out = &cout;
	*out << file_string << endl;
	if (dir_name || file_name) {
		//(*(ofstream*)out).close();
		//delete out;
	}
	if (CompoundName)    delete CompoundName;
	if (TypedefName)    delete TypedefName;
	if (holder_classname) delete holder_classname;
}
