// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.




#include "nel/misc/types_nl.h"
#include "nel/misc/file.h"
#include "nel/misc/config_file.h"

#include "string.h"

#include <vector>
#include <string>

using namespace std;
using namespace NLMISC;

// define macro for outputone code line
#define outLine( s )\
{\
	out = string(s);\
	fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );\
}\


// set of code
static set<string>	Codes;

struct CSkill
{
	string		SkillName;
	string		NormalizedSkillName;
	CSkill		*ParentSkillPtr;
	string		ParentSkill;
	uint16		MaxValue;
	string		Code;
	uint8		StageType;
	string		MainCategory;
	string		SecondaryCategory;
	vector<CSkill*> Children;

	CSkill() : ParentSkillPtr(NULL),MaxValue(0),StageType(0)
	{}

	CSkill(const CSkill &skill)
	{
		*this = skill;
	}

	CSkill &operator=(const CSkill &skill)
	{
		SkillName = skill.SkillName;
		NormalizedSkillName = skill.NormalizedSkillName;
		ParentSkillPtr = skill.ParentSkillPtr;
		ParentSkill = skill.ParentSkill;
		MaxValue = skill.MaxValue;
		Code = skill.Code;
		StageType = skill.StageType;
		MainCategory = skill.MainCategory;
		SecondaryCategory = skill.SecondaryCategory;
		Children = skill.Children;

		return *this;
	}

	void skillName(string name)
	{	
		SkillName = name;
/*		// fast correction to avoid strange cases when names end with a ' '
		sint i = SkillName.size();
		for ( ; i > 0; i-- )
		{
			if ( SkillName[i-1] != ' ' )
				break;
		}
		SkillName.resize( i );

		uint idxSpace;
		char c[2];
		c[0] = SkillName.substr( 0, 1).c_str()[0] - 32;
		c[1] = 0;
		NormalizedSkillName = string( c ) + SkillName.substr( 1 );
		while( ( idxSpace = NormalizedSkillName.find(" ") ) != string::npos )
		{
			string skillNameTmp = NormalizedSkillName.substr( 0, idxSpace );
			if( idxSpace < ( SkillName.size() - 1 ) )
			{
				c[0] = (*NormalizedSkillName.substr( idxSpace + 1, 1 ).c_str());
				if( c[0] >= 'a' ) c[0] -= 32;
				skillNameTmp = skillNameTmp + string( c ) + NormalizedSkillName.substr( idxSpace + 2 );
			}
			NormalizedSkillName = skillNameTmp;
		}
*/
		NormalizedSkillName = SkillName;
	}

	void buildCode()
	{
		if (ParentSkillPtr != NULL)
			Code = ParentSkillPtr->Code + Code;

		Codes.insert( Code );

		for (uint i = 0 ; i < Children.size() ; ++i)
			Children[i]->buildCode();
	}

	void writeInSheet(COFile &fo)
	{
		/*
	  <STRUCT Name="AccurateBleedingShot">
        <ATOM Name="Skill" Value="acurate bleeding shot "/>
        <ATOM Name="SkillCode" Value="none"/>
        <ATOM Name="MaxSkillValue" Value="50"/>
        <ARRAY Name="ChildSkills">
          <ATOM Name="AccurateBreathlessShot" Value="acurate breathless shot"/>
          <ATOM Name="AnimalSlideSlip" Value="animal slideslip"/>
	    </ARRAY>
	  </STRUCT>
	  */
		string out;
		out = string("        <STRUCT Name=\"")+ NormalizedSkillName + string("\">\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("          <ATOM Name=\"Skill\" Value=\"")+ SkillName + string("\"/>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("          <ATOM Name=\"SkillCode\" Value=\"")+ Code + string("\"/>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("          <ATOM Name=\"MaxSkillValue\" Value=\"")+ toString(MaxValue) + string("\"/>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("          <ATOM Name=\"Type of Stage\" Value=\"")+ toString(StageType) + string("\"/>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		
		if (ParentSkillPtr != NULL)
		{
			out = string("          <ATOM Name=\"ParentSkill\" Value=\"")+ ParentSkill + string("\"/>\n");			
		}
		else
		{
			out = string("          <ATOM Name=\"ParentSkill\" Value=\"\"/>\n");
		}
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );

		if( !Children.empty())
		{
			out = string("          <ARRAY Name=\"ChildSkills\">\n");
			fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
			for (uint i = 0 ; i < Children.size() ; ++i)
			{
				out = string("            <ATOM Name=\"") + Children[i]->NormalizedSkillName + string("\" Value=\"")+ Children[i]->SkillName + string("\"/>\n");
				fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
			}
			out = string("          </ARRAY>\n");
			fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		}
		out = string("        </STRUCT>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );

		for (uint i = 0 ; i < Children.size() ; ++i)
			Children[i]->writeInSheet(fo);
	}
};

struct CSkillTree
{
	void buildCode()
	{
		for (uint i = 0 ; i < RootSkills.size() ; ++i)
			RootSkills[i]->buildCode();
	}

	vector<CSkill*> RootSkills;
};

static CSkillTree SkillTree;
static map< string, CSkill> SkillNameToStruct;
char separators[]   = ";,";


//-----------------------------------------------
//	main
//
//-----------------------------------------------
sint main( sint argc, char ** argv )
{
	/////////////////////////////////////////////////////////////////////////////////////
	// Somes working variables
	char buffer[4096];
	
	// vector contained selector category
	vector< string > selector;

	/////////////////////////////////////////////////////////////////////////////////////
	// Check number of arguments
	if( argc < 4 )
	{
		printf("Create a file .typ for george contained a subset of skills\n\n");
		printf("SKILL_EXTRACTOR <output file> <input file skills> <[<selector> ...]\n");
		printf("	param 1 : create tree (yes) or no (no)\n");
		printf("    if output file have .typ extension, generate .typ george file format\n");
		printf("    if output file have .dfn extension, generate .dfn george file format\n");
		printf("    param 3 is sheet2 of SkillCategory.xls exported in csv format\n");
		printf("    Selector is category present in input file, selectors begins by '+' for or operation and by '.' for and operation\n");
		printf("    the first selector must be or operation (begin by '+' character)\n");
		return 1;
	}

	// parse the config file
	CConfigFile configFile;
	try
	{
		configFile.load( "skill_extractor.cfg" );
	}
	catch(const Exception &e ) 
	{
		nlwarning("<CShopTypeManager::initShopBase> skill_extractor.cfg %s",e.what());
		return 1;
	}

	//get the csv file
	CConfigFile::CVar * cfgVar = configFile.getVarPtr("CsvDir");
	if (!cfgVar)
	{
		printf("var 'CsvDir' not found in the skill_extractor.cfg");
		return 1;
	}
	const string& CSVDir = cfgVar->asString() + string("/");

	//get the path for the generated source files
	cfgVar = configFile.getVarPtr("SrcDir");
	if (!cfgVar)
	{
		printf("var 'SrcDir' not found in the skill_extractor.cfg");
		return 1;
	}
	const string& srcDir = cfgVar->asString() + string("/");

	//get the path for the generated source files
	cfgVar = configFile.getVarPtr("PdsDir");
	if (!cfgVar)
	{
		printf("var 'PdsDir' not found in the skill_extractor.cfg");
		return 1;
	}
	const string& pdsDir = cfgVar->asString() + string("/");

	//get the path for the generated dfn
	cfgVar = configFile.getVarPtr("DfnDir");
	if (!cfgVar)
	{
		printf("var 'DfnDir' not found in the skill_extractor.cfg");
		return 1;
	}
	const string& dfnDir = cfgVar->asString() + string("/");

	//get the path for the generated skill tree
	cfgVar = configFile.getVarPtr("SkillTreeDir");
	if (!cfgVar)
	{
		printf("var 'DfnDir' not found in the skill_extractor.cfg");
		return 1;
	}
	const string& treeDir = cfgVar->asString() + string("/");

	
	/////////////////////////////////////////////////////////////////////////////////////
	// Export .typ and .dfn file
	// open skill file
	CIFile f;
	if( ! f.open( CSVDir + string( argv[3] ) ) )
	{
		nlwarning( "File %s open failed", argv[3] );
		return 1;
	}
	
	// read all input file
	uint col;
	string skillName;
	char * ptr;
	map< string, CSkill>::const_iterator itSkillStruct;

	while( ! f.eof() )
	{
		f.getline( buffer, 4096 );
		col = 0;
		ptr = strtok( buffer, separators );
		CSkill skill;
		while( ptr && string( ptr ) != string(" ") )
		{			
			switch(col)
			{
			case 0: // skill name
				{
					skillName = strupr( string( ptr ) );					
					vector< string > emptyVectorOfString;
					skill.skillName(skillName);
				}
				break;
			case 1: // code
				skill.Code = toUpper(string( ptr ));
				break;		
			case 2: // parent skill
				skill.ParentSkill = toUpper(string( ptr ));
				break;
			case 3: // max skill value
				NLMISC::fromString(std::string(ptr), skill.MaxValue);
				break;
			case 4: // stage type
				NLMISC::fromString(std::string(ptr), skill.StageType);
				break;
			case 5: // main category
				skill.MainCategory = string( ptr );
				break;
			case 6: // secondary category
				skill.SecondaryCategory = string( ptr );
				break;
			default: // error ?
				break;
			};

			++col;
			ptr = strtok( 0, separators );			
		}

		if ( !skill.SkillName.empty())
		{
			// insert skill in the Map
			//pair< map< string, CSkill>::const_iterator, bool> skillInsert = SkillNameToStruct.insert( make_pair(skill.SkillName, skill) );
			//nlinfo("Insert skill %s, parent %s", skill.SkillName.c_str(), skill.ParentSkill.c_str() );
			SkillNameToStruct.insert( make_pair(skill.SkillName, skill) );
		}
	}
	f.close();

	// create the tree
	map< string, CSkill>::iterator itSkill;
	map< string, CSkill>::iterator itSkillEnd = SkillNameToStruct.end();
	uint count = 0;
	for ( itSkill = SkillNameToStruct.begin() ; itSkill != itSkillEnd ; ++itSkill )
	{
		++count;
		if ( (*itSkill).second.ParentSkill == string("NONE") || (*itSkill).second.ParentSkill.empty() )
		{
			SkillTree.RootSkills.push_back(&((*itSkill).second));
		}
		else
		{
			map< string, CSkill>::iterator its = SkillNameToStruct.find((*itSkill).second.ParentSkill);
			if (its == itSkillEnd)
			{
				nlwarning("ERROR : cannot find the parent skill %s for skill %s (skill %u)", (*itSkill).second.ParentSkill.c_str(), (*itSkill).second.SkillName.c_str(), count );
				nlstop;
			}			
			(*itSkill).second.ParentSkillPtr = &((*its).second);
			(*its).second.Children.push_back(&((*itSkill).second));
		}
	}

	SkillTree.buildCode();

	COFile fo;
	// create the skill tree if first param == yes
	if ( string( argv[1] ) == string("yes") )
	{
		if( ! fo.open( treeDir + string("skills.skill_tree"), false ) )
		{
			nlwarning(" Can't open file skills.skill_tree for writing");
			return 1;
		}
		
		string out("<?xml version=\"1.0\"?>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("<FORM Version=\"0.2\" State=\"modified\">\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("	<STRUCT>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("		<ARRAY Name=\"SkillData\">\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		
		for ( vector<CSkill*>::const_iterator itTree = SkillTree.RootSkills.begin() ; itTree != SkillTree.RootSkills.end() ; ++itTree)
		{
			(*itTree)->writeInSheet(fo);
		}

		out = string("		</ARRAY>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("	</STRUCT>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("</FORM>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		fo.close();

		// create the code .typ
		if( ! fo.open( string("_skillsCode.typ"), false ) )
		{
			nlwarning(" Can't open file _skillsCode.typ for writing");
			return 1;
		}
		
		out = string("<TYPE Type=\"String\" UI=\"NonEditableCombo\" Default=\"None\" Version=\"0.1\" State=\"modified\">\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("  <DEFINITION Label=\"unknown\" Value=\"unknown\"/>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		
		set<string>::const_iterator itCode;
		for ( itCode = Codes.begin() ; itCode != Codes.end() ; ++itCode )
		{
			out = string("  <DEFINITION Label=\"") +  (*itCode) + string("\" Value=\"") + (*itCode) + string("\"/>\n");
			fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		}

		out = string("</TYPE>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		fo.close();
	}

		
	// read category in command line
	for( sint i = 4; i < argc; ++i )
	{
		selector.push_back( string( argv[ i ] ) );
	}

	// generate a file containing skills and associated Code
	if( ! fo.open( string( "skill_codes.txt" ) ) )
	{
		nlwarning(" Can't open file %s for writing", "skill_codes.txt" );
		return 1;
	}
	for ( itSkill = SkillNameToStruct.begin() ; itSkill != itSkillEnd ; ++itSkill)
	{
		string out;
		string space;
		if ( (*itSkill).second.NormalizedSkillName.size() < 50)
			space.resize( 50 - (*itSkill).second.NormalizedSkillName.size(), ' ' );
		outLine((*itSkill).second.NormalizedSkillName + space + string("\t") + (*itSkill).second.Code + string("\n") );
	}


	// generate .typ or .dfn file	
	if( ! fo.open( dfnDir + string( argv[2] ), false ) )
	{
		nlwarning(" Can't open file %s for writing", argv[2] );
		return 1;
	}

	// output header of .typ or .dfn file
	string out("<?xml version=\"1.0\"?>\n");
	fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
	if( string( argv[2] ).find(".typ") != string::npos )
	{
		out = string("<TYPE Type=\"String\" UI=\"NonEditableCombo\" Default=\"unknown\" Version=\"0.1\" State=\"modified\">\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		out = string("  <DEFINITION Label=\"unknown\" Value=\"unknown\"/>\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
	}
	else
	{
		out = string("<DFN Version=\"0.0\" State=\"modified\">\n");
		fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
	}

	// parse all skills to export selected ones
	for ( itSkillStruct = SkillNameToStruct.begin() ; itSkillStruct != SkillNameToStruct.end() ; ++itSkillStruct )
	{
		bool selected = false;
		bool found = false;

		const CSkill &skill = (*itSkillStruct).second;

		for( vector< string >::iterator its = selector.begin(); its != selector.end(); ++its )
		{			
			found = false;

			if ( skill.MainCategory == (*its).substr(1) )
			{
				found = true;
			}
			else if ( skill.SecondaryCategory == (*its).substr(1) )
			{
				found = true;
			}
		
			if( found )
			{
				if( (*its).substr( 0, 1) == string("+") ) // or operation
				{
					selected = true;
				}
				else if( (*its).substr( 0, 1) == string(".") ) // and operation
				{
					selected &= true;
				}
			}
			else if( (*its).substr( 0, 1) == string(".") )
			{
				selected = false;
			}
		}

		if( selected )
		{
			if( string( argv[2] ).find(".typ") != string::npos )
			{
				out = string("  <DEFINITION Label=\"") + skill.SkillName + string("\" Value=\"") + skill.NormalizedSkillName + string("\"/>\n");
			}
			else
			{
				out = string("  <ELEMENT Name=\"") + skill.NormalizedSkillName + string("\" Type=\"Type\" Filename=\"creature_stat.typ\"/>\n");
			}
			fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
		}
	}

	if( string( argv[2] ).find(".typ") != string::npos )
	{
		out = string("</TYPE>\n");
	}
	else
	{
		out = string("</DFN>\n");
	}
	fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
	fo.close();

	/////////////////////////////////////////////////////////////////////////////////////
	// Generate skill.cpp and skill.h code file
	

	// output begin skill.h file
	//if( ! fo.open( string( "r:/code/ryzom/src_v2/game_share/skills.h" ) ) )
	if( ! fo.open( srcDir + string( "skills.h" ) ) )
	{
		nlwarning(" Can't open file %s for writing", "skills.h" );
		return 1;
	}

	// write header of header file
	outLine("/** \\file skills.h\n");
	outLine(" * skills enumeration: generated by skill extractor program\n");
	outLine(" *\n");
	outLine(" */\n");
	outLine("\n");
	outLine("#ifndef RY_SKILLS_H\n");
	outLine("#define RY_SKILLS_H\n");
	outLine("\n");
	outLine("#include \"nel/misc/types_nl.h\"\n");
	outLine("\n");
	outLine("#include <string.h>\n");
	outLine("\n");
	outLine( string("// NbSkills in enum : ") + toString( SkillNameToStruct.size() ) + string(" Report this in database.xml \n\n") );
	outLine("namespace SKILLS\n");
	outLine("{\n");
	outLine("	enum ESkills\n");
	outLine("	{\n");
	
	itSkill = SkillNameToStruct.begin();
	if (itSkill != itSkillEnd)
	{
		outLine(string("		") + (*itSkill).second.NormalizedSkillName + string(" = 0,\n") );
		for ( ++itSkill; itSkill != itSkillEnd ; ++itSkill)
		{
			outLine(string("		") + (*itSkill).second.NormalizedSkillName + string(",\n") );
		}
	}
	// output end skill enum and skill type enum and skill api
	outLine("\n");
	outLine("		NUM_SKILLS,\n");
	outLine("		unknown,\n");
	outLine("	};\n");
	outLine("\n");

	// output all skills
/*	for( it = skillsAndSelector.begin(); it != skillsAndSelector.end(); ++it )
	{
		uint idxSpace;
		out = (*it).first;
		while( ( idxSpace = out.find(" ") ) != string::npos )
		{
			string tmp = out.substr( 0, idxSpace );
			if( idxSpace < ( out.size() - 1 ) )
			{
				tmp = tmp + string("_") + out.substr( idxSpace + 1 );
			}
			out = tmp;
		}
		outLine( string("		") + out + string(",\n") );
	}

	// output end skill enum and skill type enum and skill api
	outLine("\n");
	outLine("		NUM_SKILLS,\n");
	outLine("		unknown\n");
	outLine("	};\n");
	outLine("\n");

	outLine("	enum ESkillType\n");
	outLine("	{\n");
	outLine("		skill,\n");
	outLine("		specialized_skill,\n");
	outLine("		training_characteristic,\n");
	outLine("		training_resist,\n");
	outLine("		training_score,\n");
	outLine("\n");
	outLine("		unknown_skill_type\n");
	outLine("	};\n");
	outLine("\n");
*/
	outLine("	/**\n");
	outLine("	 * get the right skill enum from the input string\n");
	outLine("	 * \\param str the input string\n");
	outLine("	 * \\return the ESkills associated to this string (Unknown if the string cannot be interpreted)\n");
	outLine("	 */\n");
	outLine("	ESkills	toSkill ( const std::string &str );\n");
	outLine("\n");
	outLine("	/**\n");
	outLine("	 * get the right skill string from the gived enum\n");
	outLine("	 * \\param skill the skill to convert\n");
	outLine("	 * \\return the string associated to this enum number (Unknown if the enum number not exist)\n");
	outLine("	 */\n");
	outLine("	const std::string& toString( uint16 skill );\n");
	outLine("\n");
	outLine("	/**\n");
	outLine("	 * get the skill category name\n");
	outLine("	 * \\param s is the enum number\n");
	outLine("	 * \\return the string name of skill type (Unknown if the enum number not exist)\n");
	outLine("	 */\n");
	outLine("	const std::string& getSkillCategoryName( uint16 s );\n");
	outLine("\n");
	outLine("}; // SKILLS\n");
	outLine("\n");
	outLine("#endif // RY_SKILLS_H\n");
	outLine("/* End of skills.h */\n");

	/////////////////////////////////////////////////////////////////////////////////////
	// begin output skill.cpp file
//	if( ! fo.open( string( "r:/code/ryzom/src_v2/game_share/skills.cpp" ) ) )
	if( ! fo.open( srcDir + string( "skills.cpp" ) ) )
	{
		nlwarning(" Can't open file skills.cpp for writing");
		return 1;
	}

	outLine("/** \\file skills.cpp\n");
	outLine(" * \n");
	outLine(" */\n\n");

	outLine("#include \"stdpch.h\"\n");
	outLine("\n");
	outLine("#include \"nel/misc/debug.h\"\n");
	outLine("#include \"skills.h\"\n");
	outLine("#include \"nel/misc/string_conversion.h\"\n");
	outLine("\n");
	outLine("using namespace std;\n");
	outLine("using namespace NLMISC;\n");
	outLine("\n");
	outLine("namespace SKILLS\n");
	outLine("{\n");
	outLine("\n");
	outLine("static string UnknownString(\"Unknown\");\n");
	outLine("\n");
	outLine("\tNL_BEGIN_STRING_CONVERSION_TABLE (ESkills)\n");

	// parser all skills and init the conversion map
	for ( itSkill = SkillNameToStruct.begin() ; itSkill != itSkillEnd ; ++itSkill)
	{
		outLine (string ("\t  NL_STRING_CONVERSION_TABLE_ENTRY(") + (*itSkill).second.NormalizedSkillName + string(")\n") );
	}
	//outLine (string ("	{ \"unknown\", unknown },\n" ) );
	outLine (string("\t  NL_STRING_CONVERSION_TABLE_ENTRY(unknown)\n") );
	
	outLine("\tNL_END_STRING_CONVERSION_TABLE(ESkills, SkillsConversion, unknown)\n");
	outLine("\n");
	outLine("\n");
	outLine("\tESkills toSkill( const std::string &str )\n");
	outLine("\t{\n");
	outLine("\t	return SkillsConversion.fromString(str);\n");
	outLine("\t}\n");
	outLine("\n");
	outLine("\tconst std::string& toString( uint16 skill )\n");
	outLine("\t{\n");
	outLine("\t	return SkillsConversion.toString((ESkills)skill);\n");
	outLine("\t}\n");
	outLine("\n");
	outLine("\n");
	outLine("\tconst std::string& getSkillCategoryName( uint16 s )\n");
	outLine("\t{\n");
	/*outLine("	if( s < sizeof(SkillCategoryStrings)/sizeof(SkillCategoryStrings[0]) )\n");
	outLine("	{\n");
	outLine("		return SkillCategoryStrings[ s ];\n");
	outLine("	}\n");
	outLine("	else return UnknownString;\n");
	*/
	outLine("\t	return UnknownString;\n");
	outLine("\t}\n");
	outLine("\n");
	outLine("}; // SKILLS\n");
	fo.close();

	/////////////////////////////////////////////////////////////////////////////////////
	// Generate skills.pds script file
	
	if( ! fo.open( pdsDir + string( "skills.pds" ) ) )
	{
		nlwarning(" Can't open file %s for writing", "skills.pds" );
		return 1;
	}
	
	outLine( string("// NbSkills in enum : ") + toString( SkillNameToStruct.size() ) + string(" Report this in database.xml \n\n") );
	outLine("file \"skills.h\"\n");
	outLine("{\n");
	outLine("\tenum TSkill\n");
	outLine("\t{\n");
	outLine("\t\tBeginSkill\n");
	outLine("\t\t{\n");

	itSkill = SkillNameToStruct.begin();
	if (itSkill != itSkillEnd)
	{
		outLine(string("\t\t\t") + (*itSkill).second.NormalizedSkillName);
		for ( ++itSkill; itSkill != itSkillEnd ; ++itSkill)
		{
			outLine(",\n");
			outLine(string("\t\t\t") + (*itSkill).second.NormalizedSkillName);
		}
	}
	outLine("\n\t\t}\n");
	outLine("\t} EndSkill\n");
	outLine("}\n");
	fo.close();

	nlinfo("job finish");
	return EXIT_SUCCESS;
}