#ifdef __APPLE__ 
#include "definitions_mac.h" 
#else 
#include "definitions.h" 
#endif 

 
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cmath>
#include "cardinfo.h"
#include "settings.h"
#include "controls.h"
#include "globals.h"
#include "binreloc.h"

using namespace std;

#define NUM_FILES_CHK 7
#define NUM_SUBDIRS_CHK 5

extern bool verbose_output;

//#define SPEW_ON

inline bool gt_rect (SDL_Rect r1, SDL_Rect r2) { return ((r1.w*r1.h) < (r2.w*r2.h)); }
inline bool eq_rect (SDL_Rect r1, SDL_Rect r2) { return ((r1.w == r2.w) && (r1.h == r2.h)); }

SETTINGS::SETTINGS()
{
	// Enable/disable spewing debug output
	#ifdef SPEW_ON
	spew = true;
	#endif

	// Get user's home directory
	FindHomeDir();

	// Figure out where the settings are to be
	settings_directory = SETTINGS_DIR;

#ifndef _WIN32
	settings_path = home_directory + settings_directory;
#else
	if(home_directory == "data/")
	{
		settings_path = home_directory + "settings";
	}
	else
	{
		settings_path = home_directory + settings_directory;    
	}
#endif

#ifndef _WIN32
	data_directory = DATA_DIR;
#else
	data_directory = "data";
#endif

/*
	cout << "**********Home dir: " << home_directory << endl;
	cout << "**********Settings dir: " << settings_directory << endl;
	cout << "**********Data dir: " << data_directory << endl;
*/
#ifdef ENABLE_BINRELOC
	BrInitError br_err;
	int br_ret = br_init( &br_err );
	if( !br_ret )
	{
		switch( br_err )
		{
		case BR_INIT_ERROR_NOMEM:
			cout << "BinReloc: Can't allocate memory." << endl;
			break;
		case BR_INIT_ERROR_OPEN_MAPS:
			cout << "BinReloc: Unable to open /proc/self/maps; see errno for details.." << endl;
			break;
		case BR_INIT_ERROR_READ_MAPS:
			cout << "BinReloc: Unable to read from /proc/self/maps; see errno for details." << endl;
			break;
		case BR_INIT_ERROR_INVALID_MAPS:
			cout << "BinReloc: The file format of /proc/self/maps is invalid; kernel bug?" << endl;
			break;
		case BR_INIT_ERROR_DISABLED:
			cout << "BinReloc: BinReloc is disabled." << endl;
			break;
		default:
			cout << "BinReloc: Unknown error." << endl;
			break;
		}
		cout << "BinReloc failed, using hardcoded paths." << endl;
	}
	else
	{
		cout << "BinReloc successfully initialized." << endl;
		cout << "Executable path: " << br_find_exe( "/usr/share/games/vdrift/bin" ) << endl;
		cout << "Data dir: " << br_find_prefix( "/usr/share/games/vdrift" );
		cout << "/data" << endl;
		data_directory = br_find_prefix( "/usr/share/games/vdrift" );
		#ifdef ENABLE_NLS
		cout << "Localedir: " << br_find_locale_dir( "/usr/share/locale" ) << endl;
		locale_directory = br_find_locale_dir( "/usr/share/locale" );
		#endif
		data_directory += "/data";
	}
#else
#ifdef ENABLE_NLS
	locale_directory = LOCALEDIR;
#endif
#endif

	// Read saved settings
	configfile.Load((settings_path + "/VDrift.config").c_str());
	ParseSettings(configfile);

	string dconfig_filename = "";
	bool found_file = false;
	int chance = 0;

	do
	{
		dconfig_filename = data_directory + "/settings/VDrift.config";

		if( !FileExists( dconfig_filename.c_str() ) )
		{
		
			cout << dconfig_filename;
			cout << " not found.";
			switch( chance )
			{
			case 0:
#ifdef ENABLE_BINRELOC
				data_directory = br_find_exe_dir( "/usr/share/games/vdrift/bin" );
				data_directory += "/data";
#else
				data_directory = "./data";
#endif
				break;
			case 1:
#ifdef ENABLE_BINRELOC
				data_directory = br_find_prefix( "/usr/share/games/vdrift" );
				data_directory += "/data";
#else
				data_directory = "../data";
#endif
				break;
			default:
			case 2:
				cout << " Can't find data in directory with executable, or in parent dir. Giving up." << endl;
				exit(1);
				break;
			}
			cout << " Attempting to load data from " << data_directory << endl;
			chance++;
		}
		else
		{
			found_file = true;
		}
	}
	while( found_file = false );

	default_configfile.Load( ( data_directory + "/settings/VDrift.config" ).c_str() );
	options_configfile.Load( ( data_directory + "/settings/options.config" ).c_str() );

	Init();

	configfile.Load((settings_path + "/VDrift.config").c_str());
	ParseSettings(configfile);

	cout << "Version of game: " << VERSION << endl;
/*
	if(!DirExists(data_directory))
	{
		cout << endl;
		cout << "WARNING!! Could not find the VDrift data directory. This is currently set to:" << endl;
		cout << data_directory << endl;;
		cout << "You may change this by editing " + settings_path + "/VDrift.config or by running vdrift thus:" << endl;
		cout << "vdrift -datadir /path/to/vdrift/data" << endl;
		cout << "Continuing to run, probably won't work..." << endl << endl;
	}
*/
}

SETTINGS::~SETTINGS()
{
	// save the config file
	configfile.Write(true);
	configfile.Clear();
	default_configfile.Clear();
	car_parts.Clear();
	parts_list.clear();
	float_options.clear();
	int_options.clear();
	bool_options.clear();
	string_options.clear();
}

void SETTINGS::Init()
{
	//bool write_defaults = false;
	//spew = verbose_output;
	#ifdef SPEW_ON
	spew = true;
	#endif
	vector<string> files;
	files.push_back( "controls" );
	files.push_back( "VDrift.config" );
	/*
	files.push_back( "carsettings/CS" );
	files.push_back( "carsettings/XS" );
	files.push_back( "carsettings/GT" );
	files.push_back( "carsettings/XSR" );
	files.push_back( "carsettings/XM" );
	*/
	vector<string> subdirs;
	subdirs.push_back( "replays/" );
	subdirs.push_back( "screenshots/" );
	subdirs.push_back( "logs/" );
	//subdirs.push_back( "carsettings/" );
	subdirs.push_back( "userdata/" );
 
	// Check for settings dir and create if non-existant (I think that a directory cannot be opened as a file)
	if(!FileExists(settings_path + "/VDrift.config"))
	{
		// Whine
		cout << "Can't find the settings directory at \"" + settings_path + "\". Making a new one..." << endl;
		// Make it
		CreateDir(settings_path);

		// Copy file
		default_configfile.Write( true, settings_path + "/VDrift.config" );
	}

	// Check for subdirs
	for( unsigned int i = 0; i < subdirs.size(); i++ )
	{
		CreateDir(settings_path + "/" + subdirs[i]);
	}

	// Check for files
	for( unsigned int i = 0; i < files.size(); i++ )
	{
		if( !FileExists( settings_path + "/" + files[i] ) )
		{
			cout << "Missing " + settings_path + "/" + files[i] + " file, copying." << endl;
			utility.FileCopy( data_directory + "/settings/" + files[i], settings_path + "/" + files[i] );
		}
		else
		{
			cout << "Found config file " + settings_path + "/" + files[i] + "." << endl;
		}
	}
/*
	if( !FileExists( settings_path + "/controls" ) )
	{
		keyman.Load();
		gamecontrols.LoadControls( data_directory + "/settings/controls" );
		gamecontrols.WriteControlFile( settings_path + "/controls" );
	}
*/
	files.clear();
	subdirs.clear();

}

void SETTINGS::ParseOptionFile()
{
	//spew = verbose_output;
	#ifdef SPEW_ON
	spew = true;
	#endif
	// fill in the options lists
	int num_options = 0;
	options_configfile.GetParam( ".num_options", num_options );
	for( int i = 0; i < num_options; i++ )
	{
		ostringstream option_id;
		option_id << "option-" << setfill( '0' ) << setw( 2 ) << i;
		string opt_id = option_id.str();
		string cat, name, title, desc, type;
		
		if( !options_configfile.GetParam( opt_id + ".cat", cat ) )
		{
			cout << "Error: " << opt_id << " is missing its category." << endl;
			cat = "";
		}

		if( !options_configfile.GetParam( opt_id + ".name", name ) )
		{
			cout << "Error: " << opt_id << " is missing its name." << endl;
			name = "";
		}

		if( !options_configfile.GetParam( opt_id + ".title", title ) )
		{
			cout << "Error: " << opt_id << " is missing its title." << endl;
			cat = "";
		}

		if( !options_configfile.GetParam( opt_id + ".desc", desc ) )
		{
			cout << "Error: " << opt_id << " is missing its description." << endl;
			cat = "";
		}

		if( !options_configfile.GetParam( opt_id + ".type", type ) )
		{
			cout << "Error: " << opt_id << " is missing its type." << endl;
			cat = "";
		}

		if( type == "int" )
		{
			OPTION<int> opt;
			string val_type = "";
			int default_val = 0;

			opt.SetCat( cat );
			opt.SetName( name );
			opt.SetTitle( title );
			opt.SetDesc( desc );

			if( !options_configfile.GetParam( opt_id + ".default", default_val ) )
			{
				cout << "Warning: " << opt_id << " is missing its default value. assuming 0" << endl;
				default_val = 0;
			}
			opt.SetDefaultValue( default_val );

			if( !options_configfile.GetParam( opt_id + ".values", val_type ) )
			{
				cout << "Error: " << opt_id << " is missing its values type." << endl;
			}

			if( val_type == "list" )
			{
				int num_vals = 0;
				vector<string> opts;
				vector<int> vals;
				if( !options_configfile.GetParam( opt_id + ".num_vals", num_vals ) )
				{
					cout << "Error: " << opt_id << " is missing its values list number." << endl;
				}
				for( int j = 0; j < num_vals; j++ )
				{
					string opt = "";
					int val = 0;
					ostringstream value_id_num;
					value_id_num << setfill( '0' ) << setw( 2 ) << j;
					string v_id = opt_id + ".val" + value_id_num.str();
					string o_id = opt_id + ".opt" + value_id_num.str();

					if( !options_configfile.GetParam( v_id, val ) )
					{
						cout << "Error: " << v_id << " is missing." << endl;
					}
					if( !options_configfile.GetParam( o_id, opt ) )
					{
						cout << "Error: " << o_id << " is missing." << endl;
					}
					opts.push_back( opt );
					vals.push_back( val );
				}
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "car_paints" )
			{
			/*
				list<string> carlist;
				string folderpath = data_directory + "/cars";
				utility.GetFolderIndex(folderpath, carlist);
				for( list<string>::iterator i = carlist.begin(); i != carlist.end(); ++i )
				{
					// create a list of paints for each car...
					string car_about_filename = data_directory + "/cars/" + *i + "/about.txt";
					if( utility.FileExists( car_about_filename ) )
					{
						ifstream car_about_file;
						car_about_file.open( car_about_filename.c_str() );
						string car_name = utility.sGetLine( car_about_file );
						vals.push_back( *i );
						opts.push_back( car_name );
					}
				}
			*/
			}
			else if( val_type == "resolution_widths" )
			{
				/*
				 * this option is processed later in ParseDynamicOptions()
				 */
				vector<string> opts;
				vector<int> vals;
				opts.push_back( "" );
				vals.push_back( 0 );
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "resolution_heights" )
			{
				/*
				 * this option is processed later in ParseDynamicOptions()
				 */
				vector<string> opts;
				vector<int> vals;
				opts.push_back( "" );
				vals.push_back( 0 );
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "joy_indeces" )
			{
				/*
				 * this option is processed later in ParseDynamicOptions()
				 */
				vector<string> opts;
				vector<int> vals;
				opts.push_back( "X" );
				vals.push_back( 0 );
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "anisotropy" )
			{
				/*
				 * this option is processed later in ParseDynamicOptions()
				 */
				vector<string> opts;
				vector<int> vals;
				opts.push_back( "" );
				vals.push_back( 0 );
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "antialiasing" )
			{
				/*
				 * this option is processed later in ParseDynamicOptions()
				 */
				vector<string> opts;
				vector<int> vals;
				opts.push_back( "" );
				vals.push_back( 0 );
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else
			{
				// Item has no values. This could be a problem but error is probably scary for people. TODO: find a better solution besides commenting out
				//cout << "Error: " << opt_id << " bad or empty values type! this option will not have values." << endl;
			}

			int_options.push_back( opt );
		}
		else if( type == "bool" )
		{
			OPTION<bool> opt;
			string val_type = "";
			bool default_val = false;
			string true_val, false_val;

			opt.SetCat( cat );
			opt.SetName( name );
			opt.SetTitle( title );
			opt.SetDesc( desc );

			if( !options_configfile.GetParam( opt_id + ".default", default_val ) )
			{
				cout << "Warning: " << opt_id << " is missing its default value. Assuming false." << endl;
			}
			opt.SetDefaultValue( default_val );

			if( !options_configfile.GetParam( opt_id + ".values", val_type ) )
			{
				cout << "Error: " << opt_id << " is missing its values type." << endl;
			}

			if( val_type == "bool" )
			{
				if( !options_configfile.GetParam( opt_id + ".true", true_val ) )
				{
					true_val = "True";
				}
				opt.SetTrueValue( true_val );

				if( !options_configfile.GetParam( opt_id + ".false", false_val ) )
				{
					false_val = "False";
				}
				opt.SetFalseValue( false_val );
			}
			else
			{
				// TODO: Frightens small children
				//cout << "Error: " << opt_id << " bad or empty values type! this option will not have values." << endl;
			}

			bool_options.push_back( opt );
		}
		else if( type == "string" )
		{
			OPTION<string> opt;
			string val_type = "";
			string default_val = "";

			opt.SetCat( cat );
			opt.SetName( name );
			opt.SetTitle( title );
			opt.SetDesc( desc );

			if( !options_configfile.GetParam( opt_id + ".default", default_val ) )
			{
				cout << "Warning: " << opt_id << " is missing its default value. Assuming \"\"." << endl;
			}
			opt.SetDefaultValue( default_val );

			if( !options_configfile.GetParam( opt_id + ".values", val_type ) )
			{
				cout << "Error: " << opt_id << " is missing its values type." << endl;
			}

			if( val_type == "list" )
			{
				int num_vals = 0;
				vector<string> opts;
				vector<string> vals;
				if( !options_configfile.GetParam( opt_id + ".num_vals", num_vals ) )
				{
					cout << "Error: " << opt_id << " is missing its values list number." << endl;
				}
				for( int j = 0; j < num_vals; j++ )
				{
					string opt = "";
					string val = "";
					ostringstream value_id_num;
					value_id_num << setfill( '0' ) << setw( 2 ) << j;
					string v_id = opt_id + ".val" + value_id_num.str();
					string o_id = opt_id + ".opt" + value_id_num.str();

					if( !options_configfile.GetParam( v_id, val ) )
					{
						cout << "Error: " << v_id << " is missing." << endl;
					}
					if( !options_configfile.GetParam( o_id, opt ) )
					{
						cout << "Error: " << o_id << " is missing." << endl;
					}
					opts.push_back( opt );
					vals.push_back( val );
				}
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "ip_valid" )
			{
				opt.SetAccepts( "0123456789." );
			}
			else if( val_type == "port_valid" )
			{
				opt.SetAccepts( "0123456789" );
			}
			else if( val_type == "tracks" )
			{
				vector<string> opts;
				vector<string> vals;
				list<string> tracklist;
				string folderpath = data_directory + "/tracks";
				utility.GetFolderIndex(folderpath, tracklist);
				for( list<string>::iterator i = tracklist.begin(); i != tracklist.end(); ++i )
				{
					string track_about_filename = data_directory + "/tracks/" + *i + "/about.txt";
					if( utility.FileExists( track_about_filename ) )
					{
						ifstream track_about_file;
						track_about_file.open( track_about_filename.c_str() );
						string track_name = utility.sGetLine( track_about_file );
						vals.push_back( *i );
						opts.push_back( track_name );
					}
				}
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "cars" )
			{
				vector<string> opts;
				vector<string> vals;
				list<string> carlist;
				string folderpath = data_directory + "/cars";
				utility.GetFolderIndex(folderpath, carlist);
				for( list<string>::iterator i = carlist.begin(); i != carlist.end(); ++i )
				{
					string car_about_filename = data_directory + "/cars/" + *i + "/about.txt";
					if( utility.FileExists( car_about_filename ) )
					{
						ifstream car_about_file;
						car_about_file.open( car_about_filename.c_str() );
						string car_name = utility.sGetLine( car_about_file );
						vals.push_back( *i );
						opts.push_back( car_name );
					}
				}
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "skins" )
			{
				vector<string> opts;
				vector<string> vals;
				list<string> skinlist;
				string folderpath = data_directory + "/skins";
				utility.GetFolderIndex(folderpath, skinlist);
				for( list<string>::iterator i = skinlist.begin(); i != skinlist.end(); ++i )
				{
					string skin_menus_dir = data_directory + "/skins/" + *i + "/menus";
					if( utility.DirExists( skin_menus_dir ) )
					{
						vals.push_back( *i );
						opts.push_back( *i );
					}
					else
					{
						cout << "Directory " << skin_menus_dir << " does not exist! Skin " << *i << " not loaded." << endl;
					}
				}
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "replays" )
			{
				/*
				 * this option is processed later in ParseDynamicOptions()
				 */
				vector<string> opts;
				vector<string> vals;
				opts.push_back( "None" );
				vals.push_back( "" );
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else
			{
				// FIXME would you put your mother through this?
				//cout << "Error: " << opt_id << " bad or empty values type! this option will not have values." << endl;
			}

			string_options.push_back( opt );
		}
		else if( type == "float" )
		{
			OPTION<float> opt;
			string val_type = "";
			float default_val = 0;

			opt.SetCat( cat );
			opt.SetName( name );
			opt.SetTitle( title );
			opt.SetDesc( desc );

			if( !options_configfile.GetParam( opt_id + ".default", default_val ) )
			{
				cout << "Error: " << opt_id << " is missing its default value." << endl;
			}
			opt.SetDefaultValue( default_val );

			if( !options_configfile.GetParam( opt_id + ".values", val_type ) )
			{
				cout << "Error: " << opt_id << " is missing its values type." << endl;
			}

			if( val_type == "list" )
			{
				int num_vals = 0;
				vector<string> opts;
				vector<float> vals;
				if( !options_configfile.GetParam( opt_id + ".num_vals", num_vals ) )
				{
					cout << "Error: " << opt_id << " is missing its values list number." << endl;
				}
				for( int j = 0; j < num_vals; j++ )
				{
					string opt = "";
					float val = 0.0;
					ostringstream value_id_num;
					value_id_num << setfill( '0' ) << setw( 2 ) << j;
					string v_id = opt_id + ".val" + value_id_num.str();
					string o_id = opt_id + ".opt" + value_id_num.str();

					if( !options_configfile.GetParam( v_id, val ) )
					{
						cout << "Error: " << v_id << " is missing." << endl;
					}
					if( !options_configfile.GetParam( o_id, opt ) )
					{
						cout << "Error: " << o_id << " is missing." << endl;
					}
					opts.push_back( opt );
					vals.push_back( val );
				}
				opt.SetValueList( vals );
				opt.SetOptionList( opts );
			}
			else if( val_type == "float" )
			{
				float min_val, max_val;
				min_val = 0.0f;
				max_val = 1.0f;
				bool is_percent = true;

				options_configfile.GetParam( opt_id + ".min", min_val );
				opt.SetMinValue( min_val );
				options_configfile.GetParam( opt_id + ".max", max_val );
				opt.SetMaxValue( max_val );
				options_configfile.GetParam( opt_id + ".percent", is_percent );
				opt.SetPercent( is_percent );
			}
			else
			{
				// FIXME, for shame
				//cout << "Error: " << opt_id << " bad or empty values type! this option will not have values." << endl;
			}

			float_options.push_back( opt );
		}
		else
		{
			cout << "Error: " << opt_id <<  "bad or empty option type! this option will not be added." << endl;
		}
	}
}

void SETTINGS::ParseDynamicOptions()
{
	// generate list of valid video modes
	GetValidVideoModes();

	vector< OPTION<int> >::iterator int_opt_iter;
	vector< OPTION<string> >::iterator string_opt_iter;
	string opt_full_name;

	for( int_opt_iter = int_options.begin(); int_opt_iter != int_options.end(); ++int_opt_iter )
	{
		vector<int> vals;
		vector<string> opts;
		opt_full_name = int_opt_iter->GetCat() + "." + int_opt_iter->GetName();
	
		if( opt_full_name == "display.width" )
		{
			vector<SDL_Rect> modes = valid_display_modes.GetModes();
			vector<SDL_Rect>::iterator modes_iter;
			for( modes_iter = modes.begin(); modes_iter != modes.end(); ++modes_iter )
			{
				ostringstream wstream;
				wstream << modes_iter->w;
				string width = wstream.str();
				ostringstream hstream;
				hstream << modes_iter->h;
				string height = hstream.str();
				vals.push_back( modes_iter->w );
				opts.push_back( width + "x" + height );
			}
			int_opt_iter->SetValueList( vals );
			int_opt_iter->SetOptionList( opts );
		}
		else if( opt_full_name == "display.height" )
		{
			vector<SDL_Rect> modes = valid_display_modes.GetModes();
			vector<SDL_Rect>::iterator modes_iter;
			for( modes_iter = modes.begin(); modes_iter != modes.end(); ++modes_iter )
			{
				ostringstream wstream;
				wstream << modes_iter->h;
				string width = wstream.str();
				vals.push_back( modes_iter->h );
				opts.push_back( width );
			}
			int_opt_iter->SetValueList( vals );
			int_opt_iter->SetOptionList( opts );
		}
		else if( opt_full_name == "joystick.selected_index" )
		{
			int num_joysticks = SDL_NumJoysticks();
			for( int i = 0; i < num_joysticks; i++ )
			{
				ostringstream j;
				j << "Joystick ";
				j << i;
				opts.push_back( j.str() );
				vals.push_back( i );
			}
			int_opt_iter->SetValueList( vals );
			int_opt_iter->SetOptionList( opts );
		}
		else if( opt_full_name == "display.anisotropic" )
		{
			int card_max_anisotropy = gfxcard.GetCapability(CARDINFOTYPE::MAXANISOTROPY);
			int a = 0;
			for( int i = -1; a < card_max_anisotropy; ++i )
			{
				a = ( i < 1 ) ? i + 1 : int( pow( 2.0, i ) );
				ostringstream al;
				if( a == 0 )
				{
					al << "Off";
				}
				else
				{
					al << a;
					al << "x";
				}
				opts.push_back( al.str() );
				vals.push_back( a );
			}
			int_opt_iter->SetValueList( vals );
			int_opt_iter->SetOptionList( opts );
		}
		else if( opt_full_name == "display.antialiasing" )
		{
			if( gfxcard.GetCapability( CARDINFOTYPE::ANTIALIASING ) == 1 )
			{
				int card_max_antialiasing = 4;  // TODO: find a way to detect this?
				int a = 0;
				for( int i = 0; a < card_max_antialiasing; ++i )
				{
					a = ( i == 0 ) ? i : int( pow( 2.0, i ) );
					ostringstream al;
					if( a == 0 )
					{
						al << "Off";
					}
					else
					{
						al << a;
						al << "x";
					}
					opts.push_back( al.str() );
					vals.push_back( a );
				}
			}
			else
			{
				opts.push_back( "Off" );
				vals.push_back( 0 );
			}
			int_opt_iter->SetValueList( vals );
			int_opt_iter->SetOptionList( opts );

		}
	}

	for( string_opt_iter = string_options.begin(); string_opt_iter != string_options.end(); ++string_opt_iter )
	{
		vector<string> vals;
		vector<string> opts;
		opt_full_name = string_opt_iter->GetCat() + "." + string_opt_iter->GetName();
	
		if( opt_full_name == "game.selected_replay" )
		{
			int n_replays = 0;
			Get( "game.num_replays", n_replays );
			for( int i = 1; i <= n_replays; ++i )
			{
				std::ostringstream s;
				s << settings_path + "/replays/";
				s << i;
				s << ".vdr";
				string replay_filename = s.str();
				if( utility.FileExists( replay_filename ) )
				{
					// here it'd be nice to load some better info about the replay
					s.str("");
				        s << i;
					vals.push_back( s.str() );
					opts.push_back( s.str() );
				}
			}
			string_opt_iter->SetValueList( vals );
			string_opt_iter->SetOptionList( opts );
		}
	}
}

void SETTINGS::GetValidVideoModes()
{
	int videoFlags = SDL_OPENGL;       /* Enable OpenGL in SDL */
	videoFlags |= SDL_GL_DOUBLEBUFFER; /* Enable double buffering */
	videoFlags |= SDL_HWPALETTE;       /* Store the palette in hardware */
	videoFlags |= SDL_RESIZABLE;       /* Enable window resizing */
	videoFlags |= SDL_HWSURFACE|SDL_ANYFORMAT|SDL_FULLSCREEN;  

	/* Get available fullscreen/hardware modes */
	SDL_Rect **tmodes = SDL_ListModes( NULL, SDL_HWSURFACE | SDL_FULLSCREEN );
	vector<SDL_Rect> modes;
	
	// put all dynamic determined modes into the vector
	for( int i = 0; tmodes[i] != NULL; i++ )
	{
		modes.push_back( *tmodes[i] );
	}

	ifstream vmf;
	vmf.open( GetFullDataPath( "lists/videomodes" ).c_str() );
	while( !vmf.eof() )
	{
		int w, h;
		vmf >> w;
		vmf >> h;

		if( !vmf.eof() )
		{
			// put all valid modes from the videomodes file into the vector
			if( SDL_VideoModeOK( w,h, 16, videoFlags ) ) 
			{			
				SDL_Rect r = {0, 0, w, h};
				modes.push_back( r );
			}
		}
	}

	// sort vector by (w*h)
	sort( modes.begin(), modes.end(), gt_rect );
	// make items in vector unique
	vector<SDL_Rect>::iterator new_end = unique( modes.begin(), modes.end(), eq_rect );
	vector<SDL_Rect> unique_modes;
	for( vector<SDL_Rect>::iterator mi = modes.begin(); mi != new_end; ++mi )
	{
		unique_modes.push_back( *mi );
	}
	valid_display_modes.SetModes( unique_modes );
}


void SETTINGS::CreateDir(string path)
{
#ifndef _WIN32
	// use mode 755
	mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#else
	mkdir(path.c_str());
#endif
}

void SETTINGS::FindHomeDir()
{
	// Figure out the user's home directory - POSIX only...
	char *homedir;
#ifndef _WIN32
	homedir = getenv("HOME");
	if (homedir == NULL)
	{
		homedir = getenv("USER");
		if (homedir == NULL)
		{
			homedir = getenv("USERNAME");
			if (homedir == NULL)
			{
				fprintf( stderr, "Could not find user's home directory!\n" );
				//Quit( 1 );
			}
		}
		home_directory = "/home/";
	}
#else
	homedir = getenv("USERPROFILE");
	if(homedir == NULL)
	{
	    homedir = "data"; // WIN 9x/Me
	}
#endif
	home_directory += homedir;
	home_directory += "/";
}

/*
bool SETTINGS::FileExists(string path)
{
	int result;
	struct stat dir_stat;
	result = stat(path.c_str(), &dir_stat);
	return ( result == 0 );
}

bool SETTINGS::DirExists(string path)
{
	int result;
	struct stat dir_stat;
	result = stat(path.c_str(), &dir_stat);
	return  S_ISDIR(dir_stat.st_mode);
}
*/

bool SETTINGS::FileExists(string path)
{
	bool result;
	ifstream testfile(path.c_str());
	result = !testfile.fail();
	return result;

	/*return utility.FileExists(path);*/
}

void SETTINGS::SaveSettings(CONFIGFILE &c)
{
	// set the current parameters in the config file

	c.SetParam("main.data_dir", data_directory);
	c.SetParam("main.version", config_version);
}

void SETTINGS::ParseSettings(CONFIGFILE &c)
{
#ifndef __APPLE__
#ifndef _WIN32
	string d_dir;
	if( c.GetParam( "main.data_dir", d_dir ) )
	{
		//data_directory = "/usr/share/games/vdrift"; /*TODO: should not be hardcoded*/
		//c.SetParam("main.data_dir", data_directory);
		data_directory = d_dir;
	}
	else
	{
		cout << "No data_dir found in VDrift.config, using " << data_directory << endl;
	}
#else
	data_directory = "data";
#endif
#endif
}

vector<string> SETTINGS::LoadCarPartsSettings()
{
	string game_car;
	Get( "game.selected_car", game_car );
	string filename = settings.GetSettingsDir() + "/carsettings/" + game_car;
	char partid[256];
	int i = 0;
	string part;
	parts_list.clear();
	car_parts.Clear();

	car_parts.Load( filename.c_str() );
	car_parts.SuppressError( true );
	sprintf( partid, "parts.part-%02d", i );

	while( car_parts.GetParam( partid, part ) )
	{
		//cout << "part loaded: " << part;
		parts_list.push_back( part );
		i++;
		sprintf( partid, "parts.part-%02d", i );
	}
	return parts_list;
}

void SETTINGS::SaveCarPartsSettings(vector<string> &p_list)
{
	char partid[256];
	for(int i = 0; i < 100; i++)
	{
		sprintf( partid, "parts.part-%02d", i );
		car_parts.ClearParam(partid);
	}
	for(unsigned int i = 0; i < p_list.size(); i++)
	{
		sprintf( partid, "parts.part-%02d", i );
		car_parts.SetParam( partid, p_list[i] );
	}
	car_parts.Write();
}

string SETTINGS::GetCarPartName(string part_path)
{
	string filename = settings.GetFullDataPath("carparts/" + part_path);
	CONFIGFILE part_file;
	string result;
	part_file.Load(filename.c_str());
	part_file.GetParam(".partname", result);
	return result;
}

string SETTINGS::GetDataDir()
{
	return data_directory;
}

string SETTINGS::GetDataDir(string filename)
{
#ifndef _WIN32
	if(FileExists(settings_path + "/userdata/" + filename))
		return (settings_path + "/userdata");
	else
#endif
		return data_directory;
}

#ifdef ENABLE_NLS
string SETTINGS::GetLocaleDir()
{
	return locale_directory;
}
#endif

string SETTINGS::GetFullDataPath(string filename)
{
	return ( GetDataDir( filename ) + "/" + filename );
}

string SETTINGS::GetHomeDir()
{
	return home_directory;
}

string SETTINGS::GetSettingsDir()
{
	return settings_path;
}

void SETTINGS::SetDataDir(string new_data_dir)
{
	data_directory = new_data_dir;
}

void SETTINGS::SetDefaultDataDir(string new_data_dir)
{
	CONFIGFILE default_config((new_data_dir + "/settings/VDrift.config").c_str());
	ParseSettings(default_config);
	data_directory = new_data_dir;
	SaveSettings(default_config);
}

string SETTINGS::GetSkinPath()
{
	string skin_path = "skins/";
	string skin_dir = "simple";
	if( Get( "display.skin", skin_dir ) )
	{
		//cout << "Skin: " << skin_dir << endl;
	}
	else
	{
		cout << "Skin name not found in config file..." << endl;
	}

	skin_path += skin_dir;

	return skin_path;
}


bool SETTINGS::Get( string val_name, float & out_var )
{
	vector< OPTION<float> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Getting float value " << val_name << "...";

	for( opt_iter = float_options.begin(); opt_iter != float_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a float value...";

			if( !configfile.GetParam( val_name, out_var ) )
			{
				if( spew ) cout << "can't find value in local config, fetching default...";
				out_var = opt_iter->GetDefaultValue();
				configfile.SetParam( val_name, out_var );
			}

			if( spew ) cout << "found value: [" << out_var << "]. done." << endl;

			return true;
		}

	}
	if( spew ) cout << "this is not a float value." << endl;
	return false;
}
/*
bool SETTINGS::Get( string val_name, float * out_var )
{
	if( spew ) cout << "Getting vector value " << val_name << "...";
	for( string::size_type i = 0; i < vec_settings.size(); i++ )
	{
		if( val_name == vec_settings[i] )
		{
			if( spew ) cout << "this is a vector value...";
			if( !configfile.GetParam( val_name, out_var ) )
			{
				if( spew ) cout << "can't find value in local config, fetching default...";
				default_configfile.GetParam( val_name, out_var );
				configfile.SetParam( val_name, out_var );
			}
			if( spew ) cout << "found value: [" << out_var << "]. done." << endl;
			return true;
		}
	}
	if( spew ) cout << "this is not a vector value." << endl;
	return false;
}
*/
bool SETTINGS::Get( string val_name, int & out_var )
{
	vector< OPTION<int> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Getting int value " << val_name << "...";

	for( opt_iter = int_options.begin(); opt_iter != int_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a int value...";
			if( !configfile.GetParam( val_name, out_var ) )
			{
				if( spew ) cout << "can't find value in local config, fetching default...";
				out_var = opt_iter->GetDefaultValue();
				configfile.SetParam( val_name, out_var );
			}
			if( spew ) cout << "found value: [" << out_var << "]. done." << endl;
			return true;
		}
	}
	if( spew ) cout << "this is not an int value." << endl;
	return false;
}

bool SETTINGS::Get( string val_name, string & out_var )
{
	vector< OPTION<string> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Getting string value " << val_name << "...";

	for( opt_iter = string_options.begin(); opt_iter != string_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a string value...";
			if( !configfile.GetParam( val_name, out_var ) )
			{
				if( spew ) cout << "can't find value in local config, fetching default...";
				out_var = opt_iter->GetDefaultValue();
				configfile.SetParam( val_name, out_var );
			}
			
			if( spew ) cout << "found value: [" << out_var << "]. done." << endl;
			
			return true;
		}
	}
	
	if( spew ) cout << "this is not a string value." << endl;
		
	return false;
	//return configfile.GetParam(val_name, out_var);
}

bool SETTINGS::Get( string val_name, bool & out_var )
{
	vector< OPTION<bool> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Getting bool value " << val_name << "...";

	for( opt_iter = bool_options.begin(); opt_iter != bool_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a bool value...";
			if( !configfile.GetParam( val_name, out_var ) )
			{
				if( spew ) cout << "can't find value in local config, fetching default...";
				out_var = opt_iter->GetDefaultValue();
				configfile.SetParam( val_name, out_var );
			}
			if( spew ) cout << "found value: [" << out_var << "]. done." << endl;
			return true;
		}
	}
	if( spew ) cout << "this is not a bool value." << endl;
	return false;
}

bool SETTINGS::Set( string val_name, float in_var )
{
	vector< OPTION<float> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Setting float value " << val_name << "...";

	for( opt_iter = float_options.begin(); opt_iter != float_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a float value...setting to [" << in_var << "]...done." << endl;
			return configfile.SetParam( val_name, in_var );
		}
	}
	if( spew ) cout << "this is not a float value." << endl;
	return false;
}
/*
bool SETTINGS::Set( string val_name, float * in_var )
{
	if( spew ) cout << "Setting vector value " << val_name << "...";
	for( string::size_type i = 0; i < vec_settings.size(); i++ )
	{
		if( val_name == vec_settings[i] )
		{
			if( spew ) cout << "this is a vector value...setting to [" << in_var << "]...done." << endl;
			return configfile.SetParam( val_name, in_var );
		}
	}
	if( spew ) cout << "this is not a vector value." << endl;
	return false;
}
*/
bool SETTINGS::Set( string val_name, int in_var )
{
	vector< OPTION<int> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Setting int value " << val_name << "...";

	for( opt_iter = int_options.begin(); opt_iter != int_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is an int value...setting to [" << in_var << "]...done." << endl;
			return configfile.SetParam( val_name, in_var );
		}
	}
	if( spew ) cout << "this is not an int value." << endl;
	return false;
}

bool SETTINGS::Set( string val_name, string in_var )
{
	vector< OPTION<string> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Setting string value " << val_name << "...";

	for( opt_iter = string_options.begin(); opt_iter != string_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a string value...setting to [" << in_var << "]...done." << endl;
			return configfile.SetParam( val_name, in_var );
		}
	}
	if( spew ) cout << "this is not a string value." << endl;
	return false;
}

bool SETTINGS::Set( string val_name, bool in_var )
{
	vector< OPTION<bool> >::iterator opt_iter;
	string opt_full_name;

	if( spew ) cout << "Setting bool value " << val_name << "...";

	for( opt_iter = bool_options.begin(); opt_iter != bool_options.end(); ++opt_iter )
	{
		opt_full_name = opt_iter->GetCat() + "." + opt_iter->GetName();

		if( val_name == opt_full_name )
		{
			if( spew ) cout << "this is a bool value...setting to [" << in_var << "]...done." << endl;
			return configfile.SetParam( val_name, in_var );
		}
	}
	if( spew ) cout << "this is not a bool value." << endl;
	return false;
}


OPTION<float> SETTINGS::GetFloatOption( string cat, string name )
{
	OPTION<float> result;
	vector< OPTION<float> >::iterator iter;
	for( iter = float_options.begin(); iter != float_options.end(); ++iter )
	{
		if( ( iter->GetCat() == cat ) && ( iter->GetName() == name ) )
		{
			result = *iter;
		}
	}

	return result;
}

OPTION<int> SETTINGS::GetIntOption( string cat, string name )
{
	OPTION<int> result;
	vector< OPTION<int> >::iterator iter;
	for( iter = int_options.begin(); iter != int_options.end(); ++iter )
	{
		if( ( iter->GetCat() == cat ) && ( iter->GetName() == name ) )
		{
			result = *iter;
		}
	}

	return result;
}

OPTION<bool> SETTINGS::GetBoolOption( string cat, string name )
{
	OPTION<bool> result;
	vector< OPTION<bool> >::iterator iter;
	for( iter = bool_options.begin(); iter != bool_options.end(); ++iter )
	{
		if( ( iter->GetCat() == cat ) && ( iter->GetName() == name ) )
		{
			result = *iter;
		}
	}

	return result;
}

OPTION<string> SETTINGS::GetStringOption( string cat, string name )
{
	OPTION<string> result;
	vector< OPTION<string> >::iterator iter;
	for( iter = string_options.begin(); iter != string_options.end(); ++iter )
	{
		if( ( iter->GetCat() == cat ) && ( iter->GetName() == name ) )
		{
			result = *iter;
		}
	}

	return result;
}
