 // vim: set noet sw=8 cino=:
#define GL_GLEXT_PROTOTYPES

#include <fenv.h>

#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <SDL/SDL.h>
#include <math.h>
#include <string>
#include <vector>

using namespace std;

#include "font.h"
#include "utility.h"
#include "textures.h"
#include "backdrop.h"
#include "camera.h"
#include "weather.h"
#include "keyman.h"
#include "mouse.h"
#include "messageq.h"
#include "particles.h"
/*#include "mesh.h"			// Header File For The MESH Class
#include "entity.h"			// ...etc
#include "meshlist.h"
#include "entitylist.h"
#include "water.h"
#include "model.h"
#include "gamedb.h"
#include "ships.h"*/
#include "settings.h"
#include "sound.h"
/* #include "menu.h" */
#include "timer.h"
#include "controls.h"
#include "vamosworld.h"
#include "replay.h"
//#include "trees.h"
#include "logo.h"
#include "gamestate.h"
#include "objects.h"
#include "multiplay.h"
#include "net.h"
#include "configfile.h"
#include "bezier.h"
#include "track.h"
#include "textures.h"
#include "exception.h"
#include "cardinfo.h"

using namespace VDrift;

#include <vamos/body/Gl_Car.h>
#include <vamos/geometry/Texture_Image.h>
#include <vamos/geometry/Three_Vector.h>
//#include <vamos/track/Strip_Track.h>
#include <vamos/body/Car.h>
#include <vamos/body/Fuel_Tank.h>
#include <vamos/body/Wheel.h>
#include <vamos/geometry/Conversions.h>
//#include <vamos/track/Track.h>
#include <vamos/world/World.h>

#include "gui/gui.h"

#ifdef _WIN32
//#define GL_GLEXT_PROTOTYPES
#include <GL/glext.h>
//#include <GL/wglext.h>
//#include <GL/glprocs.h>
#endif

//#define PERFORMANCE_PROFILE

//fps less than below will not tick the simulation
//#define MIN_FPS 5.0

#ifdef PERFORMANCE_PROFILE
#include <sys/time.h>
suseconds_t GetMicroSeconds()
{
	struct timeval tv;
	struct timezone tz;
		
	tz.tz_minuteswest = 0;
	tz.tz_dsttime = 0;
	
	gettimeofday(&tv, &tz);
	
	return tv.tv_usec;
}
#endif


// Set up some booleans
#define TRUE  1
#define FALSE 0

// screen width, height, and bit depth
//const int SCREEN_WIDTH = 1024;
//const int SCREEN_HEIGHT = 768;
//const int SCREEN_BPP = 32;

int SCREEN_WIDTH = 1024;
int SCREEN_HEIGHT = 768;
int SCREEN_BPP = 32;

//#define PATCH_DEBUG

bool verbose_output = false;

//bool in_menu = false;
//bool menu_display = false;

/*const int SCREEN_WIDTH = 1280;
const int SCREEN_HEIGHT = 1024;
const int SCREEN_BPP = 32;*/

// This is our SDL surface
SDL_Surface *surface;

// Ambient Light Values ( NEW )
//GLfloat LightAmbient[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat LightAmbient[]  = { 0.15f, 0.15f, 0.15f, 1.0f };
// Diffuse Light Values ( NEW )
//GLfloat LightDiffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat LightDiffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat LightSpecular[]  = { 0.0f, 0.0f, 0.0f, 0.0f };
// Light Position ( NEW )
GLfloat LightPosition[4];

/*
ENTITYLIST entities;
MESHLIST meshes;
TERRAIN terrain;
TREES foliage;
WATER water;
GAMEDB gamedb;
SHIPS ships;*/
KEYMAN keyman;
GAMECONTROLS gamecontrols;
SETTINGS settings;
TEXTURES textures;
CAMERA cam;
FONT font;
UTILITY utility;
BACKDROP backdrop;
WEATHER weathersystem;
MOUSE mouse;
MESSAGEQ mq1;
//TERRAIN terrain;
PARTICLE particle;
SOUNDMANAGER sound;
/* MENU menu; */
TIMER timer;
REPLAY replay;
//TREES trees;
LOGO logo;
GAMESTATE state;
OBJECTS objects;
NET net;
MULTIPLAY multiplay;
VGUI::Gui gui;
TRACK track;
CARDINFO gfxcard;

VAMOSWORLD world;
//Vamos_Track::Strip_Track *road;

ofstream error_log;

#define CAR_Y_OFFSET 1.0

double day_time;
double abs_time;

//for game timing and FPS stuff
static GLint T0;
static GLint Frames;
static GLfloat fps;

bool showfps;

static int frameno;

float timefactor = 1.0f;

//#define AVERAGEFRAMES 30
#define AVERAGEFRAMES 5

float pfps;
float lfps[AVERAGEFRAMES];
int lfpspos = 0;
bool lfpsfull = false;

//#define FRAME_TIME 0.002
#define FRAME_TIME 0.004
double execution_time = 0.0;

bool pauserequest = false;
bool unpauserequest = false;
bool esc_pressed = false;

float view_dist = 10000.0;

//int screenshots = 0;
string cur_tex_size = "";
int cur_screen_w = 0;
int cur_screen_h = 0;
int cur_screen_bpp = 0;
bool cur_fullscreen = false;
string tree_detail = "";

float FrameTime()
{
	return FRAME_TIME;
}

int Screenshot(char *filename)
{
	SDL_Surface *screen;
	SDL_Surface *temp = NULL;
	unsigned char *pixels;
	int i;
	
	screen = surface;
	
	if (!(screen->flags & SDL_OPENGL))
	{
		SDL_SaveBMP(temp, filename);
		return 0;
	}
	
	temp = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h, 16,
	#if SDL_BYTEORDER == SDL_LIL_ENDIAN
	0x000000FF, 0x0000FF00, 0x00FF0000, 0
	#else
	0x00FF0000, 0x0000FF00, 0x000000FF, 0
	#endif
	);
	if (temp == NULL)
		return -1;
	
	pixels = (unsigned char *) malloc(3 * screen->w * screen->h);
	if (pixels == NULL)
	{
		SDL_FreeSurface(temp);
		return -1;
	}
	
	glReadPixels(0, 0, screen->w, screen->h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
	
	for (i=0; i<screen->h; i++)
		memcpy(((char *) temp->pixels) + temp->pitch * i, pixels + 3*screen->w * (screen->h-i-1), screen->w*3);
	free(pixels);
	
	SDL_SaveBMP(temp, filename);
	SDL_FreeSurface(temp);
	return 0;
}

void ResetWorld(bool fullreset);

/** Cleaup and exit.
 * @param returnCode the exit code
 *
 * This function cleans up the game state and restores the desktop environment
 * to it's previous state.
 */
void Quit(int returnCode)
{
	state.SetGameState(STATE_EXIT);

	if (replay.Recording() != -1)
		replay.Stop();

	gamecontrols.WriteControlFile();

	if (verbose_output)
		cout << "Quit called" << endl;

	error_log.close();

	/* clean up the window */
	SDL_Quit( );

	if (verbose_output)
		cout << "SDL_Quit finished" << endl;

	exit(returnCode);
}

// function to reset our viewport after a window resize
int resizeWindow( int width, int height )
{
	// Height / width ration
	GLfloat ratio;
 
	// Protect against a divide by zero
	if ( height == 0 )
		height = 1;

	ratio = ( GLfloat )width / ( GLfloat )height;

	// Setup our viewport.
	glViewport( 0, 0, ( GLint )width, ( GLint )height );

	// change to the projection matrix and set our viewing volume.
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );

	// Set our perspective
	gluPerspective( 45.0f, ratio, 0.1f, 10000.0f );

	// Make sure we're chaning the model view and not the projection
	glMatrixMode( GL_MODELVIEW );

	// Reset The View
	glLoadIdentity( );

	return( TRUE );
}

void LoadingScreen(string loadtext);

void ChangeDisplay(int w, int h, int bpp, bool fullscreen, bool reloadtextures)
{
	SCREEN_WIDTH = w;
	SCREEN_HEIGHT = h;
	SCREEN_BPP = bpp;
	
	// the flags to pass to SDL_SetVideoMode
	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

	// This checks to see if surfaces can be stored in memory
	/*if ( videoInfo->hw_available )
		videoFlags |= SDL_HWSURFACE;
	else
		videoFlags |= SDL_SWSURFACE;

	if ( videoInfo->blit_hw )
		videoFlags |= SDL_HWACCEL;*/

	if (fullscreen)
	{
		videoFlags |= SDL_HWSURFACE|SDL_ANYFORMAT|SDL_FULLSCREEN;  
	}	
	else
	{
		videoFlags |= SDL_SWSURFACE|SDL_ANYFORMAT;
	}

	// get a SDL surface
	if (surface != NULL)
	{
		SDL_FreeSurface(surface);
		surface = NULL;
	}
	surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, videoFlags );

	resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT );

	if (reloadtextures)
	{
		if( gui.GetEnabled() ) gui.SetEnabled( false );
		LoadingScreen("Loading...\nReloading textures");
		//textures.DeleteAll();
		textures.ReloadAll();
		LoadingScreen("Loading...\nFont");
		font.Load();
		LoadingScreen("Loading...\nBackdrop");
		backdrop.Init();
		//menu.Load();
		LoadingScreen("Loading...\nGame Controls");
		gamecontrols.LoadControls();
		LoadingScreen("Loading...\nParticles");
		particle.Load();
		LoadingScreen("Loading...\nTimer");
		timer.Load();
		LoadingScreen("Loading...\nHUD");
		world.UnloadHUD();
		world.LoadHUD();
		LoadingScreen("Loading...\nUser interface");
		//gui.DeInit();
		gui.ReInit();
		//menu.MainMenu();
		//gui.ChangePage( "Main" );
		//gui.SetEnabled( true );
	}

	gui.UpdateScreenSize();
}

void ReloadDisplay()
{
	string tex_size;
	int w, h, bpp;
	bool fullscreen, show_fps, reload_tex;

	settings.Get( "display.width", w );
	settings.Get( "display.height", h );
	settings.Get( "display.depth", bpp );
	settings.Get( "display.fullscreen", fullscreen );
	settings.Get( "display.show_fps", show_fps );
	settings.Get( "display.texture_size", tex_size );
	settings.Get( "display.view_distance", view_dist );
	settings.Get( "display.tree_detail", tree_detail );

	reload_tex = ( tex_size == cur_tex_size ) &&
	             ( w == cur_screen_w ) &&
	             ( h == cur_screen_h ) &&
	             ( fullscreen == cur_fullscreen ) &&
	             ( bpp == cur_screen_bpp ) ? false : true;
	reload_tex = true;

	showfps = show_fps;
	world.UpdateSettings();
	objects.UpdateSettings();
	//trees.UpdateSettings();
	//terrain.UpdateSettings();
	ChangeDisplay( w, h, bpp, fullscreen, reload_tex );
	cur_tex_size = tex_size;
	cur_screen_w = w;
	cur_screen_h = h;
	cur_screen_bpp = bpp;
	cur_fullscreen = fullscreen;
}

void Update()
{
	//cam.position.DebugPrint();

	if (MP_DBGDEEP)
		cout << "main update start" << endl;

	bool mainloop = false;

	//if unpaused and getting a reasonable framerate
	//if (fps > MIN_FPS && timefactor != 0.0f)
	if (fps > 0.0f && timefactor != 0.0f && state.GetGameState() != STATE_INITIALMENU)
	{
		/*VERTEX campos = cam.position;
		campos.Scale(-1.0);
		cout << campos.x << "," << campos.z << ": " << track.Elevation(campos) << endl;*/
		
		
		execution_time += timefactor / fps;
		int num_updates = (int) (execution_time / FRAME_TIME);
		double this_frame = (double) num_updates * FRAME_TIME;

		mouse.Update(cam, SCREEN_WIDTH, SCREEN_HEIGHT, timefactor, fps);
		weathersystem.Update(abs_time);

		while (execution_time > FRAME_TIME)
		{
			#ifdef PERFORMANCE_PROFILE
			suseconds_t t1, t2;
			t1 = GetMicroSeconds();
			t1 = GetMicroSeconds();
			#endif

			//multiplay.Update(FRAME_TIME);
			
			//weather tick

			#ifdef PERFORMANCE_PROFILE
			t2 = GetMicroSeconds();
			cout << "multiplay.Update() ticks: " << t2-t1 << endl;
			t1 = GetMicroSeconds();
			#endif

			//timer tick
			//timer.TickTimer(timefactor,fps);
			timer.TickTimer(1.0, 1.0/FRAME_TIME);

			cam.Update();

			//handle input
			//keyman.DoHeldKeys(timefactor, fps, cam);
			keyman.DoHeldKeys(1.0, 1.0/FRAME_TIME, cam);

			#ifdef PERFORMANCE_PROFILE
			t1 = GetMicroSeconds();
			t1 = GetMicroSeconds();
			#endif

			//world.Update(timefactor, fps, js);
			world.Update(1.0, 1.0/FRAME_TIME, gamecontrols.Get_js());

			#ifdef PERFORMANCE_PROFILE
			t2 = GetMicroSeconds();
			cout << "world update ticks: " << t2-t1 << endl;
			t1 = GetMicroSeconds();
			#endif

			keyman.DoOneTimeKeys(cam);

			//particle.Update(timefactor, fps);
			//particle.Update(1.0, 1.0/FRAME_TIME);

			cam.ExtractFrustum();

			LightPosition[0] = cos(backdrop.sunpos_lat);
			LightPosition[1] = sin(backdrop.sunpos_lat);
			if (LightPosition[1] < 0)
				LightPosition[1] *= 10.0f; //to simulate sun disappearing behind horizon
			LightPosition[2] = 0.0f;//tan(backdrop.sunpos_lng);

			float dusktime = 0.5;
			float dawntime = 0.0;
			float transtime = 0.05;

			float diffuse = 1.0f;
			if (day_time >= dusktime || day_time <= dawntime)
			{
				diffuse = 0.0;
			}

			if (day_time > dawntime && day_time < transtime)
			{
				diffuse = day_time / (transtime);
			}

			if (day_time > dusktime - transtime && day_time < dusktime)
			{
				diffuse = (day_time - dusktime - transtime) / transtime;
			}

			diffuse = diffuse*0.9+0.1;

			VERTEX ld;
			ld.Set(LightDiffuse[0], LightDiffuse[1], LightDiffuse[2]);
			ld.Scale(diffuse);
			glLightfv( GL_LIGHT1, GL_DIFFUSE, ld.v3() );

			//float timepassed = (timefactor/fps)/86400.0;
			float timepassed = (FRAME_TIME)/86400.0;
			/*if (keyman.keys[keyman.GetKey("AccelTimeVFast")])
				timefactor = 10000.0;
			else if (keyman.keys[keyman.GetKey("AccelTimeFast")])
				timefactor = 1000.0;
			else
				timefactor = 1.0;*/
			//float timepassed = (timefactor/fps)/60.0;
			//float timepassed = (timefactor/fps)/10.0;
			abs_time += timepassed;
			day_time += timepassed;
			if (day_time > 1.0f)
				day_time -= 1.0f;

			#ifdef PERFORMANCE_PROFILE
			t2 = GetMicroSeconds();
			cout << "Time increment ticks: " << t2-t1 << endl;
			t1 = GetMicroSeconds();
			#endif

			multiplay.Send(FRAME_TIME);

			mainloop = true;

			//terrain.Update(cam, timefactor, fps, day_time);
			//terrain.Update(cam, 1.0, 1.0/FRAME_TIME, day_time);

			execution_time -= FRAME_TIME;
			num_updates++;

			//replay.IncrementFrame();
		}
		particle.Update(1.0, 1.0/this_frame);
//		terrain.Update(cam, 1.0, 1.0/this_frame, day_time);
	}
	else
	{
		//print "paused"
		if (state.GetGameState() != STATE_INITIALMENU)
			world.Update(timefactor, fps, gamecontrols.Get_js());
	}

	if (pauserequest)
	{
		if (timefactor != 0.0f)
			timefactor = 0.0f;
		pauserequest = false;
	}
	if (unpauserequest)
	{
		if (timefactor == 0.0f)
			timefactor = 1.0f;
		unpauserequest = false;
	}

	sound.Update();
	/*if (!mainloop)
		multiplay.Update(0);*/ //don't need this anymore because we're stopping things from being paused in multiplayer mode

	if (MP_DBGDEEP)
		cout << "main update done" << endl;
}

void snap_screenshot()
{
	char scrname[64];
	int num_shots;
	settings.Get( "game.num_shots", num_shots );
	sprintf(scrname, "%s/screenshots/shot%03d.bmp", (settings.GetSettingsDir()).c_str(), num_shots );
	Screenshot(scrname);
	settings.Set( "game.num_shots", num_shots + 1 );
}

void MainPause()
{
	//if (!menu.InMenu() && !multiplay.NumConnected() > 0)
	//if (state.GetGameState() == STATE_PLAYING)
	if( !gui.GetEnabled() && !multiplay.NumConnected() > 0 )
		pauserequest = true;
	/*bool mouse_enabled;
	settings.Get( "mouse.enabled", mouse_enabled );
	if( mouse_enabled )
	{
		gui.MouseReturn();
	}*/
	gui.ProcessAction( "Pause" );
}

void MainUnpause()
{
	unpauserequest = true;
}

// function to handle key press events
void handleKeyPress( SDL_keysym *keysym )
{
	//Vamos_Geometry::Three_Vector tvec1(44.464,-126.8737,0);
	//VERTEX tvec2;
	//QUATERNION trot;

	//if (state.GetGameState() == STATE_MENU)
	//if (menu.InMenu())
	//	menu.MenuKey(keysym->sym);
	//else if (timefactor == 0 && !menu.InMenu()) timefactor = 0;
		//		else timefactor = 1.0;
	if( !gui.GetEnabled() )
	{
		switch ( keysym->sym )
		{
		case SDLK_ESCAPE:
			// ESC key was pressed
			//Quit(0);
			//gui.ChangePage( "InGameMenu" );
			//menu.MainMenu();
			esc_pressed = true;
			break;
		/*case SDLK_F7:
			
			tvec1 = tvec1.rotate(0,0,6.21652);
			cout << tvec1[0] << "," << tvec1[1] << endl;
		
			
			tvec2.Set(44.464,-126.8737,0);
			
			trot.Rotate(6.21652, 0, 0, 1);
			tvec2 = trot.RotateVec(tvec2);
			tvec2.DebugPrint();
			break;*/
		/*case SDLK_F7:
			if (timefactor != 100.0f)
				timefactor = 100.0f;
			else
				timefactor = 1.0f;
			break;*/
		/*case SDLK_PAGEUP:
			//tmpvert = cam.GetVelocity();
			cam.MoveRelative(0.0f, 0.0f, -2.0f);
			//cam.Update();
			//cam.LoadVelocityIdentity();
			break;
		case SDLK_PAGEDOWN:
			//tmpvert = cam.GetVelocity();
			cam.MoveRelative(0.0f, 0.0f, 2.0f);
			//cam.Update();
			//cam.LoadVelocityIdentity();
			break;*/
		
		//case SDLK_F11:
			//SDL_WM_ToggleFullScreen( surface );
			//ChangeDisplay(1280,1024,32,true);
			//menu.DisplayMenu();//DisplaySelect();
			//break;
		
		//case SDLK_F12:
			//menu.MainMenu();
			//break;

		default:
			/*if (timefactor == 0.0f)
				MainUnpause();
			else
				keyman.OneTime(keysym->sym);*/
			break;
		}
	}

	return;
}

void handleKeyRelease( SDL_keysym *keysym )
{
	if( !gui.GetEnabled() )
	{
		switch ( keysym->sym )
		{
		case SDLK_ESCAPE:
			// ESC key was released
			if( esc_pressed )
			{
				esc_pressed = false;
				MainPause();
			}
			break;
		default:
			/*if (timefactor == 0.0f)
				MainUnpause();
			else*/
				keyman.OneTime(keysym->sym);
			break;
		}
	}

	return;
}

void glSetup()
{
	// Enable Texture Mapping ( NEW )
	glEnable( GL_TEXTURE_2D );
	// Enable smooth shading
	glShadeModel( GL_SMOOTH );
	// Set the background black
	//glClearColor( 0.53f, 0.74f, 0.91f, 0.0f );
	glClearColor(0,0,0,0);
	// Depth buffer setup
	glClearDepth( 1.0f );
	// Enables Depth Testing
	glEnable( GL_DEPTH_TEST );
	// The Type Of Depth Test To Do
	glDepthFunc( GL_LEQUAL );
	// Really Nice Perspective Calculations
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
	// Enable Lighting
	glEnable( GL_LIGHTING );

	//set the clipping planes
	//glFrustum(1000,1000,1000,1000,1,1000);

	//utility.Tex2D(3, false);
	//utility.Tex2D(2, false);
	//utility.Tex2D(1, false);
	//utility.Tex2D(0, true);
}

//string otrack_file;
//string car_file;
string data_dir;
//int car_paint;

//int number_of_opponents;
//int focused_car;

void SelectCar(string cfile, bool trim)
{	
	//if (clearold)
	world.clear_cars();
	//string car_name;
	//settings.Get( "game.selected_car", car_name );
	//int car_paint;
	//settings.Get( "game.car_paint", car_paint );
	//car_file = cfile;
	state.SetCarName(0, cfile);

	Vamos_Body::Gl_Car* car = 0;
/*	try
	{
*/		car = new Vamos_Body::Gl_Car (Vamos_Geometry::Three_Vector (11.0, 0.0, 0.6));
		car->read (data_dir, cfile);
		car->SetPaint(state.GetCarPaint(0));
		car->chassis ().translate (Vamos_Geometry::Three_Vector(track.GetStart().x, -track.GetStart().z, track.GetStart().y));
		car->chassis ().translate (Vamos_Geometry::Three_Vector (10.0*multiplay.MyNum(), 0.0, 
											   -car->chassis ().lowest_contact_position () + CAR_Y_OFFSET));
		car->start_engine ();
		car->set_controller(1);
		world.add_car (car);

		/*for (int i = 0; i < number_of_opponents; i++)
		{
			car = new Vamos_Body::Gl_Car (Vamos_Geometry::Three_Vector (11.0, 0.0, 0.6));
			car->read (data_dir, cfile);
			car->chassis ().translate (Vamos_Geometry::Three_Vector (10.0 * (i + 1), 0.0, 
				-car->chassis ().lowest_contact_position () + CAR_Y_OFFSET));
			car->start_engine ();
			car->set_controller(-1);
			world.add_car (car);
		}*/

		int i;
		//if (!multiplay.Server()) //the client re-loads the world, the server doesn't (NOT TRUE ANYMORE)
		{
			for (i = 0; i < multiplay.NumConnected()+1; i++)
			{
				if (i != multiplay.MyNum())
				{
					int idx = i;
					if (idx == 0)
						idx = 1;
					car = new Vamos_Body::Gl_Car (Vamos_Geometry::Three_Vector (11.0, 0.0, 0.6));
					car->read (data_dir, state.GetCarName(idx));
					car->SetPaint(state.GetCarPaint(idx));
					car->chassis ().translate (Vamos_Geometry::Three_Vector(track.GetStart().x, -track.GetStart().z, track.GetStart().y));
					car->chassis ().translate (Vamos_Geometry::Three_Vector (10.0*i, 0.0, 
						-car->chassis ().lowest_contact_position () + CAR_Y_OFFSET));
					car->start_engine ();
					car->set_controller(2);
					world.add_car (car);
				}
			}
		}

		if (replay.GhostCar())
		{
			car = new Vamos_Body::Gl_Car (Vamos_Geometry::Three_Vector (11.0, 0.0, 0.6));
			car->read (data_dir, replay.ReplayCar());
			car->SetPaint(replay.ReplayPaint());
			car->chassis ().translate (Vamos_Geometry::Three_Vector (10.0*multiplay.MyNum(), 0.0, 
				-car->chassis ().lowest_contact_position () + CAR_Y_OFFSET));
			car->start_engine ();
			car->set_controller(3);
			world.add_car (car);
		}

		world.reset();
/*	}
	catch (Vamos_Geometry::XML_Exception& error)
	{
		std::cerr << error.message () << std::endl;
		std::exit (EXIT_FAILURE);
	}
*/
	if (trim)
		ResetWorld(false);
}

bool UnloadWorld()
{
	//these should have error catching such that if they're called before
	// init, it's OK
	//textures.DeleteAll();
	backdrop.DeInit();
//	terrain.DeInit();
	world.DeInit();
	sound.Unload();
	
	return true;
}

#ifdef PATCH_DEBUG
BEZIER * thepatch;
#endif

bool LoadWorld()
{
	UnloadWorld();

	//begin loading world
	timefactor = 1.0;
	
	//reset best lap time
	timer.ResetBest();
	timer.ResetLast();

	LoadingScreen("Loading...\nConfiguration files");

	//state.SetCarName( 0, "" );
	//state.SetCarPaint( 0, 0 );
	//state.SetTrackName( "" );

	string selected_car = "";
	string track_name;
	int car_paint = 0;

	settings.Get( "game.selected_car", selected_car );
	settings.Get( "game.car_paint", car_paint );
	settings.Get( "game.track", track_name );

	//cout << "selected " << track_name << " " << selected_car << endl;
	state.SetCarName( 0, selected_car );
	state.SetCarPaint( 0, car_paint );
	state.SetTrackName( track_name );

	if (state.GetCarName(0) == "")
		state.SetCarName(0, "CS");

	//if (track_name == "")
	//	state.SetTrackName( "ruudskogen" );

	//string world_file = "default-world";
	//string controls_file = "default-controls";
	//number_of_opponents = 0;
	//focused_car = 0;
	//string track_file = "tracks/" + state.GetTrackName() + "/" + state.GetTrackName() + ".xml";
	//world_file = "worlds/" + world_file + ".xml";
	//controls_file = settings.GetSettingsDir() + "/controls/" + controls_file + ".xml";
	data_dir = settings.GetDataDir() + "/";

	/*LoadingScreen("Loading...\nLoading terrain");
	//terrain.Init(2000,200,2000,"outermap3.raw","outermap1-detail.bmp","outermap2-test.png");
	//terrain.Init(2000,200,2000,"shaw4.raw","outermap1-detail.bmp","shawtestmap2.png", REFLECT_RES);
	//terrain.Init(2000,200,2000,"shawlike.raw","detail-lowfreq.bmp","shawlike.png", REFLECT_RES_X, REFLECT_RES_Y);
	//terrain.Init(2000,200,2000,"outermap4.raw","outermap1-detail.bmp","outermap4v1.png");
	string terrainfile;
	string trackpath = settings.GetDataDir() + "/tracks/" + state.GetTrackName() + "/";
	terrainfile = trackpath + "parameters.txt";

	ifstream tf(terrainfile.c_str());
	int numparam, i;
	numparam = 7;
	float param[numparam];
	if (tf)
	{
		for (i = 0; i < numparam; i++)
			param[i] = utility.fGetParam(tf);
		terrain.Init((int)param[0],(int)param[1],(int)param[2], trackpath+"terrain.png", trackpath+"mask.png", trackpath+"texture.png", trackpath+"detail.png", trackpath+"detail2.png", trackpath+"parametric.png", param[3], param[4], param[5], param[6]);
	}
	else
	{
		cout << "Couldn't find track terrain parameter file " << terrainfile << endl;
		//terrain.Init(3000,300,3000, otrack_file+".png", otrack_file+"-mask.png", otrack_file+".png", otrack_file+"-detail.png", otrack_file+"-detail2.png", otrack_file+"-parametric.png", -1500, -2000, 150, 100);
		param[0] = 3000; param[1] = 300; param[2] = 3000;
		param[3] = -1500;
		param[4] = -2000;
		param[5] = 150;
		param[6] = 100;
		terrain.Init((int)param[0],(int)param[1],(int)param[2], trackpath+"terrain.png", trackpath+"mask.png", trackpath+"texture.png", trackpath+"detail.png", trackpath+"detail2.png", trackpath+"parametric.png", param[3], param[4], param[5], param[6]);
	}

	LoadingScreen("Loading...\nTrack");
	try
	{
		road = new Vamos_Track::Strip_Track;
		road->read (data_dir, track_file);
	}
	catch (Vamos_Geometry::XML_Exception& error)
	{
		std::cerr << error.message () << std::endl;
		std::exit (EXIT_FAILURE);
	}*/

	LoadingScreen("Loading...\nTrack");
	track.Load(state.GetTrackName());
	
	LoadingScreen("Loading...\nLoading backdrop");
	backdrop.Init();
	
	LoadingScreen("Loading...\nLoading scenery objects");
	string objectpath = settings.GetDataDir() + "/tracks/" + state.GetTrackName() + "/objects";
	objects.LoadObjectsFromFolder(objectpath);

	#ifdef COLLISION_TESTING
	if (0)
	{
		bool col = false;
		VERTEX origin, dir, colpt, normal;
		bool closest = true;
		origin.Set(496.334,1000,-1.11335);
		dir.Set(0,-1,0);
		BEZIER * colpatch;
		ROADSTRIP * colstrip;
		ofstream deb;
		deb.open("debug.log");
		deb << "Debug log start" << endl;
		deb.close();
		
		col = track.CollideRoads(origin, dir, colpt, closest, colstrip, colpatch, normal);
		deb.open("debug.log", ofstream::out | ofstream::app);
		deb << "col: " << col << endl;
		deb << "origin: "; origin.DebugPrint(deb);
		deb << "dir: "; dir.DebugPrint(deb);
		deb << "colpt: "; colpt.DebugPrint(deb);
		deb << "closest: " << closest << endl;
		deb << "normal: "; normal.DebugPrint(deb);
		deb << "seglen: " << 10000.0f << endl;
		deb << "-------------" << endl;
		deb.close();
		
		col = objects.Collide(origin, dir, colpt, closest, normal, 10000.0f);
		deb.open("debug.log", ofstream::out | ofstream::app);
		deb << "col: " << col << endl;
		deb << "origin: "; origin.DebugPrint(deb);
		deb << "dir: "; dir.DebugPrint(deb);
		deb << "colpt: "; colpt.DebugPrint(deb);
		deb << "closest: " << closest << endl;
		deb << "normal: "; normal.DebugPrint(deb);
		deb << "seglen: " << 10000.0f << endl;
		deb << "-------------" << endl;
		deb.close();
	}
	#endif
	
	//Quit(0);
	
	LoadingScreen("Loading...\nLoading sound");
	//sound.Deinit();
	//sound.Init();
	sound.Reload();

	LoadingScreen("Loading...\nWorld");
	world.Init(&track);

	LoadingScreen("Loading...\nCar");
	SelectCar(state.GetCarName(0), true);
	//world.GetPlayerCar()->SetPaint(car_paint); <-done in selectcar instead.

	//LoadingScreen("Loading...\nLoading Trees");
	//trees.ReadFromFile(settings.GetFullDataPath("tracks/" + state.GetTrackName() + "/treemap.png"), settings.GetFullDataPath("tracks/" + state.GetTrackName() + "/foliagemap.png"));
	/*trees.DeleteAll();
	int numtrees = 200;
	for (i = 0; i < numtrees/2; i++)
	{
		VERTEX tp;
		tp.x = ((float) rand()/RAND_MAX)*(param[0]/2.0)+param[0]/4.0;
		tp.z = ((float) rand()/RAND_MAX)*(param[2]/2.0)+param[2]/4.0;
		tp.x += param[3];
		tp.z += param[4];
		tp.y = terrain.GetHeight(tp.x, tp.z);
		trees.Add(tp, 40.0, 0, 5);
	}

	for (i = 0; i < numtrees/2; i++)
	{
		VERTEX tp;
		tp.x = ((float) rand()/RAND_MAX)*(param[0]/2.0)+param[0]/4.0;
		tp.z = ((float) rand()/RAND_MAX)*(param[2]/2.0)+param[2]/4.0;
		tp.x += param[3];
		tp.z += param[4];
		tp.y = terrain.GetHeight(tp.x, tp.z);
		trees.Add(tp, 60.0, 1, 5);
	}*/

	timer.Reset();
	particle.Clear();

	LoadingScreen("Loading...\nDone");

	mq1.AddMessage("Simulation start");

	#ifdef PATCH_DEBUG
	{
		VERTEX v[4];
		/*v[0].Set(-2045.29,604.818,-566.924);
		v[1].Set(-2040.08,605.083,-572.898);
		v[2].Set(-2026.6,602.961,-550.628);
		v[3].Set(-2020.85,602.961,-557.449);*/
		v[0].Set(-2148.81,583.537,-1395.68);
		v[1].Set(-2151.04,583.525,-1396.96);
		v[2].Set(-2145.45,583.378,-1401.53);
		v[3].Set(-2147.67,583.341,-1402.81);

		thepatch = track.GetPatch(v);
		if (thepatch != NULL)
		{
			cout << "Got patch" << endl;
		
			VERTEX p1,p2;
			/*p1.Set(-2032.96,603.927,-562.198);
			p2.Set(-2033,603.931,-562.227);*/
			p1 = (v[0] + v[1]).ScaleR(0.5);
			p2 = (v[2] + v[3]).ScaleR(0.5);
			VERTEX dir;
			dir = p2-p1;
			dir = dir.normalize();
			dir.Scale(7.0);
			p1 = p1 - dir.ScaleR(0.5);
			int steps = 100;
			/*cout << "c(";
			for (int i = 0; i < steps; i++)
			{
				VERTEX p;
				p = p1 + dir.ScaleR((float)i/(float)steps);
				cout << track.Elevation(p) << ",";
				//cout << track.Elevation(p1) << endl;
				//cout << track.Elevation(p2) << endl;
			}
			cout << ")" << endl;*/
			
			cout << "c(";
			for (int i = 0; i < steps; i++)
			{
				VERTEX p;
				p = p1 + dir.ScaleR((float)i/(float)steps);
				VERTEX dir;
				dir.y = -1000;
				p.y += 1000;
				VERTEX tvert, tnorm;
				bool col = thepatch->CollideSubDivQuadSimpleNorm(p, dir, tvert, tnorm);
				if (col)
					cout << tvert.y << ",";
				else
					cout << 600 << ",";
				//cout << track.Elevation(p) << ",";
				//cout << track.Elevation(p1) << endl;
				//cout << track.Elevation(p2) << endl;
			}
			cout << ")" << endl;
		}
		else
			cout << "couldn't find patch" << endl;
	}
	#endif
	
	//triangle-triangle collision testing
	/*if (0)
	{
		VERTEX tri1[3];
		VERTEX tri2[3];
		tri1[0].Set(0,0,0);
		tri1[1].Set(1,0,0);
		tri1[2].Set(1,0,1);
		
		tri2[0].Set(0,1,0);
		tri2[1].Set(1,1,0);
		tri2[2].Set(1,-1,1);
		
		VERTEX colpt, colseg;
		int whichtri;
		
		bool col = utility.BruteForceTriangleIntersectionF(tri1, tri2, colpt, 
				colseg, whichtri);
		if (col)
		{
			colpt.DebugPrint();
			colseg.DebugPrint();
			cout << whichtri << endl;
		}
		else
			cout << "No col" << endl;
	}
	
	if (0)
	{
		VERTEX tri1[3];
		VERTEX tri2[3];
		tri1[0].Set(132.422,-1.18925,1.84825);
		tri1[1].Set(131.82,-0.16846,-0.154412);
		tri1[2].Set(131.803,-0.173352,1.84551);
		
		tri2[0].Set(220.398,220.398,-3.08942e-06);
		tri2[1].Set(-220.398,220.398,1.19984e-05);
		tri2[2].Set(220.398,-220.398,-2.23572e-05);
		
		VERTEX colpt, colseg;
		int whichtri;
		
		bool col = utility.BruteForceTriangleIntersectionF(tri1, tri2, colpt, 
				colseg, whichtri);
		if (col)
		{
			colpt.DebugPrint();
			colseg.DebugPrint();
			cout << whichtri << endl;
		}
		else
			cout << "No col" << endl;
	}*/
	
	return true;
}

void InitGameData()
{
	//LoadingScreen("Loading...\nInitializing utilities");
	utility.Init();

	state.SetGameState(STATE_LOGO);
	logo.run();

	//state.SetGameState(STATE_PLAYING);
	state.SetGameState(STATE_INITIALMENU);
	LoadingScreen("Loading...\nSetting time");
	LightPosition[0] = 2.0f;
	LightPosition[1] = 2.0f;
	LightPosition[2] = 2.0f;
	LightPosition[3] = 0.0f;

	day_time = abs_time = 0.1;
	//day_time = abs_time = 0.03;
	//day_time = abs_time = 0.48;
	//day_time = abs_time = 0.75;

	//LoadingScreen("Loading...\nUser interface");
	//menu.Load();
	//gui.Init();

	LoadingScreen("Loading...\nInitializing weather");
	weathersystem.Init();
	weathersystem.Update(abs_time);

	LoadingScreen("Loading...\nSetting camera position");

	// Set the initial camera position
	//cam.Move(-200,-200,-200);
	//cam.Move(-596.513,-30.9295,-250.623);

	cam.Move(0,-5,0);
	cam.Update();
	cam.LoadVelocityIdentity();
	//cam.Rotate(180.0,0,1,0);
	/*
	LoadingScreen("Loading...\nLoading Meshes");
	//load scene objects
	string listval;
	listval = settings.GetSettingsDir() + "/meshlist.txt";
	int ret;
	ret = meshes.LoadList(listval);
	if (!ret)
	{
		error_log << "Fatal error:  could not load mesh list\n";
		return FALSE;
	}

	//LoadingScreen("Loading...\nLoading game database");
	//gamedb.LoadAll();

	LoadingScreen("Loading...\nLoading new game state");
	*/

	LoadingScreen("Loading...\nStarting message queue");
	mq1.SetPersist(5.0f);
	mq1.SetDepth(1);
	mq1.SetPos(0.01,0.09,5,1);
	mq1.SetBuildUp(true);
	//mq1.SetTimePrint(true);

	/*
	//LoadingScreen("Loading...\nLoading sound");
	//sound.Load(&cam);

	//LoadingScreen("Loading...\nLoading HUD");
	//playerhud.Load();

	LoadingScreen("Loading...\nLoading water");
	water.initWater(64,64,1,3.0f);
	//water.initWater(32,32,16,100.0f);
	//water.initWater(64,64,8,10.0f);
*/

	LoadingScreen("Loading...\nGame control config");
	keyman.Load();
	gamecontrols.LoadControls();

//	LoadingScreen("Loading...\nLoading Trees");
//	trees.Load();

	LoadingScreen("Loading...\nInitializing particle engine");
	particle.Load();
	VERTEX wind;
	wind.x = 0.3;
	wind.z = 0.1;
	particle.SetWind(wind);

	LoadingScreen("Loading...\nLoading Timer");
	timer.Load();

	LoadingScreen("Loading...\nInitializing Network");
	net.Init();

	string track;
	settings.Get( "game.track", track );
	state.SetTrackName( track );
	if( state.GetTrackName() == "" )
		state.SetTrackName("ruudskogen");

	ifstream csfile;
	//car_file = "";
	//state.SetCarName(0, "");
	//csfile.open("carsettings/selected_car");
	//car_paint = 0;
	//state.SetCarPaint(0, 0);
/*	if (csfile)
	{
		state.SetCarName(0, utility.sGetLine(csfile));
		state.SetCarPaint(0, utility.iGetParam(csfile));
		csfile.close();
	}
*/
	string car_name;
	int car_paint;
	settings.Get( "game.selected_car", car_name );
	settings.Get( "game.car_paint", car_paint );

	state.SetCarName( 0, car_name );
	state.SetCarPaint( 0, car_paint );
	// XXX use a better way to pick the default car
	if (state.GetCarName(0) == "")
	{
		state.SetCarName(0, "MI");
		//cout << "no car selected, picking default" << endl;
	}
	
	LoadingScreen("Loading...\nInitializing sound");
	sound.Init();
	float sound_volume = 0.5;
	settings.Get( "sound.volume", sound_volume );
	sound.SetMasterVolume( sound_volume );
}

// general OpenGL initialization function
void InitGL( GLvoid )
{
	//warning:  this must be done BEFORE anything important
	//or else it will override it
	font.Load();
	
	// Enable Texture Mapping ( NEW )
	glEnable( GL_TEXTURE_2D );

	// Enable smooth shading
	glShadeModel( GL_SMOOTH );

	// Set the background black
	//glClearColor( 0.46f, 0.54f, 0.64f, 0.0f );
	glClearColor(0,0,0,0);

	// Depth buffer setup
	glClearDepth( 1.0f );

	// Enables Depth Testing
	glEnable( GL_DEPTH_TEST );

	// The Type Of Depth Test To Do
	glDepthFunc( GL_LEQUAL );

	// Really Nice Perspective Calculations
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

	// Position The Light
	glLightfv( GL_LIGHT1, GL_POSITION, LightPosition );
	
	// Setup The Diffuse Light
	glLightfv( GL_LIGHT1, GL_DIFFUSE, LightDiffuse );
	glLightfv( GL_LIGHT1, GL_SPECULAR, LightSpecular );
	
	// Setup The Ambient Light
	glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient );

	// Enable Light One
	glEnable( GL_LIGHT1 );
	
	// Enable Lighting
	glEnable( GL_LIGHTING );
	
	//our perspective matrix
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );
	gluPerspective( 45.0f, (float)SCREEN_WIDTH/SCREEN_HEIGHT, 0.1f, 10000.0f );
	glMatrixMode( GL_MODELVIEW );
	
	// Enable front face culling, since that's what Quake3 does
	//glCullFace(GL_FRONT);
	//glEnable(GL_CULL_FACE);
}

void LoadingScreen(string loadtext)
{
	if (verbose_output)
    	cout << loadtext << endl;

	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glPushMatrix();
	
	utility.Tex2D(3, false);
	utility.Tex2D(2, false);
	utility.Tex2D(1, false);
	utility.Tex2D(0, true);
	
	glSetup();
	
	glEnable(GL_TEXTURE_2D);
	
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear Screen And Depth Buffer

	Frames++;

	font.Print(0,0,loadtext.c_str(),1,6,1,1,1);
	
	// Draw it to the screen
	SDL_GL_SwapBuffers( );

	GLint t = SDL_GetTicks();
	//if (t - T0 >= 50) 
	{
		GLfloat seconds = (t - T0) / 1000.0;
		fps = 1 / seconds;
		//printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps);
		T0 = t;
		//Frames = 0;
	}
	
	glPopMatrix();
	glPopAttrib();
}

/*int RenderReflectedScene(bool underwater, float waterheight)
{
	backdrop.SetFogStrength(weathersystem.GetFogDensity());
	backdrop.RefreshFog();
	
	int sizex, sizey;
	sizex = REFLECT_RES_X;
	sizey = REFLECT_RES_Y;
	glViewport(0, 0, sizex, sizey);
	
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );
	gluPerspective( 70.0f, (float)SCREEN_WIDTH/SCREEN_HEIGHT, 0.1f, 10000.0f );
	glMatrixMode( GL_MODELVIEW );
	
	
	
	//backdrop.SetFogStrength(0.05f);
	
	backdrop.RefreshFog();
	
	#ifdef _WIN32
	//fix multitexturing state
	pglActiveTexture(GL_TEXTURE0_ARB);
	#else
	 //fix multitexturing state
	glActiveTexture(GL_TEXTURE0_ARB);
	#endif
	
	glSetup();
	
	//glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
	float clearcolor[4];
	backdrop.GetHorizonColor(clearcolor);
	clearcolor[3] = 0.0f;
	glClearColor( clearcolor[0], clearcolor[1], clearcolor[2], clearcolor[3] );
	
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear Screen And Depth Buffer
	
	GLdouble temp_matrix[16];
	
	
	
	//QUATERNION oldcamdir = cam.dir;
	//cam.Rotate(3.141593f, 0, 0, 1);
	
	
	
	cam.PutTransformInto(temp_matrix);
	glLoadMatrixd(temp_matrix);
	
	//cam.dir = oldcamdir;
	

	
	
	glPushMatrix();
	//glTranslatef(0, -5, 0);
	glScalef(1.0f,-1.0f,1.0f);
	backdrop.SetCloudTexture(terrain.GetCloudTexture());
	backdrop.DrawSky(true, day_time, 1.0f, underwater);
	glClear (GL_DEPTH_BUFFER_BIT);
	glPopMatrix();
	
	glTranslatef(cam.GetPosition().x,cam.GetPosition().y,cam.GetPosition().z);
	
	glTranslatef(0, waterheight*2, 0);
	
	glScalef(1.0f,-1.0f,1.0f);
	
	
	//reset sun position so it's in the correct frame
	float lp[4];
	lp[0] = LightPosition[0];
	lp[1] = LightPosition[1];
	lp[2] = LightPosition[2];
	lp[3] = 0;
	glLightfv( GL_LIGHT1, GL_POSITION, lp );

	//adjust light color to be dimmer for reflection
	float lc[4];
	float light_atten = 0.0f;
	if (underwater)
		light_atten = 1.0f;
	else
		light_atten = 0.1f;
	lc[0] = LightDiffuse[0]*light_atten;
	lc[1] = LightDiffuse[1]*light_atten;
	lc[2] = LightDiffuse[2]*light_atten;
	lc[3] = LightDiffuse[3]*light_atten;
	glLightfv( GL_LIGHT1, GL_DIFFUSE, lc );
	if (underwater)
		light_atten = 0.0f;
	else
		light_atten = 0.0f;
	lc[0] = LightDiffuse[0]*light_atten;
	lc[1] = LightDiffuse[1]*light_atten;
	lc[2] = LightDiffuse[2]*light_atten;
	lc[3] = LightDiffuse[3]*light_atten;
	glLightfv( GL_LIGHT1, GL_AMBIENT, lc );

	//put a clipping plane at the water level.
	//using a simple plane as below may not be good enough; the world is 
	// curved, so the plane should have the same normal as the spherical normal
	GLdouble equation[4];
	VERTEX temp;
	VERTEX earthnormal = temp - cam.position;
	earthnormal.y += EARTH_RADIUS;
	earthnormal = earthnormal.normalize();
	equation[0] = earthnormal.x;
	equation[1] = earthnormal.y;
	equation[2] = -earthnormal.z;
	equation[3] = -(waterheight);
	glClipPlane(GL_CLIP_PLANE1, equation);
	glEnable(GL_CLIP_PLANE1);
	
	terrain.SetFrustum(cam);
	
	terrain.Draw(cam, 10.0f, true, true, false, true, timefactor, fps, day_time);
	
	//terrain doubling to try to eliminate reflection artifacts...
	// introduces artifacts of its own
	//maybe only do this if underwater?
	glPushMatrix();
	glTranslatef(0, waterheight*2, 0);
	glScalef(1.0f, -1.0f, 1.0f);
	glClipPlane(GL_CLIP_PLANE1, equation);
	glLightfv( GL_LIGHT1, GL_POSITION, lp );
	terrain.Draw(cam, 10.0f, true, true, false, false, timefactor, fps, day_time);
	glPopMatrix();
	
	//restore light color
	glLightfv( GL_LIGHT1, GL_DIFFUSE, LightDiffuse );
	glLightfv( GL_LIGHT1, GL_SPECULAR, LightSpecular );
	glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient );

	//draw trees
	foliage.Draw(terrain, cam);
	
	equation[0] = earthnormal.x;
	equation[1] = earthnormal.y;
	equation[2] = -earthnormal.z;
	equation[3] = -(waterheight);
	glClipPlane(GL_CLIP_PLANE1, equation);
	
	
	//glDisable(GL_CLIP_PLANE1);
	//glScalef(1,-1,1);
	
	if (underwater)
	{
		glPushMatrix();
		glTranslatef(0, waterheight*2, 0);
		glScalef(1.0f, -1.0f, 1.0f);
		glClipPlane(GL_CLIP_PLANE1, equation);
		glLightfv( GL_LIGHT1, GL_POSITION, lp );
		ships.Draw(true);
		glPopMatrix();
	}
	else
	{
		glClipPlane(GL_CLIP_PLANE1, equation);
		ships.Draw(true);
	}
	
	
	
	
	
	glDisable(GL_CLIP_PLANE1);
	
	
	//redraw sky so that land reflections appear washed out
	if (!underwater)
	{
		float skytrans = 0.8f;
		cam.PutTransformInto(temp_matrix);
		glLoadMatrixd(temp_matrix);
		glScalef(1.0f,-1.0f,1.0f);
		backdrop.DrawSky(true, day_time, skytrans, underwater);
	}
	
	
	//add a brightness/contrast adjustment
	glClear (GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glDisable(GL_FOG);
	glDisable(GL_LIGHTING);
	glTranslatef(0,0,-40.0f);
	glBlendFunc(GL_ONE, GL_SRC_ALPHA);
	
	float scale = 0.8f;
	float bias = 0.0f;
	
  	glColor4f(bias, bias, bias, scale);
	float x1, y1, x2, y2;
	x1 = -80;
	y1 = -80;
	x2 = 80;
	y2 = 80;
	
	if (scale > 1.0) 
	{
		float remainingScale;
		
		remainingScale = scale;
		glBlendFunc(GL_DST_COLOR, GL_ONE);
		if (remainingScale > 2.0) 
		{
			glColor4f(1, 1, 1, 1);
			while (remainingScale > 2.0) 
			{
				glRectf(x1,y1,x2,y2);
				remainingScale /= 2.0;
			}
		}
		glColor4f(remainingScale - 1,
			remainingScale - 1, remainingScale - 1, 1);
		glRectf(x1,y1,x2,y2);
		glBlendFunc(GL_ONE, GL_ONE);
		if (bias != 0)
		{
			if (bias > 0) 
			{
				glColor4f(bias, bias, bias, 0.0);
			}
			else 
			{
				glColor4f(-bias, -bias, -bias, 0.0);
				//can't do bias < 0
			}
			glRectf(x1,y1,x2,y2);
		}
	}
	else 
	{
		if (bias > 0) 
		{
			glColor4f(bias, bias, bias, scale);
		}
		else 
		{
			glColor4f(-bias, -bias, -bias, scale);
			//can't do bias < 0
		}
		glBlendFunc(GL_ONE, GL_SRC_ALPHA);
		glRectf(x1,y1,x2,y2);
	}
	
	glPopAttrib();

	
	

	// Before we copy the screen to a texture, we need to specify the current
	// texture to draw to by calling glBindTexture() with the appropriate texture 
	glBindTexture(GL_TEXTURE_2D, terrain.reflection_texture);                

	// Now comes the moment we have all been waiting for, we render the screen
	// to the texture.  We pass in the texture type, detail level (0), pixel format,
	// the x and y position to start from, the width and height to grab, and a border.
	// If you only want a part of the screen, this works great for that.
	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, sizex, sizey, 0);

	// Here we clear the screen and depth bits of the small viewport
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            

	// Set our viewport back to it's normal size
	glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);    

	
    
	//glPopMatrix();
	
	return true;
}
*/

// Here goes our drawing code
int drawGLScene( GLvoid )
{
	#ifdef PERFORMANCE_PROFILE
	suseconds_t t1, t2;
	t1 = GetMicroSeconds();
	t1 = GetMicroSeconds();
	#endif
	
	//glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if (state.GetGameState() != STATE_INITIALMENU)
	{			
		backdrop.SetFogStrength(weathersystem.GetFogDensity());
		backdrop.RefreshFog();
	}
	
	#ifdef PERFORMANCE_PROFILE
	t2 = GetMicroSeconds();
	cout << "RenderReflectedScene() ticks: " << t2-t1 << endl;
	t1 = GetMicroSeconds();
	#endif
	
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );

	gluPerspective( 45.0f, (float)SCREEN_WIDTH/SCREEN_HEIGHT, 0.1f, view_dist );
	glMatrixMode( GL_MODELVIEW );
	
	glSetup();
	
	glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); 
	//glClearColor( 255.0f, 0.0f, 0.0f, 0.0f ); 
	
	#ifdef PERFORMANCE_PROFILE
	t2 = GetMicroSeconds();
	cout << "glSetup() ticks: " << t2-t1 << endl;
	t1 = GetMicroSeconds();
	#endif
	
	utility.Tex2D(3, false);
	utility.Tex2D(2, false);
	utility.Tex2D(1, false);
	utility.Tex2D(0, true);
	
	glStencilMask(~0);
	glClearStencil(0);
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);		// Clear Screen And Depth Buffer
	
	glEnable(GL_STENCIL_TEST);
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	glStencilFunc(GL_ALWAYS, 1, ~0);
	glDisable(GL_STENCIL_TEST);

	
	GLdouble temp_matrix[16];
	cam.PutTransformInto(temp_matrix);
	glLoadMatrixd(temp_matrix);
	
	//reset sun position so it's in the correct frame
	float lp[4];
	lp[0] = LightPosition[0];
	lp[1] = LightPosition[1];
	lp[2] = LightPosition[2];
	lp[3] = 0;
	glLightfv( GL_LIGHT1, GL_POSITION, lp );
	
	#ifdef PERFORMANCE_PROFILE
	t2 = GetMicroSeconds();
	cout << "Matrix setup and light setup ticks: " << t2-t1 << endl;
	t1 = GetMicroSeconds();
	#endif
	
	if (state.GetGameState() != STATE_INITIALMENU)
	{
		
		bool normal = true;		if (normal)
			backdrop.DrawSky(day_time, 1.0f);
		glClear (GL_DEPTH_BUFFER_BIT);
		
		#ifdef PERFORMANCE_PROFILE
		t2 = GetMicroSeconds();
		cout << "DrawSky() ticks: " << t2-t1 << endl;
		t1 = GetMicroSeconds();
		#endif
		
		glTranslatef(cam.GetPosition().x,cam.GetPosition().y,cam.GetPosition().z);
	
	
		
		if (MP_DBGDEEP)
			cout << "normal draw start" << endl;
		
//		terrain.SetFrustum(cam);
		
		#ifdef PERFORMANCE_PROFILE
		t2 = GetMicroSeconds();
		cout << "terrain.SetFrustum() ticks: " << t2-t1 << endl;
		t1 = GetMicroSeconds();
		#endif
		
		//glPolygonOffset(1.0,1.0);
		glPolygonOffset(0.0,10.0);
		glEnable(GL_POLYGON_OFFSET_FILL);
	
		if (normal)
		{
			//GLfloat LightAmbient2[]  = { 0.3f, 0.3f, 0.3f, 1.0f };
			//glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient2 );
			glDisable(GL_STENCIL_TEST);
			
		/*	if (utility.numTUs() > 3 && state.GetTreeDetail() != "Off" && state.GetTreeDetail() != "FoliageOnly")
			{
				utility.SelectTU(3);
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D, trees.GetCompositeShadow());
				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
				glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
				glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
				glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_MODULATE);
				glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
				utility.SelectTU(0);
			}*/

//			terrain.Draw(cam, 1.0f, timefactor, fps, day_time);

			#ifdef PERFORMANCE_PROFILE
			t2 = GetMicroSeconds();
			cout << "terrain.Draw() ticks: " << t2-t1 << endl;
			t1 = GetMicroSeconds();
			#endif

			utility.SelectTU(3);
			glDisable(GL_TEXTURE_2D);
			utility.SelectTU(0);
			glEnable(GL_TEXTURE_2D);

			glEnable(GL_STENCIL_TEST);
//			trees.Draw();
			#ifdef PERFORMANCE_PROFILE
			t2 = GetMicroSeconds();
			cout << "trees.Draw() ticks: " << t2-t1 << endl;
			t1 = GetMicroSeconds();
			#endif
			objects.Draw(track.GetCullFaces());
			
			#ifdef PATCH_DEBUG
			track.VisualizeRoads(true, false, NULL);
			#endif
			
			#ifdef PERFORMANCE_PROFILE
			t2 = GetMicroSeconds();
			cout << "objects.Draw() ticks: " << t2-t1 << endl;
			t1 = GetMicroSeconds();
			#endif

			//glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient );
		}

		if (MP_DBGDEEP)
			cout << "normal draw done" << endl;

		glDisable(GL_POLYGON_OFFSET_FILL);

		//glClear (GL_DEPTH_BUFFER_BIT);

		#ifdef PERFORMANCE_PROFILE
		t2 = GetMicroSeconds();
		cout << "normal draw done" << t2-t1 << endl;
		t1 = GetMicroSeconds();
		#endif


		//glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_STENCIL_TEST);
		if (MP_DBGDEEP)
			cout << "world draw start" << endl;
		world.Draw();
		if (MP_DBGDEEP)
			cout << "world draw done" << endl;

		if (MP_DBGDEEP)
			cout << "shadow draw start" << endl;

		/*glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_EQUAL, 0, ~0);
		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);*/
		world.DrawShadows();
		glDisable(GL_STENCIL_TEST);

		if (MP_DBGDEEP)
			cout << "shadow draw done" << endl;

		world.DrawCars();
		
		world.DrawTopLevel();

		#ifdef PATCH_DEBUG
		VERTEX c;
		c.Set(0,1,0);
		if (thepatch != NULL)
			thepatch->Visualize(true,true,c);
		#endif
		
		//image in the framebuffer is now complete.

		/*//add a brightness/contrast adjustment
		glClear (GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		glPushAttrib(GL_ALL_ATTRIB_BITS);
		glDisable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glDisable(GL_FOG);
		glDisable(GL_LIGHTING);
		glTranslatef(0,0,-40.0f);
		glBlendFunc(GL_ONE, GL_SRC_ALPHA);

		float rd = (float) weathersystem.GetRainDrops();

		float rainscale = 0.7f;
		float clearscale = 1.1f;
		float rainbias = 0.035f;
		float clearbias = 0.0f;
		float rainmax = 50.0f;

		float rainfactor = (rd/rainmax);
		if (rainfactor > 1.0f)
			rainfactor = 1.0f;

		float scale = rainfactor*rainscale+(1.0f-rainfactor)*clearscale;
		float bias = rainfactor*rainbias+(1.0f-rainfactor)*clearbias;

		glColor4f(bias, bias, bias, scale);
		float x1, y1, x2, y2;
		x1 = -30;
		y1 = -30;
		x2 = 30;
		y2 = 30;

		if (scale > 1.0) 
		{
			float remainingScale;
			
			remainingScale = scale;
			glBlendFunc(GL_DST_COLOR, GL_ONE);
			if (remainingScale > 2.0) 
			{
				glColor4f(1, 1, 1, 1);
				while (remainingScale > 2.0) 
				{
					glRectf(x1,y1,x2,y2);
					remainingScale /= 2.0;
				}
			}
			glColor4f(remainingScale - 1,
				remainingScale - 1, remainingScale - 1, 1);
			glRectf(x1,y1,x2,y2);
			glBlendFunc(GL_ONE, GL_ONE);
			if (bias != 0)
			{
				if (bias > 0) 
				{
					glColor4f(bias, bias, bias, 0.0);
				} 
				else 
				{
					glColor4f(-bias, -bias, -bias, 0.0);
					//can't do bias < 0
				}
				glRectf(x1,y1,x2,y2);
			}
		}
		else 
		{
			if (bias > 0) 
			{
				glColor4f(bias, bias, bias, scale);
			}
			else 
			{
				glColor4f(-bias, -bias, -bias, scale);
				//can't do bias < 0
			}
			glBlendFunc(GL_ONE, GL_SRC_ALPHA);
			glRectf(x1,y1,x2,y2);
		}

		glPopAttrib();*/

		//timer.Draw();
	}

	if (MP_DBGDEEP)
		cout << "menu draw start" << endl;

	glDisable(GL_STENCIL_TEST);

	if (fps > 0.0f)
		mq1.Draw(timefactor, fps, font);


	if( gui.GetEnabled() )
		gui.Draw();

	#ifdef PERFORMANCE_PROFILE
	t2 = GetMicroSeconds();
	cout << "Brightness/contrast adjustment ticks: " << t2-t1 << endl;
	t1 = GetMicroSeconds();
	#endif

	Frames++;
	frameno++;
	if (frameno >= 30011)
		frameno -= 30011;
	lfps[lfpspos] = pfps;
	lfpspos++;
	if (lfpspos >= AVERAGEFRAMES)
	{
		lfpspos = lfpspos % AVERAGEFRAMES;
		lfpsfull = true;
	}

	int i;

	float tfps = 0.0f;
	int tnum = 0;
	for (i = 0; i < AVERAGEFRAMES; i++)
	{
		if (!(!lfpsfull && i >= lfpspos))
		{
			tfps += lfps[i];
			tnum++;
		}
	}
	fps = tfps / (float) tnum;

	/*lfps += pfps;
	{
		//const int freq = (int) MIN_FPS;
		const int freq = 60;
		if (Frames >= freq)
		{
			fps = lfps / freq;
			Frames = 0;
			lfps = 0;
		}
	}*/

	char tempchar[1024];
	sprintf(tempchar, "Frames per second:  %f\n", 
		fps);

	//font.Print(0.5,0,tempchar,0,0,1,1,0);
	if (showfps)
		font.Print( 0.75, 0.0, tempchar, 1, 5, 1.0 );

	#ifdef PERFORMANCE_PROFILE
	t2 = GetMicroSeconds();
	cout << "font.Print() ticks: " << t2-t1 << endl;
	t1 = GetMicroSeconds();
	#endif

	// Draw it to the screen
	SDL_GL_SwapBuffers( );

	GLint t = SDL_GetTicks();
	//if (t - T0 >= 50) 
	{
		GLfloat seconds = (t - T0) / 1000.0;
		pfps = 1 / seconds;
		//printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps);
		T0 = t;
		//Frames = 0;
	}

	#ifdef PERFORMANCE_PROFILE
	t2 = GetMicroSeconds();
	cout << "SwapBuffers() ticks: " << t2-t1 << endl;
	t1 = GetMicroSeconds();
	cout << endl;
	#endif

	if (MP_DBGDEEP)
		cout << "menu draw done" << endl;

	return( TRUE );
}

// This function returns true if the extension is there.
bool isExtensionSupported(string extstring)
{
	char * temp = (char *) glGetString(GL_EXTENSIONS);
	if (temp == 0)
	{
		cout << "Error getting extensions.  Continuing anyway, errors may follow!" << endl;
		return true;
	}

	string s = temp;
	string::size_type temppos = s.find(extstring);

	bool hasext = (temppos <= s.length());

	if (!hasext)
	{
		cout << "Extension not supported: " << extstring << endl;
	}

	//cout << s << endl;

	return hasext;
}

void ResetWorld(bool fullreset)
{
	if (fullreset)
	{
		SelectCar(state.GetCarName(0), true);
		
		timer.Reset();
	}

	world.reset();
}

int main( int argc, char **argv )
{
	const vector<string> args(argv, argv + argc);
// catch fpe exceptions.
//	feenableexcept(FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);

	// Flags to pass to SDL_SetVideoMode
	int videoFlags = 0;
	// main loop variable
	int done = FALSE;
	// used to collect events
	SDL_Event event;
	// this holds some info about our display
	const SDL_VideoInfo *videoInfo;
	// whether or not the window is active
	int isActive = TRUE;
	// should we run a benchmark test and then quit?
	bool benchmode = false;

	surface = NULL;

	//load error log
	error_log.open((settings.GetSettingsDir() + "/logs/main.log").c_str());

	//create trig lookups
	//CreateLookupTable();
	
	for (vector<string>::const_iterator i = args.begin(); i != args.end(); ++i)
	{
		if (*i == "-verbose")
			verbose_output = true;
		if( *i == "-benchmark")
			benchmode = true;
	}
	
	 #ifdef ENABLE_NLS
 		
 		if (verbose_output)
 			cout << settings.GetLocaleDir () << std::endl;
 		
 		setlocale (LC_MESSAGES, ""); // only LC_MESSAGES, LC_ALL breaks menus
 		bindtextdomain (PACKAGE, (settings.GetLocaleDir ()).c_str ());
 		textdomain (PACKAGE);
 	#endif

	// parse options file
	settings.ParseOptionFile();

	float sound_volume = 0.5;
	settings.Get( "sound.volume", sound_volume );
	sound.SetMasterVolume( sound_volume );

	mouse.UpdateSettings();
	world.UpdateSettings();
	multiplay.UpdateSettings();
	state.UpdateSettings();
	objects.UpdateSettings();

	for (vector<string>::const_iterator i = args.begin(); i != args.end(); ++i)
	{
		if (*i == "-nosound")
			sound.DisableAllSound();
	}

/*
	bool setdir = false;
	for (vector<string>::const_iterator i = args.begin(); i != args.end(); ++i)
	{
		if (*i == "-datadir")
			setdir = true;
		else
		{
			if (setdir)
			{
				string new_data_dir = *i;
				cout << "Changing data directory to \"" + new_data_dir + "\"..." << endl;
				settings.SetDefaultDataDir(new_data_dir);
				cout << "Done. Now re-run vdrift normally." << endl;
				setdir = false;
				Quit(0);
			}
		}
	}
	if( setdir )
	{
		cout << "You must specify a path with the -datadir and option!" << endl;
		Quit(1);
	}

	for (vector<string>::const_iterator i = args.begin(); i != args.end(); ++i)
	{
		if (*i == "-defaultdatadir")
			setdir = true;
		else
		{
			if (setdir)
			{
				string new_data_dir = *i;
				cout << "Changing default data directory to \"" + new_data_dir + "\"..." << endl;
				settings.SetDataDir(new_data_dir);
				cout << "Done. Now delete your \"~/.vdrift/VDrift.config\" and re-run vdrift normally." << endl;
				setdir = false;
				Quit(0);
			}
		}
	}
	if( setdir )
	{
		cout << "You must specify a path with the -defaultdatadir option!" << endl;
		Quit(1);
	}
*/
	//set frame stats to zero
	Frames = 0;
	T0 = 0;
	fps = 0;
	frameno = 0;

        try
        {

		// initialize SDL
#ifdef DEBUG
		if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0 )
#else
		if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0 )
#endif
		{
			throw INIT_ERROR(string("SDL initilization failed (") + SDL_GetError() + ")");
		}

		/*
		cout << _c("Run with -verbose for troubleshooting.") << endl;
		cout << _c("Run with -nosound to disable sound.") << endl;
		cout << _c("Run with -datadir /path/to/vdrift/data to change the data directory in your local settings.") << endl;
		cout << _c("Run with -defaultdatadir /path/to/vdrift/data (as root) to change the data directory system-wide.") << endl;
		cout << _c("Note: after running \"vdrift -defaultdatadir ...\" as root, delete your ~/.vdrift/VDrift.config file.") << endl;
		*/

		printf("%i joystick(s) found:\n", SDL_NumJoysticks() );
		for(int i=0; i < SDL_NumJoysticks(); i++ ) 
		{
			printf("    %i. %s\n", i, SDL_JoystickName(i));
		}
		/*SDL_Joystick *joystick;
		SDL_JoystickEventState(SDL_ENABLE);
		joystick = SDL_JoystickOpen(0);
		printf("Joystick 0 opened.\n\n");*/

		SDL_JoystickEventState(SDL_ENABLE);

		gamecontrols.InitJoy();

		SDL_WM_SetCaption(_c("VDrift - open source drift racing simulation - powered by Vamos"), NULL);
		SDL_WM_SetIcon(IMG_Load(settings.GetFullDataPath("textures/small/icons/vdrift-32x32.png").c_str()), NULL);
		SDL_ShowCursor(SDL_DISABLE);

		// Fetch the video info
		videoInfo = SDL_GetVideoInfo( );

		if ( !videoInfo )
		{
			throw INIT_ERROR(string("Video query failed (") + SDL_GetError() + ")");
		}

		// the flags to pass to SDL_SetVideoMode
		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

		// This checks to see if surfaces can be stored in memory
		if ( videoInfo->hw_available )
			videoFlags |= SDL_HWSURFACE;
		else
			videoFlags |= SDL_SWSURFACE;

		// This checks if hardware blits can be done
		if( videoInfo->blit_hw )
			videoFlags |= SDL_HWACCEL;

		// Sets up OpenGL double buffering
		SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
	    
		//set up the depth buffer to be 16 bits, this is enough
		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );

		// set up our screen
		settings.Get( "display.width", cur_screen_w );
		settings.Get( "display.height", cur_screen_h );
		settings.Get( "display.depth", cur_screen_bpp );
		settings.Get( "display.texture_size", cur_tex_size );
		settings.Get( "display.fullscreen", cur_fullscreen );

		if( SDL_VideoModeOK( cur_screen_w, cur_screen_h, cur_screen_bpp, videoFlags ) != 0 )
		{
			ChangeDisplay( cur_screen_w, cur_screen_h, cur_screen_bpp, cur_fullscreen, true );
		}
		else
		{
			surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, videoFlags );
		}

		// Verify there is a surface
		if( !surface )
		{
			throw INIT_ERROR("Surface creation failed.");
		}

		// initialize OpenGL
		LoadingScreen( "Loading...\nInitializing OpenGL" );
		InitGL();
		LoadingScreen( "Loading...\nInitializing Game Data" );
		InitGameData();

		// resize the initial window
		resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT );
		
		gfxcard.BuildCardInfo();
		gfxcard.PrintCardInfo(cout);

		if( !( isExtensionSupported( "GL_ARB_multitexture" ) ) )
		{
			throw INIT_ERROR("Required OpenGL extensions not present.");
		}
	}
	catch (const INIT_ERROR& e)
	{
		cerr << "Error initilizing VDrift: " << e.what() << "\n";
		Quit(1);
	}

	if( benchmode )
	{
		//run a benchmark and quit
		if( !replay.PlayStart("benchmark") )
		{
			cout << "Can't find the benchmark replay file (benchmark.vdr)!" << endl;
		}
		
		Quit( 1 );
	}

	// now we have set up a window etc. - we can get video modes now so it's ok to parse the option file
	LoadingScreen( "Loading...\nParsing Dynamic Options" );
	settings.ParseDynamicOptions();

	LoadingScreen( "Loading...\nUser Interface" );
	gui.Init();
	gui.ChangePage( "Main" );

	// enable the user interface
	LoadingScreen( "Loading...\nDone" );
	gui.SetEnabled( true );

	// see if the framerate counter is on
	settings.Get( "display.show_fps", showfps );

	timefactor = 1.0f;
	
	// wait for events
	while( !done )
	{
		// handle the events in the queue

		while( SDL_PollEvent( &event ) )
		{
			float v;
			
			switch( event.type )
			{
			case SDL_ACTIVEEVENT:
				// Something's happend with our focus
				// If we lost focus or we are iconified, we
				// shouldn't draw the screen

				if ( event.active.gain == 0 )
					isActive = FALSE;
				else
					isActive = TRUE;
				break;			    
			case SDL_VIDEORESIZE:
				// handle resize event
				surface = SDL_SetVideoMode( event.resize.w,
							event.resize.h,
							16, videoFlags );
				if ( !surface )
				{
					fprintf( stderr, "Could not get a surface after resize: %s\n", SDL_GetError( ) );
					Quit(1);
				}
				resizeWindow( event.resize.w, event.resize.h );
				break;
			case SDL_KEYDOWN:
				// handle key presses
				if( gui.GetEnabled() )
					gui.KeyPress( event.key.keysym.sym );
				handleKeyPress( &event.key.keysym );
				keyman.KeyDown(event.key.keysym.sym);
				break;
			case SDL_KEYUP:
				if( gui.GetEnabled() )
					gui.KeyRelease( event.key.keysym.sym );
				handleKeyRelease( &event.key.keysym );
				keyman.KeyUp(event.key.keysym.sym);
				break;
			case SDL_QUIT:
				// handle quit requests
				done = 1;
				break;
			case SDL_MOUSEBUTTONDOWN:
				if( gui.GetEnabled() )
					gui.MousePress();
				break;
			case SDL_MOUSEBUTTONUP:
				if( gui.GetEnabled() )
					gui.MouseRelease();
				break;
			case SDL_JOYBUTTONDOWN:
				if( gui.GetEnabled() )
					gui.JoyPress( event.jbutton.which, event.jbutton.button );
				//if (timefactor == 0.0f && !menu.InMenu())
				//if (timefactor == 0.0f && state.GetGameState() != STATE_MENU)
				if (timefactor == 0.0f && !gui.GetEnabled())
					MainUnpause();
				// TODO: replace this functionality in Gui
				//menu.AssignJoyButton(event.jbutton.which, event.jbutton.button);
				break;
			case SDL_JOYBUTTONUP:
				if( gui.GetEnabled() )
					gui.JoyRelease( event.jbutton.which, event.jbutton.button );
				break;
			case SDL_JOYAXISMOTION:
				v = event.jaxis.value / 32768.0f;
				if( /*( v > 0.1 || v < -0.1 ) &&*/ gui.GetEnabled() )
					gui.JoyMove( event.jaxis.which, event.jaxis.axis, v );
				// TODO: replace this functionality in Gui
					//menu.AssignJoyAxis(event.jaxis.which, event.jaxis.axis, v);
				break;
			default:
				break;
			}
		}

		if( gui.GetEnabled() )
			gui.JoyUpdate();

		// do the game logic & draw the screen
		//if ( isActive )
		{
			Update();
			if (MP_DBGDEEP)
				cout << "main draw start" << endl;
			drawGLScene( );
			if (MP_DBGDEEP)
				cout << "main draw done" << endl;
		}
	}
	
	gamecontrols.DeinitJoy();
	
	Quit(0);
	return 0; // never executed
}
