/* cpp-to-c.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2007-2009 */

/*

Copyright (c) 2007-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

/*
 * This tool is used to generate the C headers and interface
 * to use the SSWF library (libsswf).
 *
 */

#include	<stdlib.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<string.h>
#include	<assert.h>
#include	<stdarg.h>

// std lib.
#include	<string>
#include	<vector>

// get the version
#include	"sswf/libsswf-version.h"



int g_errcnt = 0;



void check_cpp_filename(const char *filename)
{
	int	l;

	l = strlen(filename);
	if(l < 2) {
		fprintf(stderr, "ERROR: input filename \"%s\" too short.\n", filename);
		exit(1);
	}
	if(filename[l - 1] != 'h' || filename[l - 2] != '.') {
		fprintf(stderr, "ERROR: the input filename \"%s\" is expected to end with .h.\n", filename);
		exit(1);
	}
}

#define OUTPUT_HEADER		0
#define OUTPUT_CPP		1

char *output_filename(const char *filename, int which)
{
	int	l;
	char	*s;

	l = strlen(filename);
	/* since we already checked we know that filename ends with .h
	 * here we want to add _c.h or _c.c++
	 */
	s = new char[l + 10];
	memcpy(s, filename, l - 2);
	s[l - 2] = '_';
	s[l - 1] = 'c';
	s[l] = '.';

	switch(which) {
	case OUTPUT_HEADER:
		s[l + 1] = 'h';
		s[l + 2] = '\0';
		break;

	case OUTPUT_CPP:
		s[l + 1] = 'c';
		s[l + 2] = '+';
		s[l + 3] = '+';
		s[l + 4] = '\0';
		break;

	default:
		fprintf(stderr, "ERROR: unknown output type.\n");
		exit(1);

	}

	return s;
}



char *read_file(const char *filename)
{
	FILE		*f;
	int		l;
	char		*s;

	f = fopen(filename, "r");
	if(f == NULL) {
		fprintf(stderr, "ERROR: cannot open file \"%s\".\n", filename);
		exit(1);
	}

	fseek(f, 0, SEEK_END);
	l = ftell(f);
	fseek(f, 0, SEEK_SET);

	s = new char[l + 1];

	if(fread(s, l, 1, f) != 1) {
		perror("ERROR: fread() failed:");
		fprintf(stderr, "ERROR: cannot read file \"%s\".\n", filename);
		exit(1);
	}

	s[l] = '\0';

	fclose(f);

	return s;
}


void write_warning(FILE *f, const char *filename)
{
	time_t now = time(NULL);
	struct tm *gm = gmtime(&now);
	fprintf(f, "/*************************************************************************\n");
	fprintf(f, " * WARNING! This file was automatically generated by cpp-to-c (%04d/%02d/%02d)\n", gm->tm_year + 1900, gm->tm_mon + 1, gm->tm_mday);
	fprintf(f, " * Avoid editing it since it is likely going to be overwritten.\n");
	fprintf(f, " *************************************************************************/\n");
	fprintf(f, "\n");
	fprintf(f, "/* %s -- written by Alexis WILKE for Made to Order Software Corp. (c) 2006-%d */\n", filename, gm->tm_year + 1900);
	fprintf(f, "\n");
	fprintf(f, "/*\n");
	fprintf(f, "\n");
	fprintf(f, "Copyright (c) 2006-%d Made to Order Software Corp.\n", gm->tm_year + 1900);
	fprintf(f, "\n");
	fprintf(f, "Permission is hereby granted, free of charge, to any\n");
	fprintf(f, "person obtaining a copy of this software and\n");
	fprintf(f, "associated documentation files (the \"Software\"), to\n");
	fprintf(f, "deal in the Software without restriction, including\n");
	fprintf(f, "without limitation the rights to use, copy, modify,\n");
	fprintf(f, "merge, publish, distribute, sublicense, and/or sell\n");
	fprintf(f, "copies of the Software, and to permit persons to whom\n");
	fprintf(f, "the Software is furnished to do so, subject to the\n");
	fprintf(f, "following conditions:\n");
	fprintf(f, "\n");
	fprintf(f, "The above copyright notice and this permission notice\n");
	fprintf(f, "shall be included in all copies or substantial\n");
	fprintf(f, "portions of the Software.\n");
	fprintf(f, "\n");
	fprintf(f, "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n");
	fprintf(f, "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\n");
	fprintf(f, "LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n");
	fprintf(f, "FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO\n");
	fprintf(f, "EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n");
	fprintf(f, "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n");
	fprintf(f, "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n");
	fprintf(f, "ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n");
	fprintf(f, "SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n");
	fprintf(f, "SOFTWARE.\n");
	fprintf(f, "\n");
	fprintf(f, "*/\n");
	fprintf(f, "\n");

}




class Input
{
public:
			Input(const char *str)
			{
				f_unget_pos = 0;
				f_str = str;
				f_line = 1;
			}

	int		Line() const
			{
				return f_line;
			}
	char		GetC()
			{
				if(f_unget_pos > 0) {
					--f_unget_pos;
					return f_unget[f_unget_pos];
				}
				if(*f_str == '\0') {
					return '\0';
				}
				if(*f_str == '\r') {
					if(f_str[1] == '\n') {
						f_str += 1;
					}
					++f_line;
					++f_str;
					return '\n';
				}
				else if(*f_str == '\n') {
					++f_line;
				}
				char c = *f_str;
				++f_str;
				if(c == '\t' || c == '\v' || c == '\f') {
					c = ' ';
				}
				return c;
			}
	void		UngetC(char c)
			{
				if(c != '\0') {
					assert(f_unget_pos < sizeof(f_unget));
					f_unget[f_unget_pos] = c;
					++f_unget_pos;
				}
			}

	const char *	GetString() const
			{
				return f_str;
			}

private:
	const char *	f_str;
	unsigned int	f_unget_pos;
	char		f_unget[8];
	int		f_line;
};



class Lex
{
public:
	enum token_t
	{
		TOK_IDENTIFIER = 256,
		TOK_STRING,
		TOK_VALUE,
		TOK_DOTDOTDOT,

		TOK_EOF
	};

			Lex(const char *filename, Input *input)
			{
				f_filename = filename;
				f_input = input;
			}

	token_t		GetToken()
			{
				char	c;

				f_text_len = 0;
				do {
					c = f_input->GetC();

					/* comment */
					if(c == '/') {
						c = f_input->GetC();
						if(c == '/') {
							// C++ comment
							do {
								c = f_input->GetC();
							} while(c != '\0' && c != '\n');
						}
						else if(c == '*') {
							// C comment
							do {
								c = f_input->GetC();
								while(c == '*') {
									c = f_input->GetC();
									if(c == '/') {
										c = ' ';
										goto exit_loop;
									}
								}
							} while(c != '\0');
exit_loop:;
						}
						else {
							/* not a comment */
							f_input->UngetC(c);
							c = '/';
						}
					}
					/* preprocessor */
					else if(c == '#') {
						// skip preprocessor as if these where comments!
						do {
							c = f_input->GetC();
						} while(c != '\0' && c != '\n');
					}

				} while(c == ' ' || c == '\n');
				if(c == '\0') {
					return TOK_EOF;
				}

				if(c == '.') {
					c = f_input->GetC();
					if(c == '.') {
						c = f_input->GetC();
						if(c == '.') {
							return TOK_DOTDOTDOT;
						}
						f_input->UngetC(c);
						f_input->UngetC('.');
						return static_cast<token_t>('.');
					}
					f_input->UngetC(c);
					return static_cast<token_t>('.');
				}

				/* identifier */
				if((c >= 'a' && c <= 'z')
				|| (c >= 'A' && c <= 'Z')
				|| c == '_') {
					/* identifier */
					do {
						f_text[f_text_len] = c;
						++f_text_len;
						c = f_input->GetC();
					} while((c >= 'a' && c <= 'z')
						|| (c >= 'A' && c <= 'Z')
						|| (c >= '0' && c <= '9')
						|| c == '_');
					// whatever we read is not part of the identifier
					f_input->UngetC(c);
					return TOK_IDENTIFIER;
				}

				/* string */
				if(c == '"') {
					for(;;) {
						c = f_input->GetC();
						if(c == '"') {
							break;
						}
						if(c == '\0' || c == '\r' || c == '\n') {
							Error("interminated string");
							/*NOTREACHED*/
						}
						assert(f_text_len < sizeof(f_text) - 1);
						f_text[f_text_len] = c;
						++f_text_len;
						if(c == '\\') {
							c = f_input->GetC();
							if(c == '\0' || c == '\r' || c == '\n') {
								Error("interminated string");
								/*NOTREACHED*/
							}
							f_text[f_text_len] = c;
						}
					}
					return TOK_STRING;
				}

				/* value (integer or float) */
				if(c >= '0' && c <= '9') {
					f_text[f_text_len] = c;
					++f_text_len;
					c = f_input->GetC();
					if(c == 'x' || c == 'X') {
						do {
							f_text[f_text_len] = c;
							++f_text_len;
							c = f_input->GetC();
						} while((c >= '0' && c <= '9')
							|| (c >= 'a' && c <= 'f')
							|| (c >= 'A' && c <= 'F'));
					}
					else {
						while(c >= '0' && c <= '9') {
							f_text[f_text_len] = c;
							++f_text_len;
							c = f_input->GetC();
						}
						if(c == '.') {
							do {
								f_text[f_text_len] = c;
								++f_text_len;
								c = f_input->GetC();
							} while(c >= '0' && c <= '9');
						}
						if(c == 'e' || c == 'E') {
							f_text[f_text_len] = c;
							++f_text_len;
							c = f_input->GetC();
							if(c == '+' || c == '-') {
								f_text[f_text_len] = c;
								++f_text_len;
								c = f_input->GetC();
							}
							while(c >= '0' && c <= '9') {
								f_text[f_text_len] = c;
								++f_text_len;
								c = f_input->GetC();
							}
						}
					}
					f_input->UngetC(c);
					return TOK_VALUE;
				}

				// the character as is
				return static_cast<token_t>(c);
			}

	const char *	GetText()
			{
				f_text[f_text_len] = '\0';
				return f_text;
			}

	void		Error(const char *format, ...)
			{
				va_list		args;

				fflush(stdout);
				va_start(args, format);
				fprintf(stderr, "%s:%d: error: ", f_filename, f_input->Line());
				vfprintf(stderr, format, args);
				fprintf(stderr, ".\n");
				va_end(args);

				exit(1);
			}

private:
	const char *	f_filename;
	Input *		f_input;
	unsigned int	f_text_len;
	char		f_text[256];
};





class Enum
{
public:
				Enum()
				{
					++f_count_enums;
				}

	static int		GetCount()
				{
					return f_count_enums;
				}

	void			SetName(const char *name)
				{
					f_name = name;
				}
	void			AddEnum(const std::string& enum_name, const std::string& enum_value)
				{
					EnumEntry	entry;

					entry.f_enum_name = enum_name;
					entry.f_enum_value = enum_value;

					f_enum.push_back(entry);
				}

	const std::string&	GetName() const
				{
					return f_name;
				}

	void			SaveEnum(FILE *f)
				{
					int					l, last, count, max;
					const char				*indent;
					std::vector<EnumEntry>::iterator	i;

					/* only really private but necessarily public enum (because of cl)
					 * is not named and we do not want it in C that's for sure!
					 */
					if(f_name.size() == 0) {
						return;
					}

					last = f_enum.size() - 1;
					fprintf(f, "enum sswf_%s\n{\n", f_name.c_str());
					max = 0;
					count = 0;
					i = f_enum.begin();
					while(i < f_enum.end()) {
						if((*i).f_enum_value.size() > 0) {
							++count;
						}
						l = ((*i).f_enum_name.size() + 16) & -8;
						if(l > max) {
							max = l;
						}
						++i;
					}
					if(count == 1) {
						max = 0;
						indent = " ";
					}
					else {
						indent = "\t";
					}
					i = f_enum.begin();
					while(i < f_enum.end()) {
						fprintf(f, "\t%s", (*i).f_enum_name.c_str());
						if((*i).f_enum_value.size() > 0) {
							l = ((*i).f_enum_name.size() + 16) & -8;
							while(l < max) {
								fprintf(f, "\t");
								l += 8;
							}
							fprintf(f, "%s= %s", indent, (*i).f_enum_value.c_str());
						}
						++i;
						if(i == f_enum.end()) {
							fprintf(f, "\n");
						}
						else {
							fprintf(f, ",\n");
						}
					}
					fprintf(f, "};\n");
				}

private:
	struct EnumEntry
	{
		std::string	f_enum_name;
		std::string	f_enum_value;
	};

	static int		f_count_enums;

	std::string		f_name;
	std::vector<EnumEntry>	f_enum;
};

int Enum::f_count_enums = 0;





class IClass
{
public:
	virtual				~IClass() {}

	virtual const std::string&	GetName() const = 0;
};


class IFindClass
{
public:
	virtual			~IFindClass() {}

	virtual IClass *	FindClassByName(const char *name) = 0;
};

class IFindEnum
{
public:
	virtual			~IFindEnum() {}

	virtual IClass *	FindEnumByName(const char *name) = 0;
};

class IFindStruct
{
public:
	virtual			~IFindStruct() {}

	virtual IClass *	FindStructByName(const char *name) = 0;
};




class WithTypedElements
{
public:
			WithTypedElements(IFindClass *find_class, IFindEnum *find_enum, IFindStruct *find_struct)
			{
				f_find_class = find_class;
				f_find_enum = find_enum;
				f_find_struct = find_struct;
			}
	virtual		~WithTypedElements()
			{
			}

	bool		SaveType(FILE *f, const char *type, const char *class_name)
			{
				const char	*s, *sep, *type_only;
				char		*d, buf[256];
				bool		is_static = false;

				sep = "";
				s = type;
				while(*s != '\0') {
					d = buf;
					type_only = buf;
					while(*s != ' ' && *s != '\0') {
						*d++ = *s++;
						if(d[-1] == ':') {
							type_only = d;
						}
					}
					*d = '\0';
//printf("Type bit = [%s] <- [%s]\n", buf, type);
					if(strcmp(buf, "static") == 0) {
						is_static = true;
					}
					else if(strcmp(buf, "virtual") != 0) {
						if(f_find_class->FindClassByName(buf) != 0) {
							/* if one of our classes, we need the proper introducer */
							fprintf(f, "%sstruct SSWF_%s", sep, buf);
						}
						else if(f_find_enum->FindEnumByName(buf) != 0) {
							/* enum of this class or parents */
							fprintf(f, "%senum sswf_%s", sep, type_only);
						}
						else if(f_find_struct->FindStructByName(buf) != 0) {
							/* struct of this class or parents */
							fprintf(f, "%sstruct sswf_%s", sep, buf);
						}
						else if(strcmp(buf, "const") == 0
								|| strcmp(buf, "signed") == 0
								|| strcmp(buf, "unsigned") == 0
								|| strcmp(buf, "char") == 0
								|| strcmp(buf, "short") == 0
								|| strcmp(buf, "int") == 0
								|| strcmp(buf, "long") == 0
								|| strcmp(buf, "int64_t") == 0
								|| strcmp(buf, "uint64_t") == 0
								|| strcmp(buf, "float") == 0
								|| strcmp(buf, "double") == 0
								|| strcmp(buf, "size_t") == 0
								|| strcmp(buf, "va_list") == 0
								|| strcmp(buf, "void") == 0
								|| strcmp(buf, "*") == 0
								|| strcmp(buf, "swf_type_t") == 0
								|| strcmp(buf, "sswf_id_t") == 0
								|| strcmp(buf, "sswf_ucs4_t") == 0
								|| strcmp(buf, "sswf_frame_t") == 0) {
							// standard C/C++ and some SSWF typedef's
							fprintf(f, "%s%s", sep, buf);
						}
						else if(strcmp(buf, "bool") == 0) {
							fprintf(f, "%sint", sep);
						}
						else if(strcmp(buf, "TagBase") == 0) {
							fprintf(f, "%sSSWF_TagBase", sep);
						}
						else if(strcmp(buf, "&") == 0) {
							fprintf(f, "%s*", sep);
						}
						else if(strcmp(buf, "*&") == 0) {
							fprintf(f, "%s**", sep);
						}
						else {
							fflush(stdout);
							fprintf(stderr, "ERROR: unresolved type \"%s\" in class \"%s\" (1).\n", buf, class_name);
							g_errcnt++;
							//exit(1);
						}
						sep = " ";
					}
					while(*s == ' ') {
						s++;
					}
				}

				return is_static;
			}

protected:
	IFindClass *		f_find_class;
	IFindEnum *		f_find_enum;
	IFindStruct *		f_find_struct;
};


class Struct : public WithTypedElements
{
public:
				Struct(IFindClass *find_class, IFindEnum *find_enum, IFindStruct *find_struct)
					: WithTypedElements(find_class, find_enum, find_struct)
				{
					++f_count_structs;
				}
	virtual			~Struct()
				{
				}

	static int		GetCount()
				{
					return f_count_structs;
				}

	void			SetName(const char *name)
				{
					f_name = name;
				}

	const std::string&	GetName() const
				{
					return f_name;
				}

	void			AddField(const std::string& type, const std::string& name)
				{
					StructField	field;

					field.f_type = type;
					field.f_name = name;
					f_fields.push_back(field);
				}

	void			SaveStruct(FILE *f, const char *class_name)
				{
					int					l, last, max;
					std::vector<StructField>::iterator	i;

					last = f_fields.size() - 1;
					fprintf(f, "struct sswf_%s\n{\n", f_name.c_str());
					max = 0;
					i = f_fields.begin();
					while(i < f_fields.end()) {
						// TODO: this is bogus since the SaveType()
						//	 may add another 5 characters to the
						//	 type name (i.e. "sswf_")
						l = ((*i).f_type.size() + 16) & -8;
						if(l > max) {
							max = l;
						}
						++i;
					}
					i = f_fields.begin();
					while(i < f_fields.end()) {
						fprintf(f, "\t");
						SaveType(f, (*i).f_type.c_str(), class_name);
						l = ((*i).f_type.size() + 16) & -8;
						while(l < max) {
							fprintf(f, "\t");
							l += 8;
						}
						fprintf(f, "\t%s;\n", (*i).f_name.c_str());
						++i;
					}
					fprintf(f, "};\n");
				}

private:
	struct StructField
	{
		std::string	f_type;
		std::string	f_name;
	};

	static int		 f_count_structs;

	std::string		 f_name;
	std::vector<StructField> f_fields;
};

int Struct::f_count_structs = 0;





class Func : public WithTypedElements
{
public:
				Func(IFindClass *find_class, IFindEnum *find_enum, IFindStruct *find_struct)
					: WithTypedElements(find_class, find_enum, find_struct)
				{
					++f_count_funcs;
				}
	virtual			~Func()
				{
				}


	static int		GetCount()
				{
					return f_count_funcs;
				}

	void			SetName(const std::string& name)
				{
					f_name = name;
				}

	void			SetType(const std::string& type)
				{
					f_type = type;
				}

	void			AddParam(const std::string& type, const std::string& name)
				{
					Param p;

					p.f_type = type;
					p.f_name = name;
					f_params.push_back(p);
				}

	bool			SameName(Func *right)
				{
					if(f_name == right->f_name) {
						if((f_type.size() == 0 && right->f_type.size() != 0)
						|| (f_type.size() != 0 && right->f_type.size() == 0)) {
							return false;
						}
						return true;
					}

					return false;
				}

	static void		ToLowercase(FILE *f, const char *begin, const char *name, const char *end)
				{
					if(*name >= 'A' && *name <= 'Z') {
						fprintf(f, "%s%c", begin, *name | 0x20);
						while(name[1] >= 'A' && name[1] <= 'Z') {
							name++;
							fprintf(f, "%c", *name | 0x20);
						}
					}
					else {
						fprintf(f, "%s%c", begin, *name);
					}
					++name;
					while(*name != '\0') {
						if(*name >= 'A' && *name <= 'Z') {
							fprintf(f, "_%c", *name | 0x20);
							while(name[1] >= 'A' && name[1] <= 'Z') {
								name++;
								fprintf(f, "%c", *name | 0x20);
							}
						}
						else {
							fprintf(f, "%c", *name);
						}
						++name;
					}
					fprintf(f, "%s", end);
				}
	void			SaveDestructor(FILE *f, const char *class_name)
				{
					ToLowercase(f, "extern void sswf_", class_name, "_destroy");
					fprintf(f, "(struct SSWF_%s *", class_name);
					ToLowercase(f, "", class_name, ");\n");
				}
	void			GenerateDestructor(FILE *f, const char *class_name)
				{
					ToLowercase(f, "void sswf_", class_name, "_destroy");
					fprintf(f, "(struct SSWF_%s *", class_name);
					ToLowercase(f, "", class_name, ")\n{\n");
					fprintf(f, "\tdelete reinterpret_cast<sswf::%s *>(", class_name);
					ToLowercase(f, "", class_name, ");\n}\n\n");
				}
	void			SaveDef(FILE *f, const char *class_name, int index)
				{
					bool		is_static = true;
					const char	*comma;
					char		idx[16];

					if(index == 0) {
						idx[0] = '\0';
					}
					else {
						snprintf(idx, sizeof(idx), "%d", index);
					}

					if(f_name == class_name) {
						// constructors cannot have a return type
						// destructors have at least the ~
						if(f_type.size() > 0) {
							// destructor
							return;
						}
						// constructor
						fprintf(f, "extern struct SSWF_%s", class_name);
						ToLowercase(f, " *sswf_", class_name, "_create");
					}
					else {
						// other function, save the type and name
						fprintf(f, "extern ");
						is_static = SaveType(f, f_type.c_str(), class_name);
						ToLowercase(f, " sswf_", class_name, "_");
						ToLowercase(f, "", f_name.c_str(), "");
					}
					fprintf(f, "%s(", idx);
					if(!is_static) {
						fprintf(f, "struct SSWF_%s *", class_name);
						ToLowercase(f, "", class_name, "");
						comma = ", ";
					}
					else {
						comma = "";
					}

					std::vector<Param>::iterator i = f_params.begin();
					while(i < f_params.end()) {
						fprintf(f, "%s", comma);
//printf("Param type = [%s]\n", (*i).f_type.c_str());
						if(SaveType(f, (*i).f_type.c_str(), class_name)) {
							fprintf(stderr, "ERROR: no variable can be static in the list of parameters.\n");
							exit(1);
						}
						fprintf(f, " %s", (*i).f_name.c_str());
						comma = ", ";
						++i;
					}
					fprintf(f, ");\n");
				}

	bool			IsEnum(const char *type)
				{
					char		*d, buf[256];

					while(*type != '\0') {
						d = buf;
						while(*type != ' ' && *type != '\0') {
							*d++ = *type++;
						}
						*d = '\0';

						if(f_find_enum->FindEnumByName(buf)) {
							return true;
						}

						while(*type == ' ') {
							type++;
						}
					}

					return false;
				}

	bool			GenerateType(FILE *f, const char *type, const char *class_name)
				{
					const char	*s, *sep, *type_only;
					char		*d, buf[256];
					bool		is_static = false;
					IClass		*c;

					sep = "";
					s = type;
					while(*s != '\0') {
						d = buf;
						type_only = buf;
						while(*s != ' ' && *s != '\0') {
							*d++ = *s++;
							if(d[-1] == ':') {
								type_only = d;
							}
						}
						*d = '\0';

						if(f_find_class->FindClassByName(buf) != 0) {
							/* if one of our classes, we need the proper introducer */
							fprintf(f, "%ssswf::%s", sep, buf);
						}
						else if((c = f_find_enum->FindEnumByName(buf)) != 0) {
							/* enum of this class or parents */
							fprintf(f, "%ssswf::%s::%s", sep, c->GetName().c_str(), type_only);
						}
						else if((c = f_find_struct->FindStructByName(buf)) != 0) {
							/* struct of this class or parents */
							fprintf(f, "%ssswf::%s::%s", sep, c->GetName().c_str(), buf);
						}
						else if(strcmp(buf, "const") == 0
								|| strcmp(buf, "signed") == 0
								|| strcmp(buf, "unsigned") == 0
								|| strcmp(buf, "bool") == 0
								|| strcmp(buf, "char") == 0
								|| strcmp(buf, "short") == 0
								|| strcmp(buf, "int") == 0
								|| strcmp(buf, "long") == 0
								|| strcmp(buf, "int64_t") == 0
								|| strcmp(buf, "uint64_t") == 0
								|| strcmp(buf, "float") == 0
								|| strcmp(buf, "double") == 0
								|| strcmp(buf, "size_t") == 0
								|| strcmp(buf, "va_list") == 0
								|| strcmp(buf, "void") == 0
								|| strcmp(buf, "*") == 0) {
							// standard C/C++ and some SSWF typedef's
							fprintf(f, "%s%s", sep, buf);
						}
						else if(strcmp(buf, "swf_type_t") == 0
								|| strcmp(buf, "sswf_id_t") == 0
								|| strcmp(buf, "sswf_ucs4_t") == 0
								|| strcmp(buf, "sswf_frame_t") == 0) {
							fprintf(f, "%ssswf::%s", sep, buf);
						}
						else if(strcmp(buf, "&") == 0) {
							fprintf(f, "%s*", sep);
						}
						else if(strcmp(buf, "*&") == 0) {
							fprintf(f, "%s**", sep);
						}
						else {
							fflush(stdout);
							fprintf(stderr, "ERROR: unresolved type \"%s\" in class \"%s\" (2).\n", buf, class_name);
							g_errcnt++;
							//exit(1);
						}
						sep = " ";

						while(*s == ' ') {
							s++;
						}
					}

					return is_static;
				}

	void			GenerateParamType(FILE *f, const char *class_name)
				{
					const char	*comma, *dereference, *type;

					comma = "";
					std::vector<Param>::iterator i = f_params.begin();
					while(i < f_params.end()) {
						type = (*i).f_type.c_str();

						// in C the & operator needs to be replaced by *
						// and thus when we want to call a C++ function we
						// need to reference first
						if(strrchr(type, '&') != 0) {
							dereference = "*";
						}
						else {
							dereference = "";
						}

						// we need to reinterpret_cast<> from C structs to C++ structs
						// we need to static_cast<> from C enum to C++ enums
						// we need special handling for bool to avoid warnings
						// we need nothing for compatible C/C++ types (except the dereference)
						if(strcmp(type, "bool") == 0) {
							fprintf(f, "%s%s != 0", comma, (*i).f_name.c_str());
						}
						else {
							if(strncmp("const ", type, 6) == 0) {
								type += 6;
							}
							if(strncmp("signed ", type, 7) == 0) {
								type += 7;
							}
							if(strncmp("unsigned ", type, 9) == 0) {
								type += 9;
							}
//printf("Now type is [%s]\n", type);
							if(
								   strcmp(type, "char") == 0
								|| strcmp(type, "char *") == 0
								|| strcmp(type, "short") == 0
								|| strcmp(type, "short *") == 0
								|| strcmp(type, "int") == 0
								|| strcmp(type, "int *") == 0
								|| strcmp(type, "long") == 0
								|| strcmp(type, "long *") == 0
								|| strcmp(type, "long &") == 0
								|| strcmp(type, "int64_t") == 0
								|| strcmp(type, "int64_t *") == 0
								|| strcmp(type, "int64_t &") == 0
								|| strcmp(type, "uint64_t") == 0
								|| strcmp(type, "uint64_t *") == 0
								|| strcmp(type, "uint64_t &") == 0
								|| strcmp(type, "float") == 0
								|| strcmp(type, "float *") == 0
								|| strcmp(type, "double") == 0
								|| strcmp(type, "double *") == 0
								|| strcmp(type, "size_t") == 0
								|| strcmp(type, "size_t &") == 0
								|| strcmp(type, "va_list") == 0
								|| strcmp(type, "void *") == 0
								|| strcmp(type, "void *&") == 0
								|| strcmp(type, "sswf_ucs4_t") == 0
								|| strcmp(type, "sswf_id_t") == 0
								|| strcmp(type, "sswf_frame_t") == 0
								|| strcmp(type, "swf_type_t") == 0
							) {
								fprintf(f, "%s%s%s", comma, dereference, (*i).f_name.c_str());
							}
							else {
								fprintf(f, "%s%s", comma, dereference);
								if(IsEnum((*i).f_type.c_str())) {
									fprintf(f, "static_cast<");
								}
								else {
									fprintf(f, "reinterpret_cast<");
								}
								GenerateType(f, (*i).f_type.c_str(), class_name);
								fprintf(f, ">(%s)", (*i).f_name.c_str());
							}
						}

						comma = ", ";
						++i;
					}
				}

	void			GenerateFunc(FILE *f, const char *class_name, int index)
				{
					bool		is_static = true, is_constructor = false, has_return;
					const char	*comma, *return_type;
					char		idx[16];

					if(index == 0) {
						idx[0] = '\0';
					}
					else {
						snprintf(idx, sizeof(idx), "%d", index);
					}

					if(f_name == class_name) {
						// constructors cannot have a return type
						// destructors have at least the ~
						if(f_type.size() > 0) {
							// destructor
							return;
						}
						// constructor
						fprintf(f, "struct SSWF_%s", class_name);
						ToLowercase(f, " *sswf_", class_name, "_create");
						is_constructor = true;
					}
					else {
						// other function, save the type and name
						is_static = SaveType(f, f_type.c_str(), class_name);
						ToLowercase(f, " sswf_", class_name, "_");
						ToLowercase(f, "", f_name.c_str(), "");
					}

					fprintf(f, "%s(", idx);
					if(!is_static) {
						fprintf(f, "struct SSWF_%s *", class_name);
						ToLowercase(f, "", class_name, "");
						comma = ", ";
					}
					else {
						comma = "";
					}

					std::vector<Param>::iterator i = f_params.begin();
					while(i < f_params.end()) {
						fprintf(f, "%s", comma);
						if(SaveType(f, (*i).f_type.c_str(), class_name)) {
							fprintf(stderr, "ERROR: no variable can be static in the list of parameters.\n");
							exit(1);
						}
						fprintf(f, " %s", (*i).f_name.c_str());
						comma = ", ";
						++i;
					}
					fprintf(f, ")\n{\n");

					if(is_constructor) {
						fprintf(f, "\treturn reinterpret_cast<struct SSWF_%s *>(new sswf::%s", class_name, class_name);

						if(f_params.size() != 0) {
							fprintf(f, "(");
							GenerateParamType(f, class_name);
							fprintf(f, ")");
						}
						fprintf(f, ");\n");

					}
					else {
						fprintf(f, "\t");
						// I never return a void pointer of any kind
						has_return = strstr(f_type.c_str(), "void") == 0;
						if(has_return) {
							fprintf(f, "return ");

							return_type = f_type.c_str();
							if(strncmp("static ", return_type, 7) == 0) {
								return_type += 7;
							}
							if(strncmp("virtual ", return_type, 8) == 0) {
								return_type += 8;
							}
							if(strncmp("signed ", return_type, 7) == 0) {
								return_type += 7;
							}
							if(strncmp("unsigned ", return_type, 9) == 0) {
								return_type += 9;
							}

//fprintf(f, "RAW [%s]\n", f_type.c_str());

							if(strcmp(return_type, "bool") != 0
							&& strcmp(return_type, "char") != 0
							&& strcmp(return_type, "short") != 0
							&& strcmp(return_type, "int") != 0
							&& strcmp(return_type, "long") != 0
							&& strcmp(return_type, "double") != 0
							&& strcmp(return_type, "float") != 0
							&& strcmp(return_type, "sswf_id_t") != 0
							&& strcmp(return_type, "sswf_frame_t") != 0
							&& strcmp(return_type, "swf_type_t") != 0
							&& strcmp(return_type, "sswf_ucs4_t") != 0
							&& strcmp(return_type, "size_t") != 0
							&& strcmp(return_type, "va_list") != 0
							) {
								if(IsEnum(f_type.c_str())) {
									fprintf(f, "static_cast<");
								}
								else {
									fprintf(f, "reinterpret_cast<");
								}
								// if the returned type is a class, enum, struct we need an extra cast
								// so we just always do it (casting from int to int is safe too!)
								SaveType(f, f_type.c_str(), class_name);
								fprintf(f, ">(");
								if(strrchr(f_type.c_str(), '&') != 0) {
									fprintf(f, "&");
								}
							}
							else {
								has_return = false;
							}
						}
						if(is_static) {
							fprintf(f, "sswf::%s::%s(", class_name, f_name.c_str());
						}
						else {
							fprintf(f, "reinterpret_cast<sswf::%s *>(", class_name);
							ToLowercase(f, "", class_name, "");
							fprintf(f, ")->%s(", f_name.c_str());
						}

						GenerateParamType(f, class_name);

						if(has_return) {
							fprintf(f, ")");
						}
						fprintf(f, ");\n");
					}

					fprintf(f, "}\n\n");
				}

private:
	struct Param
	{
		std::string		f_type;
		std::string		f_name;
	};

	static int		f_count_funcs;

	std::string		f_name;
	std::string		f_type;
	std::vector<Param>	f_params;
};

int Func::f_count_funcs = 0;




class Class : public IFindEnum, public IFindStruct, public IClass
{
public:
				Class(IFindClass *find_class, const char *name)
				{
					f_find_class = find_class;
					f_name = name;
//printf("New class [%s]\n", name);
				}
	virtual			~Class()
				{
				}

	const std::string&	GetName() const
				{
					return f_name;
				}
	void			AddEnum(Enum *e)
				{
					f_enums.push_back(e);
				}
	void			AddStruct(Struct *s)
				{
					f_structs.push_back(s);
				}
	void			AddFunc(Func *f)
				{
					f_funcs.push_back(f);
				}

	void			SaveAsStruct(FILE *f)
				{
					fprintf(f, "struct SSWF_%s;\n", f_name.c_str());
				}

	void			SaveEnums(FILE *f)
				{
					std::vector<Enum *>::iterator i = f_enums.begin();
					if(i < f_enums.end()) {
						fprintf(f, "  /* %s */\n", f_name.c_str());
						while(i < f_enums.end()) {
							(*i)->SaveEnum(f);
							++i;
						}
					}
				}

	void			SaveStructs(FILE *f)
				{
					std::vector<Struct *>::iterator i = f_structs.begin();
					while(i < f_structs.end()) {
						(*i)->SaveStruct(f, f_name.c_str());
						++i;
					}
				}

	void			CastDerivative(FILE *f, const char *name)
				{
					/* if derived from other objects, we want to add cast functions */
					std::vector<std::string>::iterator i = f_derive_from.begin();
					while(i < f_derive_from.end()) {
						fprintf(f, "extern struct SSWF_%s *", (*i).c_str());
						Func::ToLowercase(f, "sswf_", name, "_");
						Func::ToLowercase(f, "cast_", (*i).c_str(), "");
						fprintf(f, "(struct SSWF_%s *", name);
						Func::ToLowercase(f, "", name, ");\n");

						Class *c = dynamic_cast<Class *>(f_find_class->FindClassByName((*i).c_str()));
						if(c != 0) {
							c->CastDerivative(f, name);
						}
						++i;
					}
				}

	void			GenerateCastDerivative(FILE *f, const char *name)
				{
					/* if derived from other objects, we want to add cast functions */
					std::vector<std::string>::iterator i = f_derive_from.begin();
					while(i < f_derive_from.end()) {
						fprintf(f, "struct SSWF_%s *", (*i).c_str());
						Func::ToLowercase(f, "sswf_", name, "_");
						Func::ToLowercase(f, "cast_", (*i).c_str(), "");
						fprintf(f, "(struct SSWF_%s *", name);
						Func::ToLowercase(f, "", name, ")\n{\n");
						fprintf(f, "\treturn reinterpret_cast<struct SSWF_%s *>(", (*i).c_str());
						fprintf(f, "static_cast<sswf::%s *>(", (*i).c_str());
						fprintf(f, "reinterpret_cast<sswf::%s *>(", name);
						Func::ToLowercase(f, "", name, ")));\n}\n\n");

						Class *c = dynamic_cast<Class *>(f_find_class->FindClassByName((*i).c_str()));
						if(c != 0) {
							c->GenerateCastDerivative(f, name);
						}
						++i;
					}
				}

	void			SaveFuncDefs(FILE *f)
				{
					int		index;

					if(f_funcs.size() > 0 || f_derive_from.size() > 0) {
						fprintf(f, "  /* Object: %s */\n", f_name.c_str());
					}
					if(f_derive_from.size() > 0) {
						CastDerivative(f, f_name.c_str());
					}
					if(f_funcs.size() > 0) {
						std::vector<Func *>::iterator i = f_funcs.begin();
						(*i)->SaveDestructor(f, f_name.c_str());
						while(i < f_funcs.end()) {
							index = 0;
							std::vector<Func *>::iterator j = i - 1;
							while(j >= f_funcs.begin()) {
								if((*i)->SameName(*j)) {
									++index;
								}
								--j;
							}
							if(index == 0) {
								// check after then
								j = i + 1;
								while(j < f_funcs.end()) {
									if((*i)->SameName(*j)) {
										index = 1;
										break;
									}
									++j;
								}
							}
							else {
								++index;
							}
							(*i)->SaveDef(f, f_name.c_str(), index);
							++i;
						}
					}
				}

	virtual IClass *	FindEnumByName(const char *name)
				{
					const char *e, *class_name;

					// TODO: at this time I support only one scope operator
					class_name = name;
					e = name;
					while(*e != '\0') {
						if(*e == ':') {
							name = e + 1;
						}
						++e;
					}
					if(class_name != name) {
						// we want to search for the enumeration in that class first
						// derived classes usually aren't specified like this!
						std::string cn(class_name, name - class_name - 2);
						Class *c = dynamic_cast<Class *>(f_find_class->FindClassByName(cn.c_str()));
						if(c == 0) {
							fprintf(stderr, "WARNING: cannot find class \"%s\".\n", cn.c_str());
							g_errcnt++;
						}
						else {
							c = dynamic_cast<Class *>(c->FindEnumByName(name));
							if(c != 0) {
								return c;
							}
						}
					}
					std::vector<Enum *>::iterator i = f_enums.begin();
					while(i < f_enums.end()) {
						if((*i)->GetName() == name) {
//printf("Found enum [%s]\n", name);
							return this;
						}
						++i;
					}

					/* if not found here, we try a class we derived from */
					std::vector<std::string>::iterator s = f_derive_from.begin();
					while(s < f_derive_from.end()) {
						// classes which we know are not exposed
						if(*s != "MemoryManager" && *s != "MemBuffer") {
							Class *c = dynamic_cast<Class *>(f_find_class->FindClassByName((*s).c_str()));
							if(c == 0) {
								fprintf(stderr, "WARNING: cannot find class \"%s\".\n", (*s).c_str());
								g_errcnt++;
							}
							else {
								c = dynamic_cast<Class *>(c->FindEnumByName(name));
								if(c != 0) {
									return c;
								}
							}
						}
						++s;
					}

					return 0;
				}

	virtual IClass *	FindStructByName(const char *name)
				{
					std::vector<Struct *>::iterator i = f_structs.begin();
					while(i < f_structs.end()) {
						if((*i)->GetName() == name) {
							return this;
						}
						++i;
					}

					/* if not found here, we try a class we derived from */
					std::vector<std::string>::iterator s = f_derive_from.begin();
					while(s < f_derive_from.end()) {
						// classes which we know are not exposed
						if(*s != "MemoryManager" && *s != "MemBuffer") {
							Class *c = dynamic_cast<Class *>(f_find_class->FindClassByName((*s).c_str()));
							if(c == 0) {
								fprintf(stderr, "WARNING: cannot find class \"%s\".\n", (*s).c_str());
								g_errcnt++;
							}
							else {
								c = dynamic_cast<Class *>(c->FindStructByName(name));
								if(c != 0) {
									return c;
								}
							}
						}
						++s;
					}

					return 0;
				}

	void			DeriveFrom(const char *derive_from)
				{
					if(strcmp(derive_from, "MemBuffer") != 0
					&& strcmp(derive_from, "MemoryManager") != 0) {
						f_derive_from.push_back(derive_from);
					}
				}

	void			GenerateFunc(FILE *f)
				{
					int		index;

					if(f_funcs.size() > 0 || f_derive_from.size() > 0) {
						fprintf(f, "  /* Object: %s */\n", f_name.c_str());
					}
					if(f_derive_from.size() > 0) {
						GenerateCastDerivative(f, f_name.c_str());
					}
					if(f_funcs.size() > 0) {
						std::vector<Func *>::iterator i = f_funcs.begin();
						(*i)->GenerateDestructor(f, f_name.c_str());
						while(i < f_funcs.end()) {
							index = 0;
							std::vector<Func *>::iterator j = i - 1;
							while(j >= f_funcs.begin()) {
								if((*i)->SameName(*j)) {
									++index;
								}
								--j;
							}
							if(index == 0) {
								// check after then
								j = i + 1;
								while(j < f_funcs.end()) {
									if((*i)->SameName(*j)) {
										index = 1;
										break;
									}
									++j;
								}
							}
							else {
								++index;
							}
							(*i)->GenerateFunc(f, f_name.c_str(), index);
							++i;
						}
					}
				}


private:
	IFindClass *		 f_find_class;
	std::string		 f_name;
	std::vector<std::string> f_derive_from;
	std::vector<Enum *>	 f_enums;
	std::vector<Struct *>	 f_structs;
	std::vector<Func *>	 f_funcs;
};




class Parser : public IFindClass
{
public:
			Parser(Lex *lex)
			{
				f_lex = lex;
			}
	virtual		~Parser()
			{
			}

	Enum *		ReadEnum()
			{
				std::string	enum_name;
				std::string	enum_value;
				Lex::token_t	tok;
				Enum		*e = new Enum;

				/* _class: optional enumation name */
				tok = f_lex->GetToken();
				if(tok == Lex::TOK_IDENTIFIER) {
					e->SetName(f_lex->GetText());
					tok = f_lex->GetToken();
				}
				if(tok != '{') {
					f_lex->Error("'{' expected after 'enum'");
					/*NOTREACHED*/
				}
				// we do not support empty enums (useless anyway!)
				tok = f_lex->GetToken();
				do {
					if(tok != Lex::TOK_IDENTIFIER) {
						f_lex->Error("a name was expected in the enumeration (got token %d)", tok);
						/*NOTREACHED*/
					}
					// keep a copy of the name
					enum_name = f_lex->GetText();
					enum_value = "";
					tok = f_lex->GetToken();
					if(tok == '=') {
						// got a value (we do not support expressions)
						tok = f_lex->GetToken();
						if(tok == '-') {
							// a negative value!
							enum_value = "-";
							tok = f_lex->GetToken();
						}
						if(tok != Lex::TOK_IDENTIFIER && tok != Lex::TOK_VALUE) {
							f_lex->Error("expressions not supported in enumeration");
							/*NOTREACHED*/
						}
						enum_value += f_lex->GetText();
						tok = f_lex->GetToken();
					}
					e->AddEnum(enum_name, enum_value);

					if(tok == ',') {
						tok = f_lex->GetToken();
					}
					else if(tok != '}') {
						f_lex->Error("enumeration members are expected to be separated by ','");
						/*NOTREACHED*/
					}
				} while(tok != '}');

				return e;
			}

	Struct *	ReadStruct(Class *c)
			{
				std::string	field_type, field_name;
				Lex::token_t	tok;
				Struct		*s = new Struct(this, c, c);

				/* _class: structure name */
				tok = f_lex->GetToken();
				if(tok != Lex::TOK_IDENTIFIER) {
					f_lex->Error("expected a name after 'struct'");
					/*NOTREACHED*/
				}
				s->SetName(f_lex->GetText());
				tok = f_lex->GetToken();
				if(tok != '{') {
					f_lex->Error("'{' expected after the structure name");
					/*NOTREACHED*/
				}

				/* _struct_list: read the type and name of each field */
				// NOTE: we do not support empty struct's
				tok = f_lex->GetToken();
				do {
					/* _type: read the type */
					if(tok != Lex::TOK_IDENTIFIER) {
						f_lex->Error("a name was expected in the enumeration (got token %d)", tok);
						/*NOTREACHED*/
					}
					field_name = "";
					field_type = f_lex->GetText();
					for(;;) {
						/* _struct_field: read the type; expect the last identifier to be the name */
						tok = f_lex->GetToken();
						if(tok != Lex::TOK_IDENTIFIER && tok != '*') {
							break;
						}
						if(field_name.size() > 0) {
							field_type += " ";
							field_type += field_name;
						}
						if(tok == '*') {
							field_type += " *";
							field_name = "";
						}
						else {
							field_name = f_lex->GetText();
						}
					}
					if(tok != ';') {
						f_lex->Error("each field is expected to end with ';'");
						/*NOTREACHED*/
					}
					if(field_name.size() == 0) {
						f_lex->Error("found a field without a name");
						/*NOTREACHED*/
					}

					s->AddField(field_type, field_name);

					tok = f_lex->GetToken();
				} while(tok != '}');

				return s;
			}

	Func *		ReadFunc(Class *c, const char *text)
			{
				std::string	func_type, func_name, param_type, param_name;
				Lex::token_t	tok;
				Func		*f = new Func(this, c, c);
				int		depth;

				// this should not matter (i.e. the else part only would be enough!)
				if(strcmp(text, "~") == 0) {
					func_type = "~";
					func_name = "";
				}
				else {
					func_type = "";
					func_name = text;
				}

				/* _class: search the '(' -- [virtual] [TYPE] NAME '(' ... */
				for(;;) {
					tok = f_lex->GetToken();
					if(tok == Lex::TOK_EOF) {
						f_lex->Error("expected '(', not the end of the file");
						/*NOTREACHED*/
					}
					if(tok == '(') {
						break;
					}
					if(tok != Lex::TOK_IDENTIFIER && tok != '*' && tok != '&' && tok != '~' && tok != ':') {
						f_lex->Error("expected a function name (got token %d)", tok);
						/*NOTREACHED*/
					}
					if(func_name.size() > 0) {
						if(func_type.size() > 0) {
							func_type += " ";
						}
						func_type += func_name;
						if(tok == '*' || tok == '&' || tok == '~') {
							func_type += " ";
						}
					}
					if(tok == '*') {
						func_type += "*";
						func_name = "";
					}
					else if(tok == '&') {
						// we need to keep an & for when we generate the function call (c++)
						func_type += "&";
						func_name = "";
					}
					else if(tok == '~') {
						func_type += "~";
						func_name = "";
					}
					else if(tok == ':') {
						// do extra work here so we avoid the space after the scope operator
						// we expect another ':' and a name
						tok = f_lex->GetToken();
						if(tok != ':') {
							f_lex->Error("expected a scope operator (::) in a type, no lone ':' (got token %d)", tok);
							/*NOTREACHED*/
						}
						// a scope operator
						func_type += "::";
						tok = f_lex->GetToken();
						if(tok != Lex::TOK_IDENTIFIER) {
							f_lex->Error("expected an identifier after a scope operator (::) (got token %d)", tok);
							/*NOTREACHED*/
						}
						func_type += f_lex->GetText();
						func_name = "";
					}
					else {
						func_name = f_lex->GetText();
					}
				}

				f->SetName(func_name);
				f->SetType(func_type);

				/* _param_list: read the parameters up to the ')' */
				tok = f_lex->GetToken();
				for(;;) {
					if(tok == Lex::TOK_EOF) {
						break;
					}
					if(tok == ')') {
						break;
					}
					param_type = "";
					param_name = f_lex->GetText();
					for(;;) {
						tok = f_lex->GetToken();
						if(tok != Lex::TOK_IDENTIFIER && tok != '*' && tok != '&' && tok != Lex::TOK_DOTDOTDOT && tok != ':') {
							break;
						}
						if(param_name.size() > 0) {
							if(param_type.size() > 0) {
								param_type += " ";
							}
							param_type += param_name;
							if(tok == '*' || tok == '&') {
								param_type += " ";
							}
						}
						if(tok == '*') {
							param_type += "*";
							param_name = "";
						}
						else if(tok == '&') {
							// we need to keep a & for when we generate the function call
							param_type += "&";
							param_name = "";
						}
						else if(tok == ':') {
							// do extra work here so we avoid the space after the scope operator
							// we expect another ':' and a name
							tok = f_lex->GetToken();
							if(tok != ':') {
								f_lex->Error("expected a scope operator (::) in a type, no lone ':' (got token %d)", tok);
								/*NOTREACHED*/
							}
							// a scope operator
							param_type += "::";
							tok = f_lex->GetToken();
							if(tok != Lex::TOK_IDENTIFIER) {
								f_lex->Error("expected an identifier after a scope operator (::) (got token %d)", tok);
								/*NOTREACHED*/
							}
							param_type += f_lex->GetText();
							param_name = "";
						}
						else if(tok == Lex::TOK_DOTDOTDOT) {
							param_name = "...";
						}
						else {
							param_name = f_lex->GetText();
						}
					}
					// empty list but includes void
					if(param_type == "" && param_name == "void") {
						break;
					}
					f->AddParam(param_type, param_name);

					/* _assignment: skip the expression (i.e. up to the comma) */
					if(tok == '=') {
						// do not support () in expressions...
						do {
							tok = f_lex->GetToken();
						} while(tok != ',' && tok != ')' && tok != Lex::TOK_EOF);
					}

					if(tok != ',' && tok != ')') {
						break;
					}
				}
				if(tok != ')') {
					f_lex->Error("expected ')' after the last function parameter (got token %d)", tok);
					/*NOTREACHED*/
				}

				/* _class: skip anything after the ')' */
				tok = f_lex->GetToken();
				if(tok == Lex::TOK_IDENTIFIER) {
					if(strcmp(f_lex->GetText(), "const") != 0) {
						f_lex->Error("expected 'const', ';' or '{' after a function parameter list");
						/*NOTREACHED*/
					}
					tok = f_lex->GetToken();
				}

				if(tok == ';') {
					// we're done
					return f;
				}

				if(tok != '{') {
					f_lex->Error("expected 'const', ';' or '{' after a function parameter list");
					/*NOTREACHED*/
				}

				// skip the inline body
				depth = 1;
				for(;;) {
					tok = f_lex->GetToken();
					if(tok == '}') {
						--depth;
						if(depth == 0) {
							break;
						}
					}
					if(tok == '{') {
						++depth;
					}
				}

				return f;
			}

	virtual IClass *FindClassByName(const char *name)
			{
				std::vector<Class *>::iterator i = f_classes.begin();
				while(i < f_classes.end()) {
					if((*i)->GetName() == name) {
						return *i;
					}
					++i;
				}

				return 0;
			}

	void		ReadClass()
			{
				/*
				 * This function is the complicated bit of the parser.
				 *
				 * It has to read the class. The following is a simplified
				 * grammar used here to read the class. Notice that expressions
				 * are mostly "skipped" rather than parsed since we do not need
				 * to know what is going on in them.
				 *
				 * _start:
				 * 	exposed_class NAME [':' public [virtual] NAME {',' public [virtual] NAME}] '{' _class '}' ';'
				 *
				 * _class:
				 *	  LABEL ':'
				 *	| [virtual] [TYPE] [~]FUNCTION '(' [_param_list] ')' [const] _body_or_empty
				 *	| TYPE VARIABLE ';'
				 *	| enum [NAME] '{' _enum_list '}' ';'
				 *	| struct [NAME] '{' _struct_list '}' ';'
				 *
				 * _body_or_empty:
				 *	  ';'
				 *	| '{' <function body> '}'
				 *
				 * _param_list:
				 *	  _param
				 *	| _param_list ',' _param
				 *
				 * _param:
				 *	  [const] [TYPE] PARAM [_assignment]
				 *
				 * _assignment:
				 *	  '=' <expression>
				 *
				 * _enum_list:
				 *	  _enum_entry
				 *	| _enum_entries ',' _enum_entry
				 *
				 * _enum_entry:
				 *	  ENUM_TOKEN [_assignment]
				 *
				 * _struct_list:
				 *	  _struct_field
				 *	| _struct_list _struct_field
				 *
				 * _struct_field:
				 *	  _type NAME ';'
				 *
				 * _type:
				 *	  TYPE
				 *	| '*'
				 *	| _type TYPE
				 *	| _type '*'
				 *
				 * Where words in lower case not starting with '_' represent a C/C++ keyword
				 *       words in lower case starting with '_' represent the name of a rule
				 *
				 *	 NAME is a class, struct, enum, union or field name
				 *	 LABEL is one of invisible, public, protected or private
				 *	 TYPE is a valid C++ type (i.e. one or more identifiers)
				 *	 FUNCTION is the name of a function member
				 *	 VARIABLE is the name of a variable member
				 *	 PARAM is the name of a parameter
				 *	 <...> something which we skip with a sub-function
				 *	 [...] represents an optional entry
				 *	 '...' represents these very characters
				 *
				 * IMPORTANT NOTE:
				 *
				 * The input is expected to be wellformed and thus what we search is rather
				 * simpler than this grammar. The steps are described in the code directly.
				 */

				Lex::token_t	tok;
				int		depth;
				const char *	text;
				bool		got_public;

				/* _start [1]: find the exposed_class keyword (outside this function) */

				/* _start [2]: get the class name */
				tok = f_lex->GetToken();
				if(tok != Lex::TOK_IDENTIFIER) {
					/* BIG problem! */
					f_lex->Error("name expected for exposed_class");
					/*NOTREACHED*/
				}

				Class *c = new Class(this, f_lex->GetText());
				f_classes.push_back(c);

				/* _start [3]: derived classes up to the '{' */
				do {
					tok = f_lex->GetToken();
					/* sanity check */
					if(tok != ':' && tok != ',' && tok != Lex::TOK_IDENTIFIER && tok != '{') {
						f_lex->Error("invalid token (%d) between the class name and the '{'", tok);
						/*NOTREACHED*/
					}
					if(tok == Lex::TOK_IDENTIFIER) {
						text = f_lex->GetText();
						if(strcmp(text, "public") != 0
						&& strcmp(text, "protected") != 0
						&& strcmp(text, "private") != 0
						&& strcmp(text, "virtual") != 0) {
							// this is the name of a class this class derives from
							c->DeriveFrom(text);
						}
					}
				} while(tok != '{');

				got_public = false;
				do {
					/* _class [1]: search for the identifier 'public' */
					for(;;) {
						tok = f_lex->GetToken();
						if(tok == Lex::TOK_IDENTIFIER) {
							text = f_lex->GetText();
							if(strcmp(text, "public") == 0) {
								break;
							}
							if(strcmp(text, "struct") == 0) {
								// a struct can have a "... : public <name> ..."
								do {
									tok = f_lex->GetToken();
								} while(tok != '{' && tok != Lex::TOK_EOF);
							}
						}
						while(tok == '{') {
							// sub-enum/struct/class/union/function...
							depth = 1;
							do {
								tok = f_lex->GetToken();
								if(tok == Lex::TOK_EOF) {
									f_lex->Error("class \"%s\" has a mismatched set of '{' and '}'");
									/*NOTREACHED*/
								}
								if(tok == '{') {
									++depth;
								}
								else if(tok == '}') {
									--depth;
								}
							} while(depth > 0);
							tok = f_lex->GetToken();
						}
						if(tok == '}') {
							// It is actually fine to not have any members
							//if(got_public) {
							//	/* _class: found the closing '}' */
							//	// we're done!
							//	return;
							//}
							//f_lex->Error("class \"%s\" has no public members to expose", c->GetName().c_str());
							///*NOTREACHED*/
							return;
						}
					}

					/* _class: the label is expected to be followed by ':' */
					tok = f_lex->GetToken();
					if(tok != ':') {
						f_lex->Error("public was expected to be followed by ':'");
						/*NOTREACHED*/
					}

					got_public = true;

					/* _class: read the functions, sub-enum, sub-struct */
					for(;;) {
						tok = f_lex->GetToken();
						if(tok == '}' || tok == Lex::TOK_EOF) {
							/* _class: found the closure */
							break;
						}
						if(tok == Lex::TOK_IDENTIFIER) {
							text = f_lex->GetText();
							if(strcmp(text, "public") == 0) {
								tok = f_lex->GetToken();
								if(tok != ':') {
									f_lex->Error("public was expected to be followed by ':'");
									/*NOTREACHED*/
								}
								continue;
							}
							if(strcmp(text, "private") == 0
									|| strcmp(text, "invisible") == 0
									|| strcmp(text, "protected") == 0) {
								/* _class: end of 'public:' */
								tok = f_lex->GetToken();
								if(tok != ':') {
									f_lex->Error("private, invisible, protected are expected to be followed by ':'");
									/*NOTREACHED*/
								}
								break;
							}
							if(strcmp(text, "typedef") == 0) {
								/* skip those for now */
								do {
									tok = f_lex->GetToken();
								} while(tok != ';' && tok != Lex::TOK_EOF);
							}
							if(strcmp(text, "enum") == 0) {
								/* _enum_list: enumeration */
								c->AddEnum(ReadEnum());
							}
							else if(strcmp(text, "struct") == 0) {
								/* _struct_list: fields */
								c->AddStruct(ReadStruct(c));
							}
							else if(strcmp(text, "explicit") == 0) {
								tok = f_lex->GetToken();
								if(tok != Lex::TOK_IDENTIFIER) {
									f_lex->Error("explicit is expected to be followed by the constructor name");
									/*NOTREACHED*/
								}
								text = f_lex->GetText();
								c->AddFunc(ReadFunc(c, text));
							}
							else {
								c->AddFunc(ReadFunc(c, text));
							}
						}
						else if(tok == '~') {
							// destructor without virtual
							c->AddFunc(ReadFunc(c, "~"));
						}
					}
				} while(tok != '}' && tok != Lex::TOK_EOF);
			}

	void		Parse()
			{
				Lex::token_t tok;
				for(;;) {
					tok = f_lex->GetToken();
					if(tok == Lex::TOK_EOF) {
						break;
					}
					if(tok == Lex::TOK_IDENTIFIER) {
						const char *id = f_lex->GetText();
						if(strcmp(id, "exposed_class") == 0) {
							ReadClass();
						}
					}
				}

				printf("cpp-to-c: processing %ld classes, %d functions, %d structures, %d enumerations\n",
						(long) f_classes.size(),
						Func::GetCount(),
						Struct::GetCount(),
						Enum::GetCount()
					);
			}

	void		SaveClassDefs(FILE *f)
			{
				// create one struct <name>; for each class
				fprintf(f, "/* Objects */\n");
				std::vector<Class *>::iterator i = f_classes.begin();
				while(i < f_classes.end()) {
					(*i)->SaveAsStruct(f);
					++i;
				}
				fprintf(f, "\n");
			}

	void		SaveEnums(FILE *f)
			{
				fprintf(f, "/* Enumerations */\n");
				std::vector<Class *>::iterator i = f_classes.begin();
				while(i < f_classes.end()) {
					(*i)->SaveEnums(f);
					++i;
				}
				fprintf(f, "\n");
			}

	void		SaveStructs(FILE *f)
			{
				fprintf(f, "/* Structures */\n");
				std::vector<Class *>::iterator i = f_classes.begin();
				while(i < f_classes.end()) {
					(*i)->SaveStructs(f);
					++i;
				}
				fprintf(f, "\n");
			}

	void		SaveFuncDefs(FILE *f)
			{
				fprintf(f, "/* Functions */\n");
				std::vector<Class *>::iterator i = f_classes.begin();
				while(i < f_classes.end()) {
					(*i)->SaveFuncDefs(f);
					++i;
				}
				fprintf(f, "\n");
			}

	void		GenerateHeader(const char *out_filename)
			{
				FILE	*f;

				f = fopen(out_filename, "w");
				if(f == NULL) {
					fprintf(stderr, "ERROR: cannot create file \"%s\".\n", out_filename);
					exit(1);
				}

				write_warning(f, out_filename);

				/* we need size_t & va_list & int64_t */
				fprintf(f, "#include <stdlib.h>\n");
				fprintf(f, "#include <stdarg.h>\n");
				fprintf(f, "#if IRIX\n");
				fprintf(f, "#include <inttypes.h>\n");
				fprintf(f, "#endif\n");
				fprintf(f, "#include <sswf/libsswf-config.h>\n");
				fprintf(f, "\n");

				fprintf(f, "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n");

				/* some generic typedef's and such */
				fprintf(f, "/* Typedef's */\n");
				fprintf(f, "typedef signed long		sswf_ucs4_t;\n");
				fprintf(f, "typedef unsigned short		sswf_id_t;\n");
				fprintf(f, "typedef long			swf_type_t;\n");
				fprintf(f, "typedef unsigned short		sswf_frame_t;\n");
				fprintf(f, "\n");
				fprintf(f, "#ifndef	M_PI\n");
				fprintf(f, "#define	M_PI		3.14159265358979323846\n");
				fprintf(f, "#endif\n");
				fprintf(f, "\n");

				SaveClassDefs(f);
				SaveEnums(f);
				SaveStructs(f);
				SaveFuncDefs(f);

				fprintf(f, "typedef enum sswf_error_code_t (*sswf_error_manager_on_error_callback)(enum sswf_error_code_t errcode, const char *msg);\n");
				fprintf(f, "extern void sswf_error_manager_set_callback(struct SSWF_ErrorManager *error_manager, sswf_error_manager_on_error_callback callback);\n");

				fprintf(f, "#if defined(__cplusplus)\n}; // extern \"C\"\n#endif\n");

				fclose(f);
			}

	void		GenerateFuncs(FILE *f)
			{
				std::vector<Class *>::iterator i = f_classes.begin();
				while(i < f_classes.end()) {
					(*i)->GenerateFunc(f);
					++i;
				}
				fprintf(f, "\n");
			}

	void		GenerateImpl(const char *out_filename, const char *header_filename)
			{
				FILE		*f;
				const char	*e;

				f = fopen(out_filename, "w");
				if(f == NULL) {
					fprintf(stderr, "ERROR: cannot create file \"%s\".\n", out_filename);
					exit(1);
				}

				write_warning(f, out_filename);

				/* we need size_t */
				e = header_filename;
				while(*header_filename != '\0') {
					if(*header_filename == '/') {
						e = header_filename + 1;
					}
					++header_filename;
				}
				fprintf(f, "#include \"sswf/%s\"\n", e);
				fprintf(f, "#include \"sswf/libsswf.h\"\n");
				fprintf(f, "\n");

				fprintf(f, "extern \"C\"\n{\n\n");
				GenerateFuncs(f);

				fprintf(f, "void sswf_error_manager_set_callback(struct SSWF_ErrorManager *error_manager, sswf_error_manager_on_error_callback callback)\n");
				fprintf(f, "{\n");
				fprintf(f, "	static struct ErrorHandlerCallback : public sswf::ErrorManager::ErrorHandler {\n");
				fprintf(f, "		virtual sswf::ErrorManager::error_code_t OnError(sswf::ErrorManager::error_code_t errcode, const char *msg)\n");
				fprintf(f, "			{\n");
				fprintf(f, "				if(f_callback != 0) {\n");
				fprintf(f, "					return static_cast<sswf::ErrorManager::error_code_t>((*f_callback)(static_cast<enum sswf_error_code_t>(errcode), msg));\n");
				fprintf(f, "				}\n");
				fprintf(f, "				fprintf(stderr, \"sswf: error: %%d: %%s\\n\", errcode, msg);\n");
				fprintf(f, "				return errcode;\n");
				fprintf(f, "			}\n");
				fprintf(f, "		sswf_error_manager_on_error_callback f_callback;\n");
				fprintf(f, "	} error_handler;\n");
				fprintf(f, "	error_handler.f_callback = callback;\n");
				fprintf(f, "	reinterpret_cast<sswf::ErrorManager *>(error_manager)->SetErrorHandler(&error_handler);\n");
				fprintf(f, "}\n");

				fprintf(f, "\n}; // extern \"C\"\n");

				fclose(f);
			}

private:
	Lex *			f_lex;
	std::vector<Class *>	f_classes;
};

void read_header(const char *filename, const char *out_header_filename, const char *out_cpp_filename)
{
	check_cpp_filename(filename);

	if(out_header_filename == 0) {
		out_header_filename = output_filename(filename, OUTPUT_HEADER);
	}
	if(out_cpp_filename == 0) {
		out_cpp_filename = output_filename(filename, OUTPUT_CPP);
	}

	Input input(read_file(filename));
	Lex lex(filename, &input);
	Parser parser(&lex);
	parser.Parse();
	parser.GenerateHeader(out_header_filename);
	parser.GenerateImpl(out_cpp_filename, out_header_filename);
}



void usage(const char *progname)
{
	fprintf(stderr, "%s v" TO_STR(SSWF_VERSION) "\n", progname);
	fprintf(stderr, "Usage: %s [-opt] <header>\n", progname);
	fprintf(stderr, "where -opt is one or more of the following:\n");
	fprintf(stderr, "  -c <filename>    name of the C++ -> C library\n");
	fprintf(stderr, "  -h               print out this help screen\n");
	fprintf(stderr, "  -i <filename>    name of the C only header\n");
	exit(1);
}


int main(int argc, char *argv[])
{
	int		i, j, len;
	const char	*filename, *out_c, *out_i;

	filename = 0;
	out_c = 0;
	out_i = 0;
	i = 1;
	while(i < argc) {
		if(argv[i][0] == '-') {
			len = strlen(argv[i]);
			j = 1;
			while(j < len) {
				switch(argv[i][j]) {
				case 'c':
					i++;
					if(i >= argc) {
						fprintf(stderr, "ERROR: -c is expected to be followed by a filename.\n");
						exit(1);
					}
					j = len;
					out_c = argv[i];
					break;

				case 'h':
					usage(argv[0]);
					/*NOTREACHED*/
					break;

				case 'i':
					i++;
					if(i >= argc) {
						fprintf(stderr, "ERROR: -i is expected to be followed by a filename.\n");
						exit(1);
					}
					j = len;
					out_i = argv[i];
					break;

				}
				j++;
			}
		}
		else {
			// any number can be used
			read_header(argv[i], out_i, out_c);
		}
		i++;
	}

	exit(g_errcnt == 0 ? 0 : 1);
}



