#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

// using declarations
using namespace std;

// definitions
static const string OUTPUT_PATH = "..\\..\\CryEngine\\CryCommon\\Typelist.h";
static const int MAX_ELEMENTS = 25;

// helper classes
class ParameterList
{
public:
	ParameterList( const char * seperator, const char * prefix, const char * postfix, int first, int last, bool count )
		: m_seperator(seperator), m_prefix(prefix), m_postfix(postfix), m_first(first), m_last(last), m_count(count)
	{
	}

	friend ostream& operator<<( ostream& output, const ParameterList& pl )
	{
		for (int i=pl.m_first; i != pl.m_last; i++)
		{
			if (i != pl.m_first)
				output << pl.m_seperator;
			output << pl.m_prefix;
			if (pl.m_count)
				output << i;
			output << pl.m_postfix;
		}
		return output;
	}

private:
	const char * m_seperator;
	const char * m_prefix;
	const char * m_postfix;
	int m_first;
	int m_last;
	bool m_count;
};

class Indent
{
public:
	Indent( int n = 1 ) : m_count(n) {}

	friend ostream& operator<<( ostream& output, const Indent& indent )
	{
		for (int i=0; i<indent.m_count; i++)
			output << '\t';
		return output;
	}

private:
	int m_count;
};

// main routine
int main(void)
{
	// open output file, and check for success
	ofstream output(OUTPUT_PATH.c_str());
	if (!output)
	{
		cerr << "Unable to open output file: " << OUTPUT_PATH << endl;
		return 1;
	}
	else
	{
		cout << "Writing definitions for MAX_ELEMENTS=" << MAX_ELEMENTS 
			<< " to " << OUTPUT_PATH << endl;
	}

	// write prolog
	output 
		<< "// Automatically generated file: see " << __FILE__ << endl
		<< "#ifndef __TYPELIST_H__" << endl
		<< "#define __TYPELIST_H__" << endl
		<< endl
		<< "#pragma once" << endl
		<< endl
		<< "namespace NTypelist" << endl
		<< "{" << endl
		<< endl;

	// declare none type
	output
		<< Indent(1) << "class CEnd {};" << endl
		<< endl;

	// declare basic node type
	output
		<< Indent(1) << "template <class H, class T>" << endl
		<< Indent(1) << "class CNode" << endl
		<< Indent(1) << "{" << endl
		<< Indent(1) << "public:" << endl
		<< Indent(2) << "typedef H THead;" << endl
		<< Indent(2) << "typedef T TTail;" << endl
		<< Indent(1) << "};" << endl
		<< endl;

	// declare template class
	output
		<< Indent(1) << "template <" << ParameterList(", ", "class T", " = CEnd", 0, MAX_ELEMENTS+1, true) << ">" << endl
		<< Indent(1) << "class CConstruct;" << endl
		<< endl;

	// now declare template specializations
	for (int i=0; i<=MAX_ELEMENTS; i++)
	{
		// class header
		output
			<< Indent(1) << "template <" << ParameterList(", ", "class T", "", 0, i, true) << ">" << endl
			<< Indent(1) << "class CConstruct<" 
			<< ParameterList(", ", "T", "", 0, i, true);
		if (i != 0 && i != MAX_ELEMENTS)
			output << ", ";
		output
			<< ParameterList(", ", "CEnd", "", i, MAX_ELEMENTS, false) << ">" << endl;

		// class body
		output 
			<< Indent(1) << "{" << endl
			<< Indent(1) << "public:" << endl;
		if (i)
		{
			output
				<< Indent(2) << "typedef CNode<T0, typename CConstruct<"
				<< ParameterList(", ", "T", "", 1, i, true)
				<< ">::TType> TType;" << endl;
		}
		else
		{
			output
				<< Indent(2) << "typedef CEnd TType;" << endl;
		}
		output
			<< Indent(1) << "};" << endl
			<< endl;
	}

	// write epilog
	output
		<< "}" << endl
		<< endl
		<< "#define __INCLUDING_FROM_TYPELIST_H__" << endl
		<< "#include \"TypelistUtils.h\"" << endl
		<< "#undef __INCLUDING_FROM_TYPELIST_H__" << endl
		<< endl
		<< "#endif // __TYPELIST_H__" << endl;

	// we're done
	return 0;
}
