/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 * Start up of the Blender Player on GHOST.
 */

/** \file gameengine/GamePlayer/ghost/GPG_ghost.cpp
 *  \ingroup player
 */


#include <iostream>
#include <math.h>

#ifdef __linux__
#ifdef __alpha__
#include <signal.h>
#endif /* __alpha__ */
#endif /* __linux__ */

#ifdef __APPLE__
// Can't use Carbon right now because of double defined type ID (In Carbon.h and DNA_ID.h, sigh)
//#include <Carbon/Carbon.h>
//#include <CFBundle.h>
#endif // __APPLE__
#include "KX_KetsjiEngine.h"
#include "KX_PythonInit.h"
#include "KX_PythonMain.h"

/**********************************
* Begin Blender include block
**********************************/
#ifdef __cplusplus
extern "C"
{
#endif  // __cplusplus
#include "MEM_guardedalloc.h"
#include "BKE_blender.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_library.h"
#include "BLI_threads.h"
#include "BLI_blenlib.h"
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
#include "BLO_readfile.h"
#include "BLO_runtime.h"
#include "IMB_imbuf.h"
#include "BKE_text.h"
#include "BKE_sound.h"
	
	int GHOST_HACK_getFirstFile(char buf[]);
	
// For BLF
#include "BLF_api.h"
#include "BLF_translation.h"
extern int datatoc_bfont_ttf_size;
extern char datatoc_bfont_ttf[];

#ifdef __cplusplus
}
#endif // __cplusplus

#include "GPU_draw.h"

/**********************************
* End Blender include block
**********************************/

#include "BL_System.h"
#include "GPG_Application.h"

#include "GHOST_ISystem.h"
#include "RAS_IRasterizer.h"

#include "BKE_main.h"

#include "RNA_define.h"

#ifdef WIN32
#include <windows.h>
#if !defined(DEBUG)
#include <wincon.h>
#endif // !defined(DEBUG)
#endif // WIN32

const int kMinWindowWidth = 100;
const int kMinWindowHeight = 100;

static void mem_error_cb(const char *errorStr)
{
	fprintf(stderr, "%s", errorStr);
	fflush(stderr);
}

#ifdef WIN32
typedef enum {
	SCREEN_SAVER_MODE_NONE = 0,
	SCREEN_SAVER_MODE_PREVIEW,
	SCREEN_SAVER_MODE_SAVER,
	SCREEN_SAVER_MODE_CONFIGURATION,
	SCREEN_SAVER_MODE_PASSWORD,
} ScreenSaverMode;

static ScreenSaverMode scr_saver_mode = SCREEN_SAVER_MODE_NONE;
static HWND scr_saver_hwnd = NULL;

static BOOL scr_saver_init(int argc, char **argv) 
{
	scr_saver_mode = SCREEN_SAVER_MODE_NONE;
	scr_saver_hwnd = NULL;
	BOOL ret = FALSE;

	int len = ::strlen(argv[0]);
	if (len > 4 && !::stricmp(".scr", argv[0] + len - 4))
	{
		scr_saver_mode = SCREEN_SAVER_MODE_CONFIGURATION;
		ret = TRUE;
		if (argc >= 2)
		{
			if (argc >= 3)
			{
				scr_saver_hwnd = (HWND) ::atoi(argv[2]);
			}
			if (!::stricmp("/c", argv[1]))
			{
				scr_saver_mode = SCREEN_SAVER_MODE_CONFIGURATION;
				if (scr_saver_hwnd == NULL)
					scr_saver_hwnd = ::GetForegroundWindow();
			}
			else if (!::stricmp("/s", argv[1]))
			{
				scr_saver_mode = SCREEN_SAVER_MODE_SAVER;
			}
			else if (!::stricmp("/a", argv[1]))
			{
				scr_saver_mode = SCREEN_SAVER_MODE_PASSWORD;
			}
			else if (!::stricmp("/p", argv[1])
				 || !::stricmp("/l", argv[1]))
			{
				scr_saver_mode = SCREEN_SAVER_MODE_PREVIEW;
			}
		}
	}
	return ret;
}

#endif /* WIN32 */

static void usage(const char* program, bool isBlenderPlayer)
{
	const char * consoleoption;
	const char * example_filename = "";
	const char * example_pathname = "";

#ifdef _WIN32
	consoleoption = "-c ";
#else
	consoleoption = "";
#endif

	if (isBlenderPlayer) {
		example_filename = "filename.blend";
#ifdef _WIN32
		example_pathname = "c:\\";
#else
		example_pathname = "/home/user/";
#endif
	}
	
	printf("usage:   %s [-w [w h l t]] [-f [fw fh fb ff]] %s[-g gamengineoptions] "
	       "[-s stereomode] [-m aasamples] %s\n", program, consoleoption, example_filename);
	printf("  -h: Prints this command summary\n\n");
	printf("  -w: display in a window\n");
	printf("       --Optional parameters--\n"); 
	printf("       w = window width\n");
	printf("       h = window height\n\n");
	printf("       l = window left coordinate\n");
	printf("       t = window top coordinate\n");
	printf("       Note: If w or h is defined, both must be defined.\n");
	printf("          Also, if l or t is defined, all options must be used.\n\n");
	printf("  -f: start game in full screen mode\n");
	printf("       --Optional parameters--\n");
	printf("       fw = full screen mode pixel width\n");
	printf("       fh = full screen mode pixel height\n\n");
	printf("       fb = full screen mode bits per pixel\n");
	printf("       ff = full screen mode frequency\n");
	printf("       Note: If fw or fh is defined, both must be defined.\n");
	printf("          Also, if fb is used, fw and fh must be used. ff requires all options.\n\n");
	printf("  -s: start player in stereo\n");
	printf("       stereomode: hwpageflip       (Quad buffered shutter glasses)\n");
	printf("                   syncdoubling     (Above Below)\n");
	printf("                   sidebyside       (Left Right)\n");
	printf("                   anaglyph         (Red-Blue glasses)\n");
	printf("                   vinterlace       (Vertical interlace for autostereo display)\n");
	printf("                             depending on the type of stereo you want\n\n");
	printf("  -D: start player in dome mode\n");
	printf("       --Optional parameters--\n");
	printf("       angle    = field of view in degrees\n");
	printf("       tilt     = tilt angle in degrees\n");
	printf("       warpdata = a file to use for warping the image (absolute path)\n");
	printf("       mode: fisheye                (Fisheye)\n");
	printf("             truncatedfront         (Front-Truncated)\n");
	printf("             truncatedrear          (Rear-Truncated)\n");
	printf("             cubemap                (Cube Map)\n");
	printf("             sphericalpanoramic     (Spherical Panoramic)\n");
	printf("                             depending on the type of dome you are using\n\n");
	printf("  -m: maximum anti-aliasing (eg. 2,4,8,16)\n\n");
	printf("  -i: parent windows ID\n\n");
#ifdef _WIN32
	printf("  -c: keep console window open\n\n");
#endif
	printf("  -d: turn debugging on\n\n");
	printf("  -g: game engine options:\n\n");
	printf("       Name                       Default      Description\n");
	printf("       ------------------------------------------------------------------------\n");
	printf("       fixedtime                      0         \"Enable all frames\"\n");
	printf("       nomipmap                       0         Disable mipmaps\n");
	printf("       show_framerate                 0         Show the frame rate\n");
	printf("       show_properties                0         Show debug properties\n");
	printf("       show_profile                   0         Show profiling information\n");
	printf("       blender_material               0         Enable material settings\n");
	printf("       ignore_deprecation_warnings    1         Ignore deprecation warnings\n");
	printf("\n");
	printf("  - : all arguments after this are ignored, allowing python to access them from sys.argv\n");
	printf("\n");
	printf("example: %s -w 320 200 10 10 -g noaudio%s%s\n", program, example_pathname, example_filename);
	printf("example: %s -g show_framerate = 0 %s%s\n", program, example_pathname, example_filename);
	printf("example: %s -i 232421 -m 16 %s%s\n\n", program, example_pathname, example_filename);
}

static void get_filename(int argc, char **argv, char *filename)
{
#ifdef __APPLE__
	/* On Mac we park the game file (called game.blend) in the application bundle.
	 * The executable is located in the bundle as well.
	 * Therefore, we can locate the game relative to the executable.
	 */
	int srclen = ::strlen(argv[0]);
	int len = 0;
	char *gamefile = NULL;
	
	filename[0] = '\0';

	if (argc > 1) {
		if (BLI_exists(argv[argc-1])) {
			BLI_strncpy(filename, argv[argc-1], FILE_MAX);
		}
		if (::strncmp(argv[argc-1], "-psn_", 5)==0) {
			static char firstfilebuf[512];
			if (GHOST_HACK_getFirstFile(firstfilebuf)) {
				BLI_strncpy(filename, firstfilebuf, FILE_MAX);
			}
		}                        
	}
	
	srclen -= ::strlen("MacOS/blenderplayer");
	if (srclen > 0) {
		len = srclen + ::strlen("Resources/game.blend"); 
		gamefile = new char [len + 1];
		::strcpy(gamefile, argv[0]);
		::strcpy(gamefile + srclen, "Resources/game.blend");
		//::printf("looking for file: %s\n", filename);
		
		if (BLI_exists(gamefile))
			BLI_strncpy(filename, gamefile, FILE_MAX);

		delete [] gamefile;
	}
	
#else
	filename[0] = '\0';

	if (argc > 1)
		BLI_strncpy(filename, argv[argc-1], FILE_MAX);
#endif // !_APPLE
}

static BlendFileData *load_game_data(const char *progname, char *filename = NULL, char *relativename = NULL)
{
	ReportList reports;
	BlendFileData *bfd = NULL;

	BKE_reports_init(&reports, RPT_STORE);
	
	/* try to load ourself, will only work if we are a runtime */
	if (BLO_is_a_runtime(progname)) {
		bfd= BLO_read_runtime(progname, &reports);
		if (bfd) {
			bfd->type= BLENFILETYPE_RUNTIME;
			BLI_strncpy(bfd->main->name, progname, sizeof(bfd->main->name));
		}
	} else {
		bfd= BLO_read_from_file(progname, &reports);
	}
	
	if (!bfd && filename) {
		bfd = load_game_data(filename);
		if (!bfd) {
			printf("Loading %s failed: ", filename);
			BKE_reports_print(&reports, RPT_ERROR);
		}
	}

	BKE_reports_clear(&reports);
	
	return bfd;
}

static bool GPG_NextFrame(GHOST_ISystem* system, GPG_Application *app, int &exitcode, STR_String &exitstring, GlobalSettings *gs)
{
	bool run = true;
	system->processEvents(false);
	system->dispatchEvents();
	app->EngineNextFrame();
	if ((exitcode = app->getExitRequested())) {
		run = false;
		exitstring = app->getExitString();
		*gs = *app->getGlobalSettings();
	}
	return run;
}

struct GPG_NextFrameState {
	GHOST_ISystem* system;
	GPG_Application *app;
	GlobalSettings *gs;
} gpg_nextframestate;

static int GPG_PyNextFrame(void *state0)
{
	GPG_NextFrameState *state = (GPG_NextFrameState *) state0;
	int exitcode;
	STR_String exitstring;
	bool run = GPG_NextFrame(state->system, state->app, exitcode, exitstring, state->gs);
	if (run) return 0;  
	else {
		if (exitcode) 
			fprintf(stderr, "Exit code %d: %s\n", exitcode, exitstring.ReadPtr());
		return 1;
	}
}

int main(int argc, char** argv)
{
	int i;
	int argc_py_clamped= argc; /* use this so python args can be added after ' - ' */
	bool error = false;
	SYS_SystemHandle syshandle = SYS_GetSystem();
	bool fullScreen = false;
	bool fullScreenParFound = false;
	bool windowParFound = false;
#ifdef WIN32
	bool closeConsole = true;
#endif
	RAS_IRasterizer::StereoMode stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;
	bool stereoWindow = false;
	bool stereoParFound = false;
	int stereoFlag = STEREO_NOSTEREO;
	int domeFov = -1;
	int domeTilt = -200;
	int domeMode = 0;
	char* domeWarp = NULL;
	Text *domeText  = NULL;
	int windowLeft = 100;
	int windowTop = 100;
	int windowWidth = 640;
	int windowHeight = 480;
	GHOST_TUns32 fullScreenWidth = 0;
	GHOST_TUns32 fullScreenHeight= 0;
	int fullScreenBpp = 32;
	int fullScreenFrequency = 60;
	GHOST_TEmbedderWindowID parentWindow = 0;
	bool isBlenderPlayer = false;
	int validArguments=0;
	bool samplesParFound = false;
	GHOST_TUns16 aasamples = 0;
	
#ifdef __linux__
#ifdef __alpha__
	signal (SIGFPE, SIG_IGN);
#endif /* __alpha__ */
#endif /* __linux__ */
	BLI_init_program_path(argv[0]);
	BLI_init_temporary_dir(NULL);
#ifdef __APPLE__
	// Can't use Carbon right now because of double defined type ID (In Carbon.h and DNA_ID.h, sigh)
	/*
	IBNibRef 		nibRef;
	WindowRef 		window;
	OSStatus		err;

	  // Create a Nib reference passing the name of the nib file (without the .nib extension)
	  // CreateNibReference only searches into the application bundle.
	  err = ::CreateNibReference(CFSTR("main"), &nibRef);
	  if (err) return -1;
	  
		// Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar
		// object. This name is set in InterfaceBuilder when the nib is created.
		err = ::SetMenuBarFromNib(nibRef, CFSTR("MenuBar"));
		if (err) return -1;
		
		  // We don't need the nib reference anymore.
		  ::DisposeNibReference(nibRef);
	*/
#endif // __APPLE__
	
	// We don't use threads directly in the BGE, but we need to call this so things like
	// freeing up GPU_Textures works correctly.
	BLI_threadapi_init();

	RNA_init();

	init_nodesystem();
	
	initglobals();

	U.gameflags |= USER_DISABLE_VBO;
	// We load our own G.main, so free the one that initglobals() gives us
	free_main(G.main);
	G.main = NULL;

	IMB_init();
	BKE_images_init();

#ifdef WITH_FFMPEG
	IMB_ffmpeg_init();
#endif

	// Setup builtin font for BLF (mostly copied from creator.c, wm_init_exit.c and interface_style.c)
	BLF_init(11, U.dpi);
	BLF_lang_init();
	BLF_lang_set("");

	BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size);

	// Parse command line options
#if defined(DEBUG)
	printf("argv[0] = '%s'\n", argv[0]);
#endif

#ifdef WIN32
	if (scr_saver_init(argc, argv))
	{
		switch (scr_saver_mode)
		{
		case SCREEN_SAVER_MODE_CONFIGURATION:
			MessageBox(scr_saver_hwnd, "This screen saver has no options that you can set", "Screen Saver", MB_OK);
			break;
		case SCREEN_SAVER_MODE_PASSWORD:
			/* This is W95 only, which we currently do not support.
			 * Fall-back to normal screen saver behavior in that case... */
		case SCREEN_SAVER_MODE_SAVER:
			fullScreen = true;
			fullScreenParFound = true;
			break;

		case SCREEN_SAVER_MODE_PREVIEW:
			/* This will actually be handled somewhere below... */
			break;
		}
	}
#endif
	// XXX add the ability to change this values to the command line parsing.
	U.mixbufsize = 2048;
	U.audiodevice = 2;
	U.audiorate = 44100;
	U.audioformat = 0x24;
	U.audiochannels = 2;

	// XXX this one too
	U.anisotropic_filter = 2;
	// enable fast mipmap generation
	U.use_gpu_mipmap = 1;

	sound_init_once();

	/* if running blenderplayer the last argument can't be parsed since it has to be the filename. */
	isBlenderPlayer = !BLO_is_a_runtime(argv[0]);
	if (isBlenderPlayer)
		validArguments = argc - 1;
	else
		validArguments = argc;

	for (i = 1; (i < validArguments) && !error 
#ifdef WIN32
		&& scr_saver_mode == SCREEN_SAVER_MODE_NONE
#endif
		;)

	{
#if defined(DEBUG)
		printf("argv[%d] = '%s', %i\n", i, argv[i],argc);
#endif
		if (argv[i][0] == '-')
		{
			/* ignore all args after " - ", allow python to have own args */
			if (argv[i][1]=='\0') {
				argc_py_clamped= i;
				break;
			}
			
			switch (argv[i][1])
			{
			case 'g':
				// Parse game options
				{
					i++;
					if (i <= validArguments)
					{
						char* paramname = argv[i];
						// Check for single value versus assignment
						if (i+1 <= validArguments && (*(argv[i+1]) == '='))
						{
							i++;
							if (i + 1 <= validArguments)
							{
								i++;
								// Assignment
								SYS_WriteCommandLineInt(syshandle, paramname, atoi(argv[i]));
								SYS_WriteCommandLineFloat(syshandle, paramname, atof(argv[i]));
								SYS_WriteCommandLineString(syshandle, paramname, argv[i]);
#if defined(DEBUG)
								printf("%s = '%s'\n", paramname, argv[i]);
#endif
								i++;
							}
							else
							{
								error = true;
								printf("error: argument assignment %s without value.\n", paramname);
							}
						}
						else
						{
//							SYS_WriteCommandLineInt(syshandle, argv[i++], 1);
						}
					}
				}
				break;

			case 'd':
				i++;
				G.debug |= G_DEBUG;     /* std output printf's */
				MEM_set_memory_debug();
				break;

			case 'f':
				i++;
				fullScreen = true;
				fullScreenParFound = true;
				if ((i + 2) <= validArguments && argv[i][0] != '-' && argv[i+1][0] != '-')
				{
					fullScreenWidth = atoi(argv[i++]);
					fullScreenHeight = atoi(argv[i++]);
					if ((i + 1) <= validArguments && argv[i][0] != '-')
					{
						fullScreenBpp = atoi(argv[i++]);
						if ((i + 1) <= validArguments && argv[i][0] != '-')
							fullScreenFrequency = atoi(argv[i++]);
					}
				}
				break;
			case 'w':
				// Parse window position and size options
				i++;
				fullScreen = false;
				windowParFound = true;

				if ((i + 2) <= validArguments && argv[i][0] != '-' && argv[i+1][0] != '-')
				{
					windowWidth = atoi(argv[i++]);
					windowHeight = atoi(argv[i++]);
					if ((i + 2) <= validArguments && argv[i][0] != '-' && argv[i+1][0] != '-')
					{
						windowLeft = atoi(argv[i++]);
						windowTop = atoi(argv[i++]);
					}
				}
				break;
					
			case 'h':
				usage(argv[0], isBlenderPlayer);
				return 0;
				break;
			case 'i':
				i++;
				if ( (i + 1) <= validArguments )
					parentWindow = atoi(argv[i++]);
				else {
					error = true;
					printf("error: too few options for parent window argument.\n");
				}
#if defined(DEBUG)
				printf("XWindows ID = %d\n", parentWindow);
#endif // defined(DEBUG)
				break;
			case 'm':
				i++;
				samplesParFound = true;
				if ((i+1) <= validArguments )
					aasamples = atoi(argv[i++]);
				else
				{
					error = true;
					printf("error: No argument supplied for -m");
				}
				break;
			case 'c':
				i++;
#ifdef WIN32
				closeConsole = false;
#endif
				break;
			case 's':  // stereo
				i++;
				if ((i + 1) <= validArguments)
				{
					stereomode = (RAS_IRasterizer::StereoMode) atoi(argv[i]);
					if (stereomode < RAS_IRasterizer::RAS_STEREO_NOSTEREO || stereomode >= RAS_IRasterizer::RAS_STEREO_MAXSTEREO)
						stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;

					if (!strcmp(argv[i], "nostereo"))  // ok, redundant but clear
						stereomode = RAS_IRasterizer::RAS_STEREO_NOSTEREO;

					// only the hardware pageflip method needs a stereo window
					else if (!strcmp(argv[i], "hwpageflip")) {
						stereomode = RAS_IRasterizer::RAS_STEREO_QUADBUFFERED;
						stereoWindow = true;
					}
					else if (!strcmp(argv[i], "syncdoubling"))
						stereomode = RAS_IRasterizer::RAS_STEREO_ABOVEBELOW;

					else if (!strcmp(argv[i], "anaglyph"))
						stereomode = RAS_IRasterizer::RAS_STEREO_ANAGLYPH;

					else if (!strcmp(argv[i], "sidebyside"))
						stereomode = RAS_IRasterizer::RAS_STEREO_SIDEBYSIDE;

					else if (!strcmp(argv[i], "vinterlace"))
						stereomode = RAS_IRasterizer::RAS_STEREO_VINTERLACE;

#if 0
//					// future stuff
//					else if (!strcmp(argv[i], "stencil")
//						stereomode = RAS_STEREO_STENCIL;
#endif

					i++;
					stereoParFound = true;
					stereoFlag = STEREO_ENABLED;
				}
				else
				{
					error = true;
					printf("error: too few options for stereo argument.\n");
				}
				break;
			case 'D':
				stereoFlag = STEREO_DOME;
				stereomode = RAS_IRasterizer::RAS_STEREO_DOME;
				i++;
				if ((i + 1) <= validArguments)
				{
					if (!strcmp(argv[i], "angle")) {
						i++;
						domeFov = atoi(argv[i++]);
					}
					if (!strcmp(argv[i], "tilt")) {
						i++;
						domeTilt = atoi(argv[i++]);
					}
					if (!strcmp(argv[i], "warpdata")) {
						i++;
						domeWarp = argv[i++];
					}
					if (!strcmp(argv[i], "mode")) {
						i++;
						if (!strcmp(argv[i], "fisheye"))
							domeMode = DOME_FISHEYE;
							
						else if (!strcmp(argv[i], "truncatedfront"))
							domeMode = DOME_TRUNCATED_FRONT;
							
						else if (!strcmp(argv[i], "truncatedrear"))
							domeMode = DOME_TRUNCATED_REAR;
							
						else if (!strcmp(argv[i], "cubemap"))
							domeMode = DOME_ENVMAP;
							
						else if (!strcmp(argv[i], "sphericalpanoramic"))
							domeMode = DOME_PANORAM_SPH;

						else
							printf("error: %s is not a valid dome mode.\n", argv[i]);
					}
					i++;
				}
				break;
			default:
				printf("Unknown argument: %s\n", argv[i++]);
				break;
			}
		}
		else
		{
			i++;
		}
	}

	if ((windowWidth < kMinWindowWidth) || (windowHeight < kMinWindowHeight))
	{
		error = true;
		printf("error: window size too small.\n");
	}
	
	if (error )
	{
		usage(argv[0], isBlenderPlayer);
		return 0;
	}

#ifdef WIN32
	if (scr_saver_mode != SCREEN_SAVER_MODE_CONFIGURATION)
#endif
	{
#ifdef __APPLE__
		//SYS_WriteCommandLineInt(syshandle, "show_framerate", 1);
		//SYS_WriteCommandLineInt(syshandle, "nomipmap", 1);
		//fullScreen = false;		// Can't use full screen
#endif

		if (SYS_GetCommandLineInt(syshandle, "nomipmap", 0)) {
			GPU_set_mipmap(0);
		}

		GPU_set_anisotropic(U.anisotropic_filter);
		GPU_set_gpu_mipmapping(U.use_gpu_mipmap);
		
		// Create the system
		if (GHOST_ISystem::createSystem() == GHOST_kSuccess) {
			GHOST_ISystem* system = GHOST_ISystem::getSystem();
			assertd(system);
			
			if (!fullScreenWidth || !fullScreenHeight)
				system->getMainDisplayDimensions(fullScreenWidth, fullScreenHeight);
			// process first batch of events. If the user
			// drops a file on top off the blenderplayer icon, we
			// receive an event with the filename
			
			system->processEvents(0);
			
			// this bracket is needed for app (see below) to get out
			// of scope before GHOST_ISystem::disposeSystem() is called.
			{
				int exitcode = KX_EXIT_REQUEST_NO_REQUEST;
				STR_String exitstring = "";
				GPG_Application app(system);
				bool firstTimeRunning = true;
				char filename[FILE_MAX];
				char pathname[FILE_MAX];
				char *titlename;

				get_filename(argc_py_clamped, argv, filename);
				if (filename[0])
					BLI_path_cwd(filename);
				

				// fill the GlobalSettings with the first scene files
				// those may change during the game and persist after using Game Actuator
				GlobalSettings gs;

				do {
					// Read the Blender file
					BlendFileData *bfd;
					
					// if we got an exitcode 3 (KX_EXIT_REQUEST_START_OTHER_GAME) load a different file
					if (exitcode == KX_EXIT_REQUEST_START_OTHER_GAME)
					{
						char basedpath[FILE_MAX];
						
						// base the actuator filename relative to the last file
						BLI_strncpy(basedpath, exitstring.Ptr(), sizeof(basedpath));
						BLI_path_abs(basedpath, pathname);
						
						bfd = load_game_data(basedpath);

						if (!bfd) {
							// just add "//" in front of it
							char temppath[242];
							strcpy(temppath, "//");
							strcat(temppath, basedpath);

							BLI_path_abs(temppath, pathname);
							bfd = load_game_data(temppath);
						}
					}
					else {
						bfd = load_game_data(BLI_program_path(), filename[0]? filename: NULL);
					}
					
					//::printf("game data loaded from %s\n", filename);
					
					if (!bfd) {
						usage(argv[0], isBlenderPlayer);
						error = true;
						exitcode = KX_EXIT_REQUEST_QUIT_GAME;
					}
					else {
#ifdef WIN32
#if !defined(DEBUG)
						if (closeConsole) {
							system->toggleConsole(0); // Close a console window
						}
#endif // !defined(DEBUG)
#endif // WIN32
						Main *maggie = bfd->main;
						Scene *scene = bfd->curscene;
						G.main = maggie;

						if (firstTimeRunning) {
							G.fileflags  = bfd->fileflags;

							gs.matmode= scene->gm.matmode;
							gs.glslflag= scene->gm.flag;
						}

						//Seg Fault; icon.c gIcons == 0
						BKE_icons_init(1);
						
						titlename = maggie->name;
						
						// Check whether the game should be displayed full-screen
						if ((!fullScreenParFound) && (!windowParFound)) {
							// Only use file settings when command line did not override
							if ((scene->gm.playerflag & GAME_PLAYER_FULLSCREEN)) {
								//printf("fullscreen option found in Blender file\n");
								fullScreen = true;
								fullScreenWidth= scene->gm.xplay;
								fullScreenHeight= scene->gm.yplay;
								fullScreenFrequency= scene->gm.freqplay;
								fullScreenBpp = scene->gm.depth;
							}
							else
							{
								fullScreen = false;
								windowWidth = scene->gm.xplay;
								windowHeight = scene->gm.yplay;
							}
						}
						
						
						// Check whether the game should be displayed in stereo
						if (!stereoParFound) {
							if (scene->gm.stereoflag == STEREO_ENABLED) {
								stereomode = (RAS_IRasterizer::StereoMode) scene->gm.stereomode;
								if (stereomode == RAS_IRasterizer::RAS_STEREO_QUADBUFFERED)
									stereoWindow = true;
							}
						}
						else {
							scene->gm.stereoflag = STEREO_ENABLED;
						}

						if (!samplesParFound)
							aasamples = scene->gm.aasamples;

						if (stereoFlag == STEREO_DOME) {
							stereomode = RAS_IRasterizer::RAS_STEREO_DOME;
							scene->gm.stereoflag = STEREO_DOME;
							if (domeFov > 89)
								scene->gm.dome.angle = domeFov;
							if (domeTilt > -180)
								scene->gm.dome.tilt = domeTilt;
							if (domeMode > 0)
								scene->gm.dome.mode = domeMode;
							if (domeWarp) {
								//XXX to do: convert relative to absolute path
								domeText= BKE_text_load(G.main, domeWarp, "");
								if (!domeText)
									printf("error: invalid warpdata text file - %s\n", domeWarp);
								else
									scene->gm.dome.warptext = domeText;
							}
						}
						
						//					GPG_Application app (system, maggie, startscenename);
						app.SetGameEngineData(maggie, scene, &gs, argc, argv); /* this argc cant be argc_py_clamped, since python uses it */
						BLI_strncpy(pathname, maggie->name, sizeof(pathname));
						if (G.main != maggie) {
							BLI_strncpy(G.main->name, maggie->name, sizeof(G.main->name));
						}
#ifdef WITH_PYTHON
						setGamePythonPath(G.main->name);
#endif
						if (firstTimeRunning) {
							firstTimeRunning = false;

							if (fullScreen) {
#ifdef WIN32
								if (scr_saver_mode == SCREEN_SAVER_MODE_SAVER)
								{
									app.startScreenSaverFullScreen(fullScreenWidth, fullScreenHeight, fullScreenBpp, fullScreenFrequency,
									                               stereoWindow, stereomode, aasamples);
								}
								else
#endif
								{
									app.startFullScreen(fullScreenWidth, fullScreenHeight, fullScreenBpp, fullScreenFrequency,
									                    stereoWindow, stereomode, aasamples, (scene->gm.playerflag & GAME_PLAYER_DESKTOP_RESOLUTION));
								}
							}
							else
							{
#ifdef __APPLE__
								// on Mac's we'll show the executable name instead of the 'game.blend' name
								char tempname[1024], *appstring;
								::strcpy(tempname, titlename);
								
								appstring = strstr(tempname, ".app/");
								if (appstring) {
									appstring[2] = 0;
									titlename = &tempname[0];
								}
#endif
								// Strip the path so that we have the name of the game file
								STR_String path = titlename;
#ifndef WIN32
								vector<STR_String> parts = path.Explode('/');
#else  // WIN32
								vector<STR_String> parts = path.Explode('\\');
#endif // WIN32                        
								STR_String title;
								if (parts.size()) {
									title = parts[parts.size()-1];
									parts = title.Explode('.');
									if (parts.size() > 1)
									{
										title = parts[0];
									}
								}
								else {
									title = "blenderplayer";
								}
#ifdef WIN32
								if (scr_saver_mode == SCREEN_SAVER_MODE_PREVIEW)
								{
									app.startScreenSaverPreview(scr_saver_hwnd, stereoWindow, stereomode, aasamples);
								}
								else
#endif
								{
									if (parentWindow != 0)
										app.startEmbeddedWindow(title, parentWindow, stereoWindow, stereomode, aasamples);
									else
										app.startWindow(title, windowLeft, windowTop, windowWidth, windowHeight,
										                stereoWindow, stereomode, aasamples);
								}
							}
						}
						else {
							app.StartGameEngine(stereomode);
							exitcode = KX_EXIT_REQUEST_NO_REQUEST;
						}
						
						// Add the application as event consumer
						system->addEventConsumer(&app);
						
						// Enter main loop
						bool run = true;
						char *python_main = NULL;
						pynextframestate.state = NULL;
						pynextframestate.func = NULL;
#ifdef WITH_PYTHON
						python_main = KX_GetPythonMain(scene);
#endif // WITH_PYTHON
						if (python_main) {
							char *python_code = KX_GetPythonCode(maggie, python_main);
							if (python_code) {
#ifdef WITH_PYTHON
								gpg_nextframestate.system = system;
								gpg_nextframestate.app = &app;
								gpg_nextframestate.gs = &gs;
								pynextframestate.state = &gpg_nextframestate;
								pynextframestate.func = &GPG_PyNextFrame;

								printf("Yielding control to Python script '%s'...\n", python_main);
								PyRun_SimpleString(python_code);
								printf("Exit Python script '%s'\n", python_main);
#endif // WITH_PYTHON
								MEM_freeN(python_code);
							}
							else {
								fprintf(stderr, "ERROR: cannot yield control to Python: no Python text data block named '%s'\n", python_main);
							}
						}
						else {
							while (run) {
								run = GPG_NextFrame(system, &app, exitcode, exitstring, &gs);
							}
						}
						app.StopGameEngine();

						/* 'app' is freed automatic when out of scope.
						 * removal is needed else the system will free an already freed value */
						system->removeEventConsumer(&app);

						BLO_blendfiledata_free(bfd);
						/* G.main == bfd->main, it gets referenced in free_nodesystem so we can't have a dangling pointer */
						G.main = NULL;
						if (python_main) MEM_freeN(python_main);
					}
				} while (exitcode == KX_EXIT_REQUEST_RESTART_GAME || exitcode == KX_EXIT_REQUEST_START_OTHER_GAME);
			}

			// Seg Fault; icon.c gIcons == 0
			BKE_icons_free();

			// Dispose the system
			GHOST_ISystem::disposeSystem();
		}
		else {
			error = true;
			printf("error: couldn't create a system.\n");
		}
	}

	/* refer to WM_exit_ext() and free_blender(),
	 * these are not called in the player but we need to match some of there behavior here,
	 * if the order of function calls or blenders state isn't matching that of blender proper,
	 * we may get troubles later on */

	free_nodesystem();

	// Cleanup
	RNA_exit();
	BLF_exit();

#ifdef WITH_INTERNATIONAL
	BLF_free_unifont();
	BLF_free_unifont_mono();
	BLF_lang_free();
#endif

	IMB_exit();
	BKE_images_exit();

	SYS_DeleteSystem(syshandle);

	int totblock= MEM_get_memory_blocks_in_use();
	if (totblock!=0) {
		printf("Error Totblock: %d\n",totblock);
		MEM_set_error_callback(mem_error_cb);
		MEM_printmemlist();
	}

	return error ? -1 : 0;
}
