// BlinkenSisters - Hunt for the Lost Pixels
//     Bringing back the fun of the 80s
//
// (C) 2005-07 Rene Schickbauer, Wolfgang Dautermann
//
// See LICENSE for licensing information
//

#include <SDL.h>
#include <stdlib.h>
#include <string.h>
#include "globals.h"
#include "sound.h"
#include "engine.h"
#include "showtext.h"
#include "debug.h"
#include "levelhandler.h"
#include "showloading.h"
#include "gameengine.h"
#include "joystick.h"
#include "cmdopts.h"
#include "errorhandler.h"
#include "fonthandler.h"
#include "menu.h"
#include "showvideo.h"
#include "minilzo.h"
#include "extractmetabmf.h"
#include "fginlay.h"
#include "intro.h"
#ifdef HASOGGSUPPORT
#include "oggplayer.h"
#endif // HASOGGSUPPORT
#include "splash.h"
#include "bsgui.h"
#include "bl_lua.h"
#include "drawprimitives.h"
#include "bsscreen.h"

#ifndef DISABLE_NETWORK
#include <SDL_net.h>
#include "blpstream.h"
#include "mcufstream.h"
#endif // DISABLE_NETWORK

// Screen surface
SDL_Surface *gScreen = 0;

// Last iteration's tick value
#include <SDL_types.h>
Uint32 gLastTick;

#ifdef ENABLE_FULLSCREEN
#ifdef START_WITH_FULLSCREEN
bool isFullscreen = true;
#else // NO START_WITH_FULLSCREEN
bool isFullscreen = false;
#endif // START_WITH_FULLSCREEN
#endif // ENABLE_FULLSCREEN
#ifdef DISABLE_SOUND
bool soundOK = false;
#else // DISABLE_SOUND
bool soundOK = true;
#endif // DISABLE_SOUND
bool performanceTestMode = false;

/* How many microseconds should the splash screen appear? */
#define SPLASHTIME 10000


/* global variable. declared as extern in cmdopts.cpp */
CMD_OPTS cmd_opt_data[] = {
/* {short option name, long option name, description, is_boolean, defaultvalue_boolean, defaultvalue_string} */
#ifdef ENABLE_FULLSCREEN
#ifdef START_WITH_FULLSCREEN
{"-f", "--fullscreen", "Use Fullscreen (default)", true, 0, NULL},
{"-w", "--windowed", "Use windowed mode", true, 0, NULL},
#else // START_WITH_FULLSCREEN not defined
{"-f", "--fullscreen", "Use Fullscreen", true, 0, NULL},
{"-w", "--windowed", "Use windowed mode (default)", true, 0, NULL},
#endif // START_WITH_FULLSCREEN
#endif // ENABLE_FULLSCREEN
#ifndef DISABLE_SOUND
{"-s", "--disable-sound", "Disable sound", true, 0, NULL},
#endif // DISABLE_SOUND
{"-v", "--version", "Print version information and exit", true, 0, NULL},
{"-h", "--help", "Command-line help", true, 0, NULL},
{"-u", "--update-basedata", "Force update of basedata", true, 0, NULL},
{"-q", "--quit-after-update", "Quit after basedata update (implies -u)", true, 0, NULL},
#ifndef DISABLE_NETWORK
{"-p", "--proxyurl", "URL of the HTTP Proxy (--proxyurl=http://proxy.example.com:port or -p=http://proxy.example.com:port)", false, 0, NULL},
{"-b", "--blpstream", "Adress/Port for blpstream (--blpstream=192.168.0.1:2323 or -b=192.168.0.1:2323)", false, 0, NULL},
{"-m", "--mcufstream", "Adress/Port for mcufstream (--mcufstream=192.168.0.1:2323 or -m=192.168.0.1:2323)", false, 0, NULL},
#endif // DISABLE_NETWORK
{"-c", "--color3d", "Enable Color-3D (Anaglyph rendering) with Red/Cyan glasses", true, 0, NULL},
{"-d", "--quick-draw", "Quick drawing - leave out CPU intense non-essential graphics", true, 0, NULL},
{"-X", "--XO1", "Set parameters suitable for OLPC/XO-1 (overrides most other setting)", true, 0, NULL},
{"-t", "--test-mode", "Testmode - run a series of automated games and exit", true, 0, NULL},
{"-z", "--zoom", "Enlarge display to given screensize with|without smoothing (--zoom=1280x1024x1)", false, 0, NULL},

/* currently not supported, therefore commented
{"-r", "--respath", "Set path to game data", false, 0, NULL},
*/
CMD_OPTS_END /* {"END", "END", "END", false, 0, "END"} */
};


char * zoomstring ;

#ifndef DISABLE_NETWORK
char * proxyurl ;
char * blpstreamstring ;
char blphost[255] ;
short unsigned blpport ;
char * mcufstreamstring ;
char mcufhost[255] ;
short unsigned mcufport ;

#endif // DISABLE_NETWORK

void minimizeWindow() {
	printf("Minimizing window\n");
	BS_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, SDL_SWSURFACE);
}

// Entry point
int main(int argc, char*argv[])
{
	scriptIsRunning = 0; // MUST BE THE FIRST THING TO DO; OR DIE() MIGHT NOT WORK
	displayGraphicalErrors = false;
	isPauseMode = false;
	bool enableZoom=false;
	Uint32 zoomWidth=0;
	Uint32 zoomHeight=0;
	Uint32 zoomSmooth=0;

	int numoptions ;
	Uint32 timer ;  /* used for calculating the time how long the splashscreen is displayed... */
	// Parse command-line options
	if(!initCmdOpt(argc, argv)) {
		helpCmdOpt(argv[0]);
		exit(1);
	}


	bool forceBasedataUpdate = false;
	bool quitAfterUpdate = false;
	numoptions = getCmdOptBool((char*)"-u");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) { forceBasedataUpdate = true; }
	numoptions = getCmdOptBool((char*)"-q");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) {
		forceBasedataUpdate = true;
		quitAfterUpdate = true;
	}


	// Initialize the mini-lzo we use for BMF handling
    printf("\nProgram is using LZO real-time data compression library (v%s, %s).\n",
            lzo_version_string(), lzo_version_date());
    printf("Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n");
    // We use example code from libtheora (BSD License) */
    printf("\nIncludes code from libtheora Copyright (C) Xiph.org Foundation.\n\n");

#ifdef OLPCBUILD
    printf("Build for OLPC - will automatically assume -X option\n");
#endif //OLPCBUILD

    if (lzo_init() != LZO_E_OK)
    {
		DIE(ERROR_LZO, "lzo_init()");
    }


	numoptions = getCmdOptBool((char*)"-h");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) { helpCmdOpt(argv[0]) ; exit(0); }

#ifdef ENABLE_FULLSCREEN
	numoptions = getCmdOptBool((char*)"-f");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) { isFullscreen = true; }

	numoptions = getCmdOptBool((char*)"-w");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) { isFullscreen = false; }
#endif // ENABLE_FULLSCREEN

#ifndef DISABLE_SOUND
	numoptions = getCmdOptBool((char*)"-s");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) { soundOK = false; }
#endif // DISABLE_SOUND

	numoptions = getCmdOptBool((char*)"-d");
	if (numoptions < 0) { helpCmdOpt(argv[0]) ; exit(1); } /* Error */
	if (numoptions > 0) { enableQuickDraw = true; }
	
#ifndef DISABLE_NETWORK
	proxyurl = getCmdOptString((char*)"-p");
	if (proxyurl != NULL ) {
		if (strlen(proxyurl) >=255) {  /* NOTE: Probably use a errormacro or something 	similiar? */
			fprintf(stderr,"Error. The Proxyurl must be <= 255 Characters. \n"); /* Defined in RFC 2181: A FQDN is limited to 255 chars */
			exit(1);
		}
	} ;

	blpstreamstring = getCmdOptString((char*)"-b");
	if (blpstreamstring != NULL ) {
		blpport = 0;
		if (strlen(blpstreamstring) >=255) {  /* NOTE: Probably use a errormacro or something 	similiar? */
			fprintf(stderr,"Error. The blpstream must be <= 255 Characters. \n");
			exit(1);
		} ;
		sscanf(blpstreamstring, "%[^:]:%hu", blphost, &blpport);
//printf("blphost: %c blpport: %\n", blphost, blpport);
		if (blpport == 0) {
			blpport = 2323;
		}
	} else { strcpy(blphost,"") ; blpport=0 ; } ; /* If option is not defined, no stream */

	mcufstreamstring = getCmdOptString((char*)"-m");
	if (mcufstreamstring != NULL ) {
		mcufport = 0;
		if (strlen(mcufstreamstring) >=255) {  /* NOTE: Probably use a errormacro or something 	similiar? */
			fprintf(stderr,"Error. The mcufstream must be <= 255 Characters. \n");
			exit(1);
		} ;
		sscanf(mcufstreamstring, "%[^:]:%hu", mcufhost, &mcufport);
		if (mcufport == 0) {
			mcufport = 2323;
		}
	} else { strcpy(mcufhost,"") ; mcufport=0 ; } ; /* If option is not defined, no stream */

#endif // DISABLE_NETWORK

	zoomstring = getCmdOptString((char*)"-z");
	if (zoomstring != NULL ) {
		enableZoom = true;
		if (strlen(zoomstring) >=255) {  /* NOTE: Probably use a errormacro or something 	similiar? */
			fprintf(stderr,"Error. The zoomstring must be <= 255 Characters. \n");
			exit(1);
		} ;
		//sscanf(zoomstring, "%u*[x]%u*[x]%u", &zoomWidth, &zoomHeight, &zoomSmooth);
		sscanf(zoomstring, "%ux%ux%u", &zoomWidth, &zoomHeight, &zoomSmooth);
printf("zoom: %d x %d  smooth:%d\n", zoomWidth, zoomHeight, zoomSmooth);
		if (zoomWidth < SCR_WIDTH || zoomHeight < SCR_HEIGHT) {
			fprintf(stderr,"Error. Zoomsize must be at least %d x %d\n", SCR_WIDTH,SCR_HEIGHT);
		}
	}

	numoptions = getCmdOptBool((char*)"-X");
	if (numoptions < 0) { exit(1); } /* Error */
#ifdef OLPCBUILD
	// OLPC-> simulate setting of option
	numoptions = 1;
#endif // OLPCBUILD
	if (numoptions > 0) {
		enableZoom = true;
		zoomWidth = 1200;
		zoomHeight = 900;
		zoomSmooth = 0;
		enableQuickDraw = true;
#ifdef ENABLE_FULLSCREEN
		isFullscreen = false; 	/* actually, it is still displayed as fullscreen,
								 * except that it is "windowed" in terms of the
								 * SUGAR desktop manager, wich means "fullscreen
								 * with sugar borders"
								 */
#endif // ENABLE_FULLSCREEN
	}

	numoptions = getCmdOptBool((char*)"-t");
	if (numoptions < 0) { exit(1); } /* Error */
	if (numoptions > 0) {
		performanceTestMode = true;
	}

	numoptions = getCmdOptBool((char*)"-c");
	if (numoptions < 0) { exit(1); } /* Error */
	if (numoptions > 0) {
		enableColor3D = true;
	}

	numoptions = getCmdOptBool((char*)"-v");
	if (numoptions < 0) { exit(1); } /* Error */
	if (numoptions > 0) {
		printf("BlinkenSisters - Hunt for the Lost Pixels\nLostPixels Version: " VERSION "\n");
		printf("\nCompile-Flags:\n");
#ifdef ENABLE_FULLSCREEN
		printf("  Fullscreen: ENABLED\n");
#else // NO ENABLE_FULLSCREEN
		printf("  Fullscreen: DISABLED\n");
#endif // ENABLE_FULLSCREEN
#ifndef DISABLE_SOUND
		printf("  Sound-Support: ENABLED.\n");
#ifdef LIBRARIES_DEBUG
		printf("  Linked SDL_mixer version: %d.%d.%d\n", Mix_Linked_Version()->major, Mix_Linked_Version()->minor, Mix_Linked_Version()->patch);
#endif // LIBRARIES_DEBUG
#else // NO DISABLE_SOUND
		printf("  Sound-Support: DISABLED\n");
#endif // DISABLE_SOUND
#ifndef DISABLE_NETWORK
		printf("  Networking-Support: ENABLED.\n");
#ifdef LIBRARIES_DEBUG
		printf("  Linked SDL_net version: %d.%d.%d\n", SDLNet_Linked_Version()->major, SDLNet_Linked_Version()->minor, SDLNet_Linked_Version()->patch);
#endif // LIBRARIES_DEBUG
#else // NO DISABLE_NETWORK
		printf("  Networking-Support: DISABLED\n");
#endif // DISABLE_NETWORK
		printf("  Included Lua version: %s\n", LUA_VERSION);
#ifdef LIBRARIES_DEBUG
#ifdef OLPCBUILD
		printf("  OLPC-Build - always assume -X\n");
#endif //OLPCBUILD
		printf("  Linked SDL version: %d.%d.%d\n", SDL_Linked_Version()->major, SDL_Linked_Version()->minor, SDL_Linked_Version()->patch);
		printf("  Linked SDL_image version: %d.%d.%d\n", IMG_Linked_Version()->major, IMG_Linked_Version()->minor, IMG_Linked_Version()->patch);
		printf("  Linked SDL_ttf version: %d.%d.%d\n", TTF_Linked_Version()->major, TTF_Linked_Version()->minor, TTF_Linked_Version()->patch);
#endif // LIBRARIES_DEBUG
		printf("  Resource-Path: %s\n", RESPATH );
		exit(0);
	}
	// END processing command-line arguments

	// Initialize SDL's subsystems
	Uint32 SDLinitflags = SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ;
	if (soundOK!=false) { /* sound is compiled and not disabled by command line switch */
		SDLinitflags = SDLinitflags | SDL_INIT_AUDIO ;
	}
#ifndef CAVAC_RELEASEMODE
		SDLinitflags = SDLinitflags |SDL_INIT_NOPARACHUTE;
#endif // CAVAC_RELEASEMODE

	if ( SDL_Init(SDLinitflags) < 0 ) {
		fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
		exit(1);
	}

#ifndef DISABLE_NETWORK
	if ( SDLNet_Init() < 0 ) {
		fprintf(stderr, "Unable to init SDLNet: %s\n", SDLNet_GetError());
		exit(1);
	}
#endif // DISABLE_NETWORK

	// Init Config
	timer = BS_GetTicks();


	// Show splash screen
	showSplash();
	initFontHandler();

	if(!quitAfterUpdate) {
		if(enableColor3D) {
			renderFontHandlerText(200, 50, "3D Mode", MENUCOLOR_INACTIVE, false, false, FONT_menufont_20);
		}
		renderFontHandlerText(60, 240, "Blinkensisters is free software\nlicensed under the Gnu Public License", MENUCOLOR_INACTIVE, false, false, FONT_textfont_20);
		BS_Flip(gScreen);
		SDL_Delay (2000) ; /* wait 2 sec. */
		drawrect(50, 235, 405, 55, 0xffffff);  /* clear text field */
		renderFontHandlerText(60, 240, "Open Source Libs used: SDL, SDL_ttf, LUA, \nSDL_mixer, SDL_net, libtheora", MENUCOLOR_INACTIVE, false, false, FONT_textfont_20);
		BS_Flip(gScreen);
		SDL_Delay (2000) ; /* wait 2 sec. */
		drawrect(50, 235, 405, 55, 0xffffff);  /* clear text field */
		renderFontHandlerText(60, 240, "Visit http://www.blinkensisters.org\nand join the development-team", MENUCOLOR_INACTIVE, false, false, FONT_textfont_20);
		BS_Flip(gScreen);
		SDL_Delay (2000) ; /* wait 2 sec. */
	} else {
		renderFontHandlerText(60, 240, "Installing updates for basedata and\nexiting. Please wait...", MENUCOLOR_INACTIVE, false, false, FONT_textfont_20);
		BS_Flip(gScreen);
	}

	// Basic configuration init
	if(forceBasedataUpdate) {
		printf("Running (forced) basedata update\n");
	}
	configInit(forceBasedataUpdate);
	if(quitAfterUpdate) {
		printf("Quitting after forced update\n");
		exit(0);
	}

	// Initialize sound subsystem
	initSound();
	initJoystick();
	initShowVideo();
	timer = BS_GetTicks() - timer ; /* how much time was used for copying config files? */
	if (timer > SPLASHTIME ) {timer = SPLASHTIME ; }
	SDL_Delay (SPLASHTIME - timer) ; /* wait at least SPLASHTIME microsec. */
	// Attempt to create a SCR_WIDTHxSCR_HEIGHT window with 32bit pixels.

	if(enableZoom) {
		BS_SetRealResolution(zoomWidth,zoomHeight,zoomSmooth);
	}
//isFullscreen = false;
#ifdef ENABLE_FULLSCREEN
	if(isFullscreen) {
		gScreen = BS_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, SDL_SWSURFACE | SDL_FULLSCREEN);
	} else {
		gScreen = BS_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, SDL_SWSURFACE);
	}
#else // NO ENABLE_FULLSCREEN
	gScreen = BS_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, SDL_SWSURFACE);
#endif // ENABLE_FULLSCREEN

	// If we fail, return error.
	if ( gScreen == NULL )
	{
		fprintf(stderr, "Unable to set %d x %d videomode: %s\n", SCR_WIDTH, SCR_HEIGHT, SDL_GetError());
		exit(1);
	}
    initGui();

	// Hide the mousepointer
	SDL_ShowCursor(SDL_DISABLE);
	/* Set a window title. */
	SDL_WM_SetCaption("Blinkensisters - Hunt for the lost pixels", NULL); // Window title: blinkensisters, Icon title: none
	SDL_WM_SetIcon(IMG_Load(configGetPath("lostpixels.png")), NULL);

	atexit(SDL_Quit);
#ifndef DISABLE_NETWORK
	atexit(SDLNet_Quit);
#endif // DISABLE_NETWORK
	atexit(minimizeWindow);
	atexit(deInitSound);

#ifndef DISABLE_NETWORK
	// Initialize loader screen and show it
	if (blpport != 0) {
		initBLPSocket();
	} ;
	if (mcufport != 0) {
		initMCUFSocket();
	} ;
#endif // DISABLE_NETWORK
	initShowLoading("loading.jpg");
	showLoading();
	initExtractMetaBMF();
	initFGInlay();

	// Inits that need an initialized screen
	initShowText();
	initFPSCounter();
	initMenu();

	// Display the Intro sequence
#ifdef CAVAC_RELEASEMODE
#ifdef HASOGGSUPPORT
	playogg(configGetPath("startanim_video.ogg"),gScreen); /* play the standard ogg video instead of the BMF showVideo("startanim.bmf", true); */
#else // HASOGGSUPPORT
	showVideo("startanim.bmf", true, true);
#endif // NO HASOGGSUPPORT
#endif // CAVAC_RELEASEMODE
	configStartupComplete();

	displayGraphicalErrors = true; // Show errors in display as well

	if(performanceTestMode) {
		menuPerformanceTestMode();
		exit(0);
	}
#ifndef BLINKOMATEDITION
	while(menuDisplay()) {
		//
	}
#else // Running on Blinkomat
	while(true) {
		menuAttrackMode();
		menuAddonDisplay();
	}
#endif //BLINKOMATEDITION



// Clean up
	deInitMenu();
	deInitShowLoading();
	deInitFGInlay();
	deInitGui();
#ifndef DISABLE_NETWORK
	if (blpport != 0) {
		deInitBLPSocket();
	} ;
	if (mcufport != 0) {
		deInitMCUFSocket();
	} ;
#endif // DISABLE_NETWORK
	SDL_ShowCursor(SDL_ENABLE);
	return 0;
}


#if WIN32

#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <fstream>

void RedirectIOToConsole()
{
	int hConHandle;
	long lStdHandle;
	CONSOLE_SCREEN_BUFFER_INFO coninfo;
	FILE *fp;

	// allocate a console for this app

	AllocConsole();
	// set the screen buffer to be big enough to let us scroll text

	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&coninfo);
	coninfo.dwSize.Y = 40;
	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),	coninfo.dwSize);

	// redirect unbuffered STDOUT to the console

	lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

	fp = _fdopen( hConHandle, "w" );

	*stdout = *fp;
	setvbuf( stdout, NULL, _IONBF, 0 );

	// redirect unbuffered STDIN to the console
	lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

	fp = _fdopen( hConHandle, "r" );
	*stdin = *fp;
	setvbuf( stdin, NULL, _IONBF, 0 );

	// redirect unbuffered STDERR to the console

	lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "w" );
	*stderr = *fp;
	setvbuf( stderr, NULL, _IONBF, 0 );
}

void RedirectIOToFiles()
{
	FILE *fp;

	// allocate a console for this app


	// redirect unbuffered STDOUT to the console

	fp = fopen( "stdoutstderr_lp.txt", "w" );

	*stdout = *fp;
	setvbuf( stdout, NULL, _IONBF, 0 );

	// redirect unbuffered STDERR to the console
	*stderr = *fp;
	setvbuf( stderr, NULL, _IONBF, 0 );
}


int WINAPI WinMain(  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,						// handle to previous instance
  LPSTR lpCmdLine,								// command line
  int nCmdShow									// show state
  )
{
	int argc = 0;
	int i=0;

    // Avoid some "unused parameter" warnings from gcc
    if(hInstance || hPrevInstance || hPrevInstance || nCmdShow) {
        argc = 0;
    }

	if (lpCmdLine[0]) // at least one paramteter
		argc = 1;
	while (lpCmdLine[i])
	{
		if (lpCmdLine[i] == ' ')
			argc++;
		i++;
	}
	argc++;  // add app name

	char** argv = (char**)malloc(argc*sizeof(char*));
	char appName[500];
	GetModuleFileNameA(0, appName, sizeof(appName));
	char* p;

	while((p= strrchr(appName,'\\')))
		*p= '/';
	p= argv[0] = (char*)malloc(strlen(appName)+10);
	strcpy(p,appName);

	i = argc-1;
	char* strptr = strrchr(lpCmdLine, ' ');
	while (strptr)
	{
		p=argv[i] = (char*)malloc(strlen(strptr+1)+10);
		strcpy( p,strptr+1);
		*strptr = '\0';
		i--;
		strptr = strrchr(lpCmdLine, ' ');
	}
	if (lpCmdLine[0])
	{
		p=argv[1] = (char*)malloc(strlen(lpCmdLine)+1);
		strcpy(p,lpCmdLine);
	}
	//RedirectIOToConsole();
	RedirectIOToFiles();

	return main(argc, argv);
}
#endif // WIN32


