// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

///////////////////////////////////////////////////////////////////////
//                   HOLY SHIT THIS CODE IS UGLY
// --------------------------------------------------------------------
// Seriously. I'm not a terrible programmer, I swear. This wrapper
// generator, though, is hacky and ugly. The code is a mess and I'm
// pretty sure I'm the only one who can maintain this mess. It was
// intended to solve a specific problem, and it did. Please limit
// complaints to the quality of the generated code (for example the
// ugly dynamic cast tree it uses to figure out an object's type, or
// the fact that it looks like the same thing copy+pasted a zillion
// times.
///////////////////////////////////////////////////////////////////////

#include "../hacks/toolhacks.h"

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

#include "../filesystem/filesystem.h"
#include "../string/string.h"

FILE *in = NULL; // Main file input. Tokenizer reads from this.
bool moreTokens = true; // So we know when to quit parsing.
int lineNo = 1; // So we know where the errors are.
std::vector<std::string> fileList; // List of all files specified on the command line.
int currentFile = 0; // Index of the next file in the file list.

std::vector<std::string> storedComments; // Continuous comments we've come across.

// Just open the next file in fileList.
void openNextFile(void) {
	
	// Close the last file.
	if(in) fclose(in);
	
	if(currentFile == (int)fileList.size()) {
		// Reached the end of the list.
		in = NULL;
		return;
	}
	
	// Open the next one.
	in = fopen(fileList[currentFile].c_str(), "rb");
	
	if(!in) {
		
		// Woops.
		cout << "Couldn't open: " << fileList[currentFile] << endl;
		exit(1);
	}
	
	lineNo = 1;
	currentFile++;
}

// This is a quick and dirty function to grab the next token from the
// "in" global file handle. It ignores all whitespace and gives back
// either multi-character tokens like C-legal (and some illegal) names,
// or single characters for everything else it doesn't recognize. It
// also ignores C++-style comments (but not C-style comments).
std::string getNextToken(void) {
	string currentToken;
	
	int lastReadCharacter;
	
	bool doneToken = false;
	bool multichar = false;

	// If we got called again after the last EOF was found, it means
	// we're still expecting stuff. That would be an error.
	if(!moreTokens) {
		cout << "Unexpected end of file." << endl;
		exit(1);
	}

	lastReadCharacter = fgetc(in);
	
	// Skip whitespace.
	while(isWhiteSpace(lastReadCharacter)) {
		if(lastReadCharacter == '\n') lineNo++;
		lastReadCharacter = fgetc(in);
	}
	
	while(!doneToken) {
		
		if(lastReadCharacter == '\n') lineNo++;
		
		if(lastReadCharacter == EOF) {
			
			// Reached the end of this file. Load the next file.
			openNextFile();
			
			// No more files?
			if(!in) {
				moreTokens = false;
				doneToken = true;
				break;
			}
		}
		
		// C++-style comments check.
		if(lastReadCharacter == '/') {
			int peekNext = fgetc(in);
			if(peekNext != '/') {
				ungetc(peekNext, in);
			} else {
				// To make up for the slash we basically ate, add it
				// back to the token.
				currentToken = currentToken + '/';
				
				// Read until the end of the line.
				while(lastReadCharacter != '\n' && lastReadCharacter != EOF) {
					
					currentToken = currentToken + (char)lastReadCharacter;
					lastReadCharacter = fgetc(in);
					
					if(lastReadCharacter == '\n') lineNo++;
				}
				break;
			}
		}
		
		// Handle quotes.
		if(lastReadCharacter == '"' || lastReadCharacter == '\'') {
			currentToken = currentToken + (char)lastReadCharacter;
			
			// Keep the thing used to start the quotes.
			char startChar = lastReadCharacter;
			
			// skipCheck will tell us if we can skip checking for an
			// end-quote (ie: it was escaped).
			bool skipCheck = false;
			
			do {
				lastReadCharacter = fgetc(in);
				
				if(lastReadCharacter == '\\') {
					
					int escapedChar = fgetc(in);
					
					// Handle various escaped characters.
					switch(escapedChar) {
						case 'r':
							
							// Ummmmmmm... I'm going to just ignore
							// this one.
							
							//currentToken = currentToken + "\r";
							break;
						case 'n':
							currentToken = currentToken + "\n";
							break;
						case 't':
							currentToken = currentToken + "\t";
							break;
						case '\'':
						case '"':
							currentToken = currentToken + (char)escapedChar;
							break;
					}	
				}
				
				currentToken = currentToken + (char)lastReadCharacter;
				
			} while((skipCheck || lastReadCharacter != startChar) && lastReadCharacter != EOF);
			
			doneToken = true;
			break;
		}
		
		// Check for C/C++-legal names. Note: Also matches numbers.
		if(	(lastReadCharacter >= 'a' && lastReadCharacter <= 'z') || 
			(lastReadCharacter >= 'A' && lastReadCharacter <= 'Z') || 
			(lastReadCharacter >= '0' && lastReadCharacter <= '9') ||
			lastReadCharacter == '_') {
			
			// We're still in a multi-character token.
			multichar = true;
			
		} else {
			
			// Didn't get a multi-character-token legal character. So
			// we're at the end of a token, or never even started a
			// multi-character token.
			if(multichar) {
				
				// In multi-character. Put back the last token so we
				// can read it next time.
				ungetc(lastReadCharacter, in);
				
				// Decrement the line number back, too, or it'll end up
				// getting counted twice.
				if(lastReadCharacter == '\n') lineNo--;
				
			} else {
				
				// Not a multi-character token. Just save the character.
				currentToken = currentToken + (char)lastReadCharacter;
			}
			
			doneToken = true;
			break;
		}
		
		currentToken = currentToken + (char)lastReadCharacter;
		
		lastReadCharacter = fgetc(in);
	}
	
	return currentToken;
}

// A function that wraps getNextToken(), but skips over comments.
std::string getNextToken2(void) {
	string token = getNextToken();
	
	while(strStartsWith("//", token) && moreTokens) {
		// Got a comment. Send it to stdout and go to the next one.
		
		// Or just silently ignore it.
		//cout << token.c_str() + 2 << endl;
		
		// Store it.
		storedComments.push_back(token);
		
		token = getNextToken();
	}
	
	// Last token, and it was a comment.
	if(strStartsWith("//", token)) return "";
	
	return token;
}

// Convenience function to get some standard error information.
std::string fileInfo(void) {
	ostringstream str;
	str << fileList[currentFile - 1] << ":" << lineNo << ": ";
	return str.str();
}

// We use this error all over the place.
void badTokenError(const std::string &token) {
	cerr << fileInfo() << ": Bad token: " << token << endl;
	exit(1);
}

enum ParameterType {
	PARAM_NUMBERI,
	PARAM_NUMBERF,
	PARAM_STRING,
	PARAM_BOOL,
	PARAM_CLASS,
	
	PARAM_VOID,
};

struct FuncDef {
	std::string name;
	ParameterType returnType;
	std::vector<ParameterType> parameters;
	std::vector<std::string> parameterNames;
	
	std::vector<std::string> commentLines;
};

struct ClassDef {
	std::string name;
	std::vector<FuncDef*> functions;
	ClassDef *parent;
	
	std::vector<std::string> commentLines;
};

std::string classRoot;
std::map<std::string, ClassDef*> existingClasses;
std::vector<std::string> classNamesInOrder;

// Get a ParameterType from a string.
ParameterType strToParamType(const std::string &typeStr) {

	if(typeStr == "string") {
		
		return PARAM_STRING;
		
	} else if(typeStr == "int") {
		
		return PARAM_NUMBERI;
	
	} else if(typeStr == "float" || typeStr == "double") {
		
		return PARAM_NUMBERF;
		
	} else if(typeStr == "bool") {
		
		return PARAM_BOOL;
		
	} else if(typeStr == "void") {
		
		return PARAM_VOID;
		
	} else if(existingClasses[typeStr]) {
		
		return PARAM_CLASS;
		
	}
	
	cerr << fileInfo() << "Unknown type: " << typeStr << endl;
	exit(1);
}

std::string paramToLuaType(ParameterType paramType, const std::string &classType) {

	switch(paramType) {
		case PARAM_STRING:
			return "string";
		case PARAM_CLASS:
			return classType;
		case PARAM_NUMBERF:
		case PARAM_NUMBERI:
			return "number";
		case PARAM_BOOL:
			return "boolean";
		case PARAM_VOID:
			return "";
		default:
			return "???";
	}
}

string classGetterFunc;
string tableGetterFunc;

std::string getParameter(const std::string &paramName, ParameterType paramType, unsigned int paramNum, unsigned int totalParams) {
	ostringstream out;
	
	int stackPos = (int)paramNum - (int)totalParams;
	
	assert(paramType != PARAM_VOID);
	
	switch(paramType) {
		
		case PARAM_BOOL:
			out << "bool " << paramName << " = lua_toboolean(L, " << stackPos << ");" << endl;
			break;
			
		case PARAM_NUMBERF:
			out << "float " << paramName << " = lua_tonumber(L, " << stackPos << ");" << endl;
			break;

		case PARAM_NUMBERI:
			out << "int " << paramName << " = (int)lua_tonumber(L, " << stackPos << ");" << endl;
			break;

		case PARAM_STRING:
			out << "std::string " << paramName << ";" << endl;
			
			// FIXME: Only indents the first line, so the rest are indented manually.
			out << "  {" << endl;
			out << "    const char *_tmpCStr = lua_tostring(L, " << stackPos << ");" << endl;
			out << "    if(_tmpCStr) {" << endl;
			out << "      " << paramName << " = _tmpCStr;" << endl;
			out << "    }" << endl;
			out << "  }" << endl;
			break;
		
		case PARAM_VOID:
			// This shouldn't ever happen.
			break;
		
		case PARAM_CLASS:
			out << classRoot << " *" << paramName << " = " << classGetterFunc << "(L, " << stackPos << ");" << endl;
			break;
	}
	
	return out.str();
}

std::string indent(int x) {
	string indent = "";
	while(x--) {
		indent = indent + "  ";
	}
	return indent;
}

// Spew out the code to get a string name for a class type.
std::string uglyDynamicCastTree(const std::string &currentType, int indentLevel) {
	ostringstream out;
	
	ClassDef *rootClassDef = existingClasses[currentType];
	
	// FIXME: This will scale very badly with a large number of classes.
	for(unsigned int i = 0; i < classNamesInOrder.size(); i++) {
		string className = classNamesInOrder[i];
		ClassDef *classDef = existingClasses[className];
	
		if(classDef->parent == rootClassDef) {
			
			// Found a child.
			
			out << indent(indentLevel) << "if(dynamic_cast<" << className << "*>(p)) {" << endl;
			out << uglyDynamicCastTree(className, indentLevel + 1);
			out << indent(indentLevel) << "}" << endl;
		}
	}
	
	out << indent(indentLevel) << "return \"" << currentType << "\";" << endl;
	
	return out.str();
	
}

std::string noDocError(const std::string &name) {
	cerr << "Warning: No documentation for " << name << endl;
	return string("FIXME: ") + name + " is undocumented";
}

std::string genDocStringFromComments(const std::string &name, std::vector<std::string> &commentLines, const std::string paragraphSplit) {
	
	// No comments preceding the function at all - no documentation.
	if(!commentLines.size()) {
		return noDocError(name);
	}
	
	// Search backwards from the end for the first comment that's
	// doesn't start with "///".
	int lastIndex = (int)commentLines.size() - 1;
	while(lastIndex >= 0 && strStartsWith("///", commentLines[lastIndex])) {
		lastIndex--;
	}
	lastIndex++;

	// We're right back to the end - no documentation.
	if(lastIndex == (int)commentLines.size()) {
		return noDocError(name);
	}
	
	ostringstream ret;
	while(lastIndex != (int)commentLines.size()) {
		
		// Figure out if this line is all whitespace or not.
		bool allWhiteSpace = true;
		for(unsigned int i = 3; i < commentLines[lastIndex].size(); i++) {
			if(!isWhiteSpace(commentLines[lastIndex][i])) {
				// Non-whitespace found.
				allWhiteSpace = false;
			}
		}		
		
		if(allWhiteSpace) {
			
			// This line is all whitespace, so we'll treat it as a
			// paragraph break.
			ret << paragraphSplit;
			
		} else {
			
			// Add the line, with a space and without the "///".
			// Whitespace doesn't really matter because it's being output
			// to HTML.
			ret << " " << commentLines[lastIndex].c_str() + 3;
		}
		
		lastIndex++;
	}
	
	return ret.str();
	
}

// This dumps children in a hierarchal HTML unordered list.
string dumpChildrenFor(const std::string &className, vector<string> &classNamesInOrder, map<string, ClassDef*> &existingClasses) {

	bool dumpStarted = false;
	ostringstream outStr;

	ClassDef *mainClassDef = existingClasses[className];
	outStr << "<li><a href=\"#" << className << "\">" << className << "</a>";

	for(unsigned int i = 0; i < classNamesInOrder.size(); i++) {
		string childClassName = classNamesInOrder[i];
		ClassDef *classDef = existingClasses[childClassName];

		if(classDef->parent == mainClassDef) {
			// Found a child.
			
			if(!dumpStarted) {
				outStr << "<ul>";
				dumpStarted = true;
			}
			
			outStr << dumpChildrenFor(childClassName, classNamesInOrder, existingClasses);
		}
		
	}
	
	if(dumpStarted) {
		outStr << "</ul>";
	}
	
	outStr << "</li>";
	
	return outStr.str();
}


int main(int argc, char *argv[]) {

	if(argc < 2) {
		cerr << "Usage: " << argv[0] << " <filename> [filename] ..." << endl;
		return 1;
	}
	
	for(int i = 1; i < argc; i++) {
		fileList.push_back(argv[i]);
	}

	openNextFile();

	string inToken;
	cout << "/*" << endl;
	while(moreTokens) {
		
		// Find a class.
		inToken = getNextToken2();
		if(inToken == "LUA_WRAP") {
			
			// Save the comments.
			std::vector<std::string> classCommentLines = storedComments;
			storedComments.clear();
			
			inToken = getNextToken2();
			
			while(inToken == ";") {
				// Eat extra ";" characters.
				inToken = getNextToken2();
			}

			if(!moreTokens) {
				// Done after eating ";" and whitespace.
				break;
			}

			if(inToken != "class") {
				badTokenError(inToken);
			}
			
			// Found a class.
			ClassDef *newClass = new ClassDef();
			
			// Keep class comments.
			newClass->commentLines = classCommentLines;
			
			// Get the class name.
			string className = getNextToken2();
			if(existingClasses[className]) {
				cerr << fileInfo() << "Class " << className << " redefinition" << endl;
				exit(1);
			}
			newClass->name = className;
			
			cout << "Generating code for " << className << "..." << endl;

			existingClasses[className] = newClass;
			classNamesInOrder.push_back(className);

			string curlyOrColon = getNextToken2();
			if(curlyOrColon != ":" && curlyOrColon != "{") {
				badTokenError(curlyOrColon);
			}
			
			// Handle class parent.
			if(curlyOrColon == ":") {
			
				// Get the class that this is derived from.
				string classParent = getNextToken2();
				
				// Skip inheritance type.
				if(classParent == "public" || classParent == "private" || classParent == "protected") {
					classParent = getNextToken2();
				}
				
				if(!existingClasses[classParent]) {
					cerr << fileInfo() << "No class " << classParent << " defined yet" << endl;
					exit(1);
				}
				
				newClass->parent = existingClasses[classParent];
				
				curlyOrColon = getNextToken2();
			
			} else {
				
				if(classRoot.size()) {
					cerr << fileInfo() << "Multiple class hierarchy roots: " << classRoot << " and " << newClass->name << endl;
					exit(1);
				}
				classRoot = newClass->name;
				newClass->parent = NULL;
				
			}
			
			if(curlyOrColon == "{") {
				
				// First, let's just add the constructor function here,
				// since that's a weird special case.
				
				// TODO: Add a way to explicitly enable or disable this.
				// TODO: Add a way to do different constructors.
				FuncDef *newFunc = new FuncDef();
				newFunc->name = "new";
				newFunc->returnType = PARAM_CLASS;
				newClass->functions.push_back(newFunc);
				
				// Another special case for destructors.
				newFunc = new FuncDef();
				newFunc->name = "delete";
				newFunc->returnType = PARAM_VOID;
				newClass->functions.push_back(newFunc);
				
				// Now handle each function...
				std::string typeStr = getNextToken2();
				
				while(typeStr != "}") {
					
					if(typeStr == "LUA_WRAP") {
						
						// Save the comments for documentation.
						std::vector<std::string> funcCommentLines = storedComments;
						storedComments.clear();
						
						// Found a function to wrap.
						
						typeStr = getNextToken2();
						while(typeStr == "virtual" || typeStr == "static" || typeStr == "std" || typeStr == "const") {
							if(typeStr == "std") {
								// Eat scope operator for this.
								typeStr = getNextToken2();
								typeStr = getNextToken2();
							}
							typeStr = getNextToken2();
						}
						
						FuncDef *newFunc = new FuncDef();
						
						newFunc->commentLines = funcCommentLines;
						
						// Figure out the type of the function.
						newFunc->returnType = strToParamType(typeStr);
						
						// Get the function name.
						newFunc->name = getNextToken2();
						while(newFunc->name == "*" || newFunc->name == "&") {
							newFunc->name = getNextToken2();
						}
						
						cout << "  Adding function " << newFunc->name << "..." << endl;
						
						std::string junk = getNextToken2();
						if(junk != "(") {
							badTokenError(junk);
						}
						
						string paramType = getNextToken2();
						while(paramType != ")") {
							
							// Uh.... what else can go in here again?
							// We don't care about any of this.
							while(paramType == "const" || paramType == "explicit" || paramType == "std") {
								
								// Quick hack to handle explicit namespace.
								if(paramType == "std") {
									// Eat the scope operator.
									paramType = getNextToken2();
									paramType = getNextToken2();
								}
								
								paramType = getNextToken2();
							}
							
							ParameterType realParamType = strToParamType(paramType);
							
							if(realParamType != PARAM_VOID) {
								newFunc->parameters.push_back(realParamType);
								cout << "    Param: " << paramType << endl;
							}
							
							string paramName = getNextToken2();
							while(paramName == "*" || paramName == "&") {
								// Eat all pointer and reference stuff.
								// All of it is ignored and assumes that...
								//   All class types are pointers.
								//   All basic data types (int, float, bool)
								//     are values.
								//   All strings are C++ and either passed
								//     by value or by reference.
								paramName = getNextToken2();
							}
							
							if(paramName != "," && paramName != ")") {
								
								// Ate the parameter name. Now really get
								// the next token (or comma, or whatever).
								paramType = getNextToken2();
								
								newFunc->parameterNames.push_back(paramName);
								
							} else {
								
								// Just pass this through...
								paramType = paramName;
								
								newFunc->parameterNames.push_back("!unnamedParameter!");
							}
							
							if(paramType == ",") {
								// Eat a single comma.
								paramType = getNextToken2();
							}
						}
						
						std::string semiColon = getNextToken2();

						while(semiColon == "const") {
							// Eat a "const" that we don't care about,
							// if it's there.
							semiColon = getNextToken2();
						}
						
						if(semiColon != ";") {
							
							if(semiColon == "{") {
								
								// It's a function with curly braces.
								// Keep eating tokens until we even out
								// on the number of curly braces.
								int numCurlies = 1;
								string token;
								while(numCurlies > 0) {
									token = getNextToken2();
									if(token == "{") numCurlies++;
									if(token == "}") numCurlies--;
								}
								
							} else {
								
								badTokenError(semiColon);
							}
						}
						
						// Done. Add it to the list.
						newClass->functions.push_back(newFunc);
						
						typeStr = getNextToken2();
					
					} else {
						
						// Keep searching for the LUA_WRAP tag.
						typeStr = getNextToken2();
					}
				}
				
			}
			
		} else {
			
			storedComments.clear();
			
		}
	}
	
	cout << "*/" << endl;

	cout << endl << "// Begin auto-generated code..." << endl << endl;

	if(!classRoot.size()) {
		cerr << "No class hierarchy root found." << endl;
		exit(1);
	}

	// Done reading everything. Do output.
	ostream &out = cout;
	ofstream docOut;
	docOut.open(string(classRoot + ".html").c_str(), std::ios::out);
	
	out << "#include <map>" << endl;
	out << "#include <string>" << endl;
	out << "#include \"autogen.h\"" << endl << endl;

	out << "extern \"C\" {" << endl;
	out << "#include <lua.h>" << endl;
	out << "}" << endl << endl;

	for(unsigned int i = 0; i < fileList.size(); i++) {
		
		// Strip out most of the path. Just use the basename.
		string name = fileList[i];
		int lastSlash = -1;
		for(int j = 0; j < name.size(); j++) {
			if(name[j] == '/') {
				lastSlash = j;
			}
		}
		
		out << "#include \"" << (name.c_str() + (lastSlash + 1)) << "\"" << endl;
	}

	out << endl << endl;

	// First, we need a function to go from Lua tables to classes.
	classGetterFunc = "autoGenLuaWrapper_get" + classRoot + "FromTable";
	out << classRoot << " *" << classGetterFunc << "(lua_State *L, int index) {" << endl;
	out << "  if(!lua_istable(L, index)) return NULL;" << endl;
	out << "  lua_pushstring(L, \"uid\");" << endl;
	out << "  lua_gettable(L, index - 1);" << endl;
	out << "  const char *uid_cstr = lua_tostring(L, -1);" << endl;
	out << "  lua_pop(L, 1);" << endl;
	out << "  if(uid_cstr) {" << endl << endl;
	out << "    // !!! This needs to be defined externally. !!!" << endl;
	out << "    return get" << classRoot << "ByUID(uid_cstr);" << endl << endl;
	out << "  }" << endl;
	out << "  return NULL;" << endl;
	out << "}" << endl << endl;
	
	// RTTI may actually be the cleanest way to do this, but I still
	// feel like I shouldn't be doing it this way.
	//out << "// FIXME: RTTI Abuse: Mapping type info name() stuff to actual class names." << endl;
	//out << "static std::map<std::string, std::string> rttiFor" << classRoot << ";" << endl << endl;
	
	// Function to get the most defined name for a class. (Hacky as
	// hell!.)
	string nameGetterFunc = string("autoGenLuaWrapper_getClassNameFrom") + classRoot;
	out << "static std::string " << nameGetterFunc << "(" << classRoot << " *p) {" << endl;
	out << uglyDynamicCastTree(classRoot, 1) << endl;
	out << "}" << endl << endl;

	// A function to go from classes to Lua tables.
	tableGetterFunc = string("autoGenLuaWrapper_getTableFrom") + classRoot;
	out << "void " << tableGetterFunc << "(lua_State *L, " << classRoot << " *p) {" << endl;
	out << "  if(!p) { lua_pushnil(L); return; }" << endl;

	// This must be defined outside the system.
	string errorFunc = string("autoGenLuaWrapper_") + classRoot + string("_luaError");

	// Create the table and set the UID.
	out << "  lua_newtable(L);" << endl;
	
	// Wrapping the return of p->getUID() in a string, so the actual
	// function can return a c string or c++ string.
	out << "  lua_pushstring(L, std::string(p->getUID()).c_str());" << endl;
	out << "  lua_setfield(L, -2, \"uid\");" << endl;

	// Figure out what class it is.
	out << "  std::string className = " << nameGetterFunc << "(p);" << endl;
	
	// Get the table for that class.
	out << "  lua_getglobal(L, className.c_str());" << endl;
	
	// Get the inheritFromMe table from that.
	out << "  lua_getfield(L, -1, \"inheritFromMe\");" << endl;
	
	// Remove the global class from the stack.
	out << "  lua_remove(L, -2);" << endl;
	
	// Set the metatable.
	out << "  lua_setmetatable(L, -2);" << endl;
	
	out << "}" << endl << endl;
	

	for(unsigned int i = 0; i < classNamesInOrder.size(); i++) {
		string className = classNamesInOrder[i];
		ClassDef *classDef = existingClasses[className];
		
		out << "// ----------------------------------------------------------------------------" << endl;
		out << "//  " << className << endl;
		out << "// ----------------------------------------------------------------------------" << endl << endl;
		
		for(unsigned int j = 0; j < classDef->functions.size(); j++) {
			FuncDef *funcDef = classDef->functions[j];
			
			out << "static int autoGenLuaWrapper_" << className << "_" << funcDef->name << "(lua_State *L) {" << endl;
			
			// TODO: Check number of parameters.
			out << "  if(lua_gettop(L) < " << funcDef->parameters.size() + 1 << ") {" << endl;
			out << "    " << errorFunc << "(L, \"Not enough parameters for " << className << ":" << funcDef->name << "\");" << endl;
			out << "    return 0;" << endl;
			out << "  }" << endl;
			
			// Get all the parameters.
			for(unsigned int k = 0; k < funcDef->parameters.size(); k++) {
				ostringstream str;
				str << "param" << k;
				out << "  " << getParameter(str.str(), funcDef->parameters[k], k, funcDef->parameters.size());
			}
			
			if(funcDef->name == "new" && funcDef->returnType == PARAM_CLASS) {
			
				// Special case to make new stuff. (Basically just
				// wrapping "new ClassName(x, y, z)" instead of
				// "classInstance->func(x, y, z)"). Also doesn't want
				// an actual class instance.
				
				// The base class constructor should handle UIDs.
				
				out << "  " << className << " *p = new " << className << "();" << endl;
				out << "  " << tableGetterFunc << "(L, p);" << endl;
			
			} else if(funcDef->name == "delete" && funcDef->returnType == PARAM_VOID) {
				
				// Special case for deleting things. Basically wrapping
				// C++ delete here. 
				
				// The base class destructor (which should be virtual)
				// should handle UIDs.
				
				out << "  " << classRoot << " *p = " << classGetterFunc << "(L, -1);" << endl;
				out << "  if(p) {" << endl;
				out << "    autoGenLuaWrapper_" << classRoot << "_safeDelete(p);" << endl;
				out << "  } else {" << endl;
				out << "    " << errorFunc << "(L, \"Cannot delete incorrect or non-existant instance of " << className << "\");" << endl;
				out << "  }" << endl;
				
			} else {
				
				// Get the class instance this is coming from.
				out << "  " << getParameter("base", PARAM_CLASS, -1, funcDef->parameters.size());
				
				// Cast up to the right type.
				out << "  " << className << " *p = dynamic_cast<" << className << "*>(base);" << endl;
				
				out << "  if(p) {" << endl;
				
				string preCall;
				string postCall;
				
				// We're going to wrap up the result of the function
				// call based on the return type we were given so we
				// can interpret it if needed. (For example, pointers
				// to classes need to go back to tables with UIDs and
				// class-appropriate metatables.)
				
				// Excuse the messiness of this part. The generated
				// code looks cleaner.
				
				switch(funcDef->returnType) {
					
					case PARAM_BOOL:
						preCall = "    lua_pushboolean(L, (";
						postCall = "));\n    return 1;";
						break;
						
					case PARAM_NUMBERF:
					case PARAM_NUMBERI:
						preCall = "    lua_pushnumber(L, (float)(";
						postCall = "));\n    return 1;";
						break;
						
					case PARAM_STRING:
						preCall = "    std::string ret = (";
						postCall = ");\n    lua_pushstring(L, ret.c_str());\n    return 1;";
						break;
						
					case PARAM_VOID:
						preCall = "    ";
						postCall = ";\n    return 0;";
						break;
						
					case PARAM_CLASS:
						preCall = string("    ") + tableGetterFunc + "(L, (";
						postCall = "));\n    return 1;";
				}
				
				// Do the function call.
				out << preCall << "p->" << funcDef->name << "(";
				
				// Go through and add each parameter.
				for(unsigned int l = 0; l < funcDef->parameters.size(); l++) {
					ostringstream str;
					str << "param" << l;
					out << str.str();
					
					// Add a comma to separate them.
					if(l + 1 != funcDef->parameters.size()) {
						out << ", ";
					}
				}
				
				// Finish up and do the return weirdness.
				out << ")" << postCall << endl;
				
				out << "  } else {" << endl;
				// TODO: !!! Lua script error handling here !!!
				out << "    autoGenLuaWrapper_" << classRoot << "_luaError(L, \"Tried to call " << funcDef->name << " on an invalid " << className << "\");" << endl;
				out << "    return 0;" << endl;
				out << "  }" << endl;
				
			}
			
			if(funcDef->returnType == PARAM_VOID) {
				out << "  return 0;" << endl;
			} else {
				out << "  return 1;" << endl;
			}
			
			out << "}" << endl << endl;
		}
	}

	out << "// ----------------------------------------------------------------------------" << endl;
	out << "//  Wrapper init code" << endl;
	out << "// ----------------------------------------------------------------------------" << endl << endl;

	out << "void autoGenLuaWrapper_" << classRoot << "_init(lua_State *L) {" << endl << endl;

	for(unsigned int i = 0; i < classNamesInOrder.size(); i++) {
		string className = classNamesInOrder[i];
		ClassDef *classDef = existingClasses[className];
		
		out << "  // Create Lua table for " << className << "." << endl << endl;
		out << "  lua_newtable(L);" << endl;
		
		for(unsigned int j = 0; j < classDef->functions.size(); j++) {
			FuncDef *funcDef = classDef->functions[j];
			
			out << "  lua_pushstring(L, \"" << funcDef->name << "\");" << endl;
			out << "  lua_pushcfunction(L, autoGenLuaWrapper_" << className << "_" << funcDef->name << ");" << endl;
			out << "  lua_settable(L, -3);" << endl << endl;
		}
		
		// Class's own inherit-from-me metatable.
		out << "  // Set up inheritFromMe, in case anything wants to inherit from this." << endl;
		out << "  lua_pushstring(L, \"inheritFromMe\");" << endl;
		out << "  lua_newtable(L);" << endl;
		
		// Stack is: newClass, "inheritFromMe", inherit_from_me
		//   Set the __index element in it.
		out << "  lua_pushstring(L, \"__index\");" << endl;
		// Stack is: newClass, "inheritFromMe", inherit_from_me, "__index"
		out << "  lua_pushvalue(L, -4); // Get the real table we're making for the class." << endl; // newClass.
		// Stack is: newClass, "inheritFromMe", inherit_from_me, "__index", newClass	
		out << "  lua_settable(L, -3); // Set it to the __index element in this table." << endl; // inherit_from_me["__index"] = newClass
		// Stack is: newClass, "inheritFromMe", inherit_from_me	
		out << "  lua_settable(L, -3); // Set this table to inheritFromMe in the real table." << endl; // newClass["inheritFromMe"] = inherit_from_me
		
		// Class's metatable.
		if(classDef->parent) {
			out << endl;
			
			out << "  // Link " << className << " to its parent, " << classDef->parent->name << endl;
			
			// Get the global parent class.
			out << "  lua_getglobal(L, \"" << classDef->parent->name << "\");" << endl;
			
			// Get its inherit-from-me table.
			out << "  lua_getfield(L, -1, \"inheritFromMe\");" << endl;
			
			// Remove the global class from the stack.
			out << "  lua_remove(L, -2);" << endl;
			
			// Set the meta table.
			out << "  lua_setmetatable(L, -2);" << endl;
		}
		
		out << endl;
		out << "  lua_setglobal(L, \"" << className << "\");" << endl << endl;
		
		//out << "  // RTTI abuse." << endl;
		//out << "  rttiFor" << classRoot << "[typeid(" << className << ").name()] = \"" << className << "\";" << endl;
		
		out << endl;
	}

	out << "}" << endl << endl;


	// Dump documentation.
	
	// HOLY GOD THIS CODE IS MESSY.
	
	docOut << "<html>" << endl;
	docOut << "<head><title>" << classRoot << "</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" /></head>" << endl;
	docOut << "<body>";
	
	vector<string> classNamesByAlphabet = classNamesInOrder;
	docOut << "Classes in alphabetical order: <br/>";
	docOut << "<ul>";
	sort(classNamesByAlphabet.begin(), classNamesByAlphabet.end());
	for(unsigned int i = 0; i < classNamesByAlphabet.size(); i++) {
		docOut << "<li><a href=\"#" << classNamesByAlphabet[i] << "\">" << classNamesByAlphabet[i] << "</a></li>" << endl;
	}
	docOut << "</ul>";
	
	docOut << "Classes in hierarchy: <br/>";

	docOut << "<ul>" << dumpChildrenFor(classRoot, classNamesInOrder, existingClasses) << "</ul>";

	for(unsigned int i = 0; i < classNamesInOrder.size(); i++) {
		string className = classNamesInOrder[i];
		ClassDef *classDef = existingClasses[className];
		
		docOut << "<div class=\"classMainBlock\">";
		
		docOut << "<a name=\"" << className << "\"><div class=\"classHeaderName\">" << className << "</div></a>" << endl;
		
		if(classDef->parent) {
			docOut << "Parent class: <a href=\"#" << classDef->parent->name << "\">" << classDef->parent->name << "</a><br/>" << endl;
		}
		
		// Build a list of child classes.
		vector<string> childList;
		for(unsigned int k = 0; k < classNamesInOrder.size(); k++) {
			string className = classNamesInOrder[k];
			ClassDef *childClassDef = existingClasses[className];
			if(childClassDef->parent == classDef) {
				childList.push_back(className);
			}
		}

		if(childList.size()) {
			docOut << "Child classes: ";
			for(unsigned int k = 0; k < childList.size(); k++) {
				docOut << "<a href=\"#" << childList[k] << "\">" << childList[k] << "</a>";
				if(k != childList.size() - 1) {
					docOut << ", ";
				}
			}
			docOut << "<br/>";
		}
		
		docOut << "<br/>";

		docOut << "<div class=\"classDocBlock\">";
		docOut << "<div class=\"classDocParagraph\">";
		docOut << genDocStringFromComments(className, classDef->commentLines, "</div><div class=\"classDocParagraph\">") << endl;
		docOut << "</div>";
		docOut << "</div>";
		
		docOut << "<div class=\"funcDocBlock\">";
		docOut << "<dl>" << endl;
		
		for(unsigned int j = 0; j < classDef->functions.size(); j++) {
			FuncDef *funcDef = classDef->functions[j];
			
			// The line with the name of the function, its return type,
			// and parameters.
			docOut << "<dt class=\"funcPrototype\">";
			
			// Return type.
			docOut << "<div class=\"funcParamType\">" << paramToLuaType(funcDef->returnType, classRoot) << "</div>" << " ";
			
			// The function name itself.
			docOut << "<div class=\"funcName\">" << funcDef->name << "</div>";
			
			// Parameter list.
			docOut << "(";
			for(unsigned int k = 0; k < funcDef->parameters.size(); k++) {
				std::string paramName = funcDef->parameterNames[k];
				docOut << "<div class=\"funcParamType\">" << paramToLuaType(funcDef->parameters[k], classRoot) << "</div> ";
				docOut << paramName;
				if(k != funcDef->parameters.size() - 1) {
					docOut << ", ";
				}
			}
			
			docOut << ")" << endl;
			
			docOut << "</dt><dd class=\"funcDesc\">" << endl;
			
			if(funcDef->name == "new") {
				docOut << "Auto-generated wrapper. Creates and returns a new instance of " << className << "." << endl;
			} else if(funcDef->name == "delete") {
				docOut << "Auto-generated wrapper. Deletes an instance of " << className << ". Note that the deletion calls are wrapped in a function so that the C++ implementation may deny requests to delete an object." << endl;
			} else {
				docOut << genDocStringFromComments(className + ":" + funcDef->name, funcDef->commentLines, "<br/><br/>") << endl;
			}
			
			docOut << "</dd>" << endl;
		}
		
		docOut << "</dl>" << endl;
		docOut << "</div>";
		
		docOut << "</div>";
		
	}
	docOut << "<body></html>" << endl;

	// Clean up allocated stuff. Probably not necessary because it's
	// about to end, but I'm anal about this stuff even in a dirty,
	// filthy thing like this. Also, am drunk.
	
	// I CLEAN UP MEMORY LEAKS WHEN I'M DRUNK, K?
	
	for(unsigned int i = 0; i < classNamesInOrder.size(); i++) {
		string className = classNamesInOrder[i];
		ClassDef *classDef = existingClasses[className];
		
		for(unsigned int j = 0; j < classDef->functions.size(); j++) {
			FuncDef *funcDef = classDef->functions[j];
			delete funcDef;
		}
		
		delete classDef;
	}
	
	docOut.close();
	
	return 0;

}

