#include "global.h"

char* fontNames[FNT_NUM_FONTS] = {
	"GlaserSted",
	"GlaserSted",
	"Machine BT",
	"Arial",
};
char* fontFileNames[FNT_NUM_FONTS] = {
	"../Data/GLASTRSN.TTF",
	"../Data/GLASTRSN.TTF",
	"../Data/MACHINEN.TTF",
	NULL,
};


void GameInformation::Draw()
{
	switch (state)
	{
		case GS_INTRO:		intro.Draw();		break;
		case GS_MENU:		menu.Draw();		break;
		case GS_PLAYING:
			world.Draw();
			hud.Draw();
			if (showingInGameMenu)
				menu.Draw();
			break;
		default:	break;
	}	
}

bool GameInformation::RegisterModels()
{
	for (int fnum=0; fnum < FNT_NUM_FONTS; fnum++)
		font[fnum] = new GLFont(fontNames[fnum],fontFileNames[fnum],0,fnum==FNT_TITLE_OUTLINE);

	game.font[FNT_MENU2]->setScale(3,4,1);

	menu.RegisterModels();
	world.RegisterModels();
	hud.RegisterModels();
	intro.RegisterModels();

	return false;
}

void GameInformation::ReleaseModels()
{
	for (int fnum=0; fnum < FNT_NUM_FONTS; fnum++)
		delete font[fnum];

	intro.ReleaseModels();
	menu.ReleaseModels();
	world.ReleaseModels();
	hud.ReleaseModels();
}


#define NUM_MSG_KILL_KILLERFIRST  4
#define NUM_MSG_KILL_KILLERSECOND 3
#define NUM_MSG_KILL_SUICIDE      8
#define NUM_MSG_KILL_FIRE			 4
const char* killMessagesKillerFirst[NUM_MSG_KILL_KILLERFIRST] = {
	"%s blew up %s.",
	"%s just lambasted %s.",
	"%s nailed %s",
	"The force is stronger with %s than %s",
};
const char* killMessagesKilleeFirst[NUM_MSG_KILL_KILLERSECOND] = {
	"%s was on the leader.  Until %s blew them away.",
	"%s is calling %s \"daddy.\"",
	"%s got tooled by %s.",
};
const char* killMessagesSuicide[NUM_MSG_KILL_SUICIDE] = {
	"%s just got tooled by... well, themselves!  How lame!",
	"Hint for %s: Kill the OTHER players.",
	"%s seems to have a split personality -- one of them just won.",
	"Oh, your mommy wouldn't like that %s",
	"%s must have cracked under the pressure.",
	"%s: \"ooops\"",
	"%s: \"Hey, what's this button do...?\"",
	"Whatever you do, %s, never EVER press the red butt---",
};
const char* killMessagesFire[NUM_MSG_KILL_FIRE] = {
	"%s couldn't handle the heat.",
	"%s just got \"toasted.\"",
	"If see a charred, dead, smoking heap of rubble... it might be %s.",
	"%s is having trouble with the thermostat",
};

void GameInformation::NextGameSong()
{
	if (wantMusic)
	{
		if (++currentGameSong >= NUM_SONGS)
			currentGameSong = MGameSong1;
		PlaySong(currentGameSong);

		char str[MAX_PATH];
		sprintf(str,"Now playing %s",songname[currentGameSong]);
		hud.AddMessage(MESG_SYSTEM_NOTICE,0,str);
	}
}
void GameInformation::PrevGameSong()
{
	if (wantMusic)
	{
		if (--currentGameSong <= MGameSong1)
			currentGameSong = NUM_SONGS-1;
		PlaySong(currentGameSong);

		char str[MAX_PATH];
		sprintf(str,"Now playing %s",songname[currentGameSong]);
		hud.AddMessage(MESG_SYSTEM_NOTICE,0,str);
	}
}
void GameInformation::StopMusic()
{
	StopSong();
	wantMusic=false;

	char str[MAX_PATH];
	sprintf(str,"Stopping music.");
	hud.AddMessage(MESG_SYSTEM_NOTICE,0,str);
}
void GameInformation::PlayMusic()
{
	wantMusic=true;
	PlaySong(currentGameSong);

	char str[MAX_PATH];
	sprintf(str,"Starting music:  Playing %s",songname[currentGameSong]);
	hud.AddMessage(MESG_SYSTEM_NOTICE,0,str);
}

void GameInformation::update(timet dtime)
{
	switch (state)
	{
		case GS_INIT:		SetState(GS_INTRO);		break;
		case GS_INTRO:		intro.update(dtime);		break;
		case GS_MENU:		menu.update(dtime);			break;
		case GS_PLAYING:
			if (showingInGameMenu)
				menu.update(dtime);
			world.update(dtime);
			hud.update(dtime);
			// If the music has stopped, go to next song
			if (!snd.musicPlaying)
				NextGameSong();
			break;
		default:	break;
	}
	while (netclient.GetNumQueuedChatMessages() > 0)
	{
		HTMSG_CHAT_FULL msg = netclient.GetNextChatMessage();
		hud.AddMessage(MESG_CHAT, netclient.GetPlayerIdx(msg.pidFrom), msg.msg);
	}
	while (netclient.GetNumQueuedEvents() > 0)
	{
		static int killMessage = 0;
		HTMSG_GAME_EVENT msg = netclient.GetNextEvent();
		printf("got event message: ID:%d P1:%d P2:%d\n",(int)(msg.eventID), (int)msg.player1, (int)msg.player2 );
		switch (msg.eventID)
		{
			case HT_MSGID_NOTIFY_KILL:
			{
				int randomnum = rand();
				char deathmsg[MAX_PATH];
				char player1name[MAX_PATH];
				char player2name[MAX_PATH];
				if (msg.player1 != msg.player2)
				{
					game.netclient.getPlayerName(game.netclient.GetPlayerIdx(msg.player1),player1name);
					game.netclient.getPlayerName(game.netclient.GetPlayerIdx(msg.player2),player2name);

					if ((randomnum%2)==0)
					{
						int deathmsgid = randomnum % NUM_MSG_KILL_KILLERFIRST;
						sprintf(deathmsg,killMessagesKillerFirst[deathmsgid],player1name,player2name);
					}
					else
					{
						int deathmsgid = randomnum % NUM_MSG_KILL_KILLERSECOND;
						sprintf(deathmsg,killMessagesKilleeFirst[deathmsgid],player2name,player1name);
					}
					hud.AddMessage(MESG_GAME_EVENT,-1,deathmsg);
				}
				// Otherwise it must have been a suicide -- but that's not under KILL
				break;
			}
			case HT_MSGID_NOTIFY_SUICIDE:
			{
				int randomnum = rand();
				char deathmsg[MAX_PATH];
				char player1name[MAX_PATH];
				int playerIdx = netclient.GetPlayerIdx(msg.player1);
				netclient.getPlayerName(playerIdx,player1name);
				int deathmsgid = randomnum % NUM_MSG_KILL_SUICIDE;
				sprintf(deathmsg,killMessagesSuicide[deathmsgid],player1name);
				hud.AddMessage(MESG_GAME_EVENT,-1,deathmsg);

				world.tank[playerIdx].deaths++;

				break;
			}
			case HT_MSGID_NOTIFY_BURNED:
			{
				int randomnum = rand();
				char deathmsg[MAX_PATH];
				char player1name[MAX_PATH];
				int playerIdx = netclient.GetPlayerIdx(msg.player1);
				netclient.getPlayerName(playerIdx,player1name);
				int deathmsgid = randomnum % NUM_MSG_KILL_FIRE;
				sprintf(deathmsg,killMessagesFire[deathmsgid],player1name);
				hud.AddMessage(MESG_GAME_EVENT,-1,deathmsg);

				world.tank[playerIdx].deaths++;

				break;
			}
			default:
				printf("Unknown message id!\n");
				break;
		}
	}
}

bool GameInformation::HandleInput(InputEvent_type e)
{
	bool handled = false;

	// If we don't handle the key press, forward it
	// to the next level
	switch (state)
	{
		case GS_INTRO:
			if (e.type == IE_KB)
				intro.ExitIntro();
			break;
		case GS_MENU:		handled = menu.HandleInput(e);	break;
		case GS_PLAYING:
			if (showingInGameMenu)
				handled = menu.HandleInput(e);
			else
			{
				handled = hud.HandleInput(e);
				if (!handled)
					handled = world.HandleInput(e);
			}
			break;
		default:	break;
	}
	if (!handled)
	{
		if (e.type == IE_KB)
		{
			switch (e.key)
			{
				case 0X1B:
					if (state == GS_PLAYING)
					{
						showingInGameMenu = !showingInGameMenu;
						menu.inGameMenu = true;
						menu.state = MS_SETTINGS;
					}
					handled = true;
					break;

				case '[':
					PrevGameSong();
					break;
				case ']':
					NextGameSong();
					break;
				case '\\':
					if (wantMusic)	StopMusic();
					else PlayMusic();
					break;
			}
		}
	}
	return handled;
}

void GameInformation::SetState(GameState newState)
{
	// Ugly, but it's due in 6 hrs
	if (state != GS_INTRO && newState == GS_MENU)
	{
		StopSong();
		PlaySong(MMenuSong);
	}
	state = newState;
	switch (state)
	{
		case GS_INTRO:
			menu.ResetMenuWorld();
			intro.Reset();
			intro.StartIntro();
			break;
		case GS_MENU: 
			input.mousex = 250;
			input.mousey = 400;
			menu.inGameMenu = false;
			menu.ChangeMenu(MS_MAIN);
			break;//		menu.Reset();		break;
		case GS_PLAYING:
			showingInGameMenu = false;
			StopSong(MMenuSong);
			PlaySong(MGameSong1);
			//world.Reset();
			break;
		default:	break;
	}
}

bool GameInformation::LoadData()
{
	printf("GAME: Loading data...\n");
	world.LoadData();
	menu.LoadData();
	hud.LoadData();
	intro.LoadData();
	intro.Reset();
	printf("GAME: Data loaded\n");
	return true;
}

void GameInformation::Initialize()
{
	printf("GAME: Initialiazing...\n");
	GetDisplayModeInfo();
	hud.SetWorld(&world);
	config.windowedWidth = glutarConfig.win_width;
	config.windowedHeight = glutarConfig.win_height;
	/*
	LoadData();
	world.Reset();
	initGL();
	*/
	wantMusic=true;
	currentGameSong=MGameSong1;

	world.skyBoxLowerZ = 1;

	printf("GAME: Initialization complete, starting...\n");
}

void AddFreq( DEVMODE& dm, BitDepthPerResolution& bd)
{
	int f;
	for (f=0; f < bd.freq.size(); f++)
		if (bd.freq[f].freq == dm.dmDisplayFrequency)
			return;
	FreqPerBitDepth newFreq;
	newFreq.freq = dm.dmDisplayFrequency;
	bd.freq.push_back(newFreq);
	return;
}

void AddBitdepth( DEVMODE& dm, ScreenResolution& res)
{
	int bd;
	for (bd=0; bd < res.bitdepth.size(); bd++)
		if (res.bitdepth[bd].bitdepth == dm.dmBitsPerPel)
		{
			AddFreq( dm, res.bitdepth[bd] );
			return;
		}

	BitDepthPerResolution newBD;
	newBD.bitdepth = dm.dmBitsPerPel;
	AddFreq( dm, newBD );
	res.bitdepth.push_back(newBD);
	return;
}

void AddVideoMode( DEVMODE& dm, VideoModeList& list)
{
	int res;
	for (res=0; res < list.size(); res++)
	{
		if (list[res].width == dm.dmPelsWidth && list[res].height == dm.dmPelsHeight)
		{
			AddBitdepth( dm, list[res] );
			return;
		}
	}
	ScreenResolution newRes;
	newRes.width = dm.dmPelsWidth;
	newRes.height = dm.dmPelsHeight;
	AddBitdepth( dm, newRes );
	list.push_back( newRes );
	return;
}

extern LARGE_INTEGER lasttime;
#include "input.h"

bool GameInformation::SetVideoMode(int vmIdx, int bdIdx, int fIdx, bool fullscreen, int width, int height)
{
	if (fullscreen)
	{
		// If we were previously windowed, save the window size settings
		if (!fullscreen)
		{
			config.windowedHeight = glutarConfig.win_height;
			config.windowedWidth  = glutarConfig.win_width;
		}
		// First, validate parameters:
		if (vmIdx < 0 || bdIdx < 0 || fIdx < 0)
			return false;
		if (vmIdx >= config.availableModes.size())
			return false;
		ScreenResolution& s = config.availableModes[vmIdx];
		if (bdIdx >= s.bitdepth.size())
			return false;
		BitDepthPerResolution& r = s.bitdepth[bdIdx];
		if (fIdx  >= r.freq.size())
			return false;
		FreqPerBitDepth& f = r.freq[fIdx];
		
		// Valid video mode, go ahead!
		config.fullScreen = fullscreen;
		config.chosenVideoMode = vmIdx;
		config.chosenBitdepth = bdIdx;
		config.chosenFreq = fIdx;
		//printf("Switching to fullscreen, %dx%d %d bpp %d Hz\n",
		//	s.width,s.height,r.bitdepth,f.freq);

		bool wkb = wantKbAcquire,wms = wantMouseAcquire;
		keyboardAcquire(false);
		mouseAcquire(false);
		ExitDirectInput();
		ReleaseModels();
		glutarChangeWindowSettings("HoverTank",0,0,s.width,s.height,r.bitdepth,fullscreen,f.freq);
		reshape(glutarConfig.win_width,glutarConfig.win_height);
		initGL();
		InitDirectInput();
		keyboardAcquire(wkb);
		mouseAcquire(wms);
		QueryPerformanceCounter(&lasttime);
	}
	else
	{
		if (width == -1)
			width = game.config.windowedWidth;
		if (height == -1)
			height = game.config.windowedHeight;

		// only need to change window if we were already in fullscreen
		// and are switching out, otherwise can just resize
		if (config.fullScreen)
		{
			bool wkb = wantKbAcquire,wms = wantMouseAcquire;
			// Destroy old window, make new window
			//printf("Switching to windowed, %dx%d\n",width,height);
			keyboardAcquire(false);
			mouseAcquire(false);
			ExitDirectInput();
			ReleaseModels();
			glutarChangeWindowSettings("HoverTank",200,200,config.windowedWidth,config.windowedHeight,32,false,60);
			reshape(glutarConfig.win_width,glutarConfig.win_height);
			initGL();
			InitDirectInput();
			keyboardAcquire(wkb);
			mouseAcquire(wms);
			QueryPerformanceCounter(&lasttime);
		}
		else
			printf("Keeping windowed, %dx%d\n",width,height);
		config.fullScreen = fullscreen;
		config.windowedHeight = height;
		config.windowedWidth = width;
	}
	return true;
}

void GameInformation::GetDisplayModeInfo()
{
	DEVMODE dm;
	DWORD curMode;
	int res,bd;

	printf("Querying available video modes... ");
	for (curMode = 0; EnumDisplaySettings(NULL,curMode,&dm); curMode++)
	{
		if (dm.dmBitsPerPel >= 16 && dm.dmPelsWidth >= 640)
				AddVideoMode( dm, config.availableModes );
	}
	printf("Done\n");

	printf("Discovered video modes: (%d)",config.availableModes.size());
	for (res=0; res < config.availableModes.size(); res++)
	{
		ScreenResolution& r = config.availableModes[res];
		printf("\n%4dx%4d : ",r.width,r.height);
		for (bd=0; bd < r.bitdepth.size(); bd++)
			printf("%d(%d) ",r.bitdepth[bd].bitdepth,r.bitdepth[bd].freq.size());
	}
	printf("\n---\n");
}

void GameInformation::error(char *fmt, ...)
{
	char		text[2048];											// Holds Our String
	va_list		ap;												// Pointer To List Of Arguments

	if (fmt == NULL)												// If There's No Text
		return;														// Do Nothing

	va_start(ap, fmt);											// Parses The String For Variables
	    vsprintf(text, fmt, ap);								// And formats the string accordingly
	va_end(ap);														// Results Are Stored In Text

	fprintf(stderr,">> ERROR: %s\n",text);
}
