#include "GameManager.h"
#include "SoundManager.h"

#define INTERVAL (1.0f / 50.0f)

static pascal void Timer(EventLoopTimerRef r, void * d)
{
	#pragma unused(r)
	VGame * g;
	double currentTime;
	double interval;
	Vector front, push;
	
	g = (VGame *)d;
	
	currentTime = GetCurrentEventTime();
	interval = (currentTime - g->lastTime) + g->slop;
	while (interval > INTERVAL) {
		
		if (g->Status == MENU) {
			RunInterface(&g->Interface, INTERVAL);
		} else {
			if (!g->Paused) {
		  	RunWorld(&g->world, INTERVAL);
				if (g->world.Finished && !(g->Status & (FINISHEDLEVEL | DYING))) {
					SwitchMode(g, (PLAYING | FINISHEDLEVEL));
				}
			}
			
			ForwardsVector(&g->world.Camera, 1.0, &front);
			push = front;
			if (g->Direction & STOP) {
				StopShip(&g->world.Physics, (SHIPACCELERATION / interval));
			} else if (g->Direction & FORWARDS) {
				push.x *= (SHIPACCELERATION / interval);
				push.y *= (SHIPACCELERATION / interval);
				push.z *= (SHIPACCELERATION / interval);
				PushShip(&g->world.Physics, &push);
			} else if (g->Direction & BACKWARDS) {
				push.x *= -(SHIPACCELERATION / interval);
				push.y *= -(SHIPACCELERATION / interval);
				push.z *= -(SHIPACCELERATION / interval);
				PushShip(&g->world.Physics, &push);
			}
			TurnShip(&g->world.Physics, g->Rotation, interval);
			
			if (g->world.Player.Shields <= 0 && !(g->Status & (DYING | FINISHEDLEVEL))) {
				SwitchMode(g, (PLAYING | DYING));
			}
			
			if (g->Status & (STARTINGLEVEL | FINISHEDLEVEL | DYING)) {
				g->ModeFade += (1.0 / INTERVAL);
				
				if ((g->Status & STARTINGLEVEL) && g->ModeFade > STARTLEVELFADETIME) {
					SwitchMode(g, PLAYING);
				} else if ((g->Status & FINISHEDLEVEL) && g->ModeFade > FINISHLEVELFADETIME) {
					StopLevel(&g->world);
					if ((g->world.CurrentLevel + 1) >= g->world.NumberOfLevels) {
						SwitchMode(g, MENU);
						g->Interface.GameWasWon = 1;
					} else {
						/*
						if(g->world.CurrentLevel >= 0)
							StartLevel(&g->world, -(g->world.CurrentLevel + 1));
						else
							StartLevel(&g->world, -g->world.CurrentLevel);
						*/
						StartLevel(&g->world, (g->world.CurrentLevel + 1));
						SwitchMode(g, (PLAYING | STARTINGLEVEL));
					}
				} else if ((g->Status & DYING) && g->ModeFade > DEATHFADETIME) {
					StopLevel(&g->world);
					InitPlayer(&g->world.Player);
					StartLevel(&g->world, g->world.CurrentLevel);
					SwitchMode(g, (PLAYING | STARTINGLEVEL));
				}
			}
		}
		
		interval -= INTERVAL;
	}
	g->slop = interval;
	g->lastTime = currentTime;
	
	if(g->Status == MENU)
	{
		glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
		
		DrawInterface(&g->Interface);
		
		if(g->w.IsFullScreen)
			aglSwapBuffers(g->w.FullScreen);
		else
			aglSwapBuffers(g->w.Windowed);
		return;
	}
	
	g->Frames++;
	
	if(TickCount() - g->FrameTimer > 60)
	{
		g->FrameTimer = TickCount();
		g->FramesPerSecond = g->Frames;
		g->Frames = 0;
	}
	
	glClear(GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	
	DrawWorld(&g->world, interval);
	
	if (g->Status & (STARTINGLEVEL | FINISHEDLEVEL | DYING)) {
		if (g->Status & STARTINGLEVEL)
			glColor4f(0.0, 0.0, 0.0, 1.0 - (g->ModeFade / STARTLEVELFADETIME));
		else if (g->Status & FINISHEDLEVEL)
			glColor4f(0.0, 0.0, 0.0, (g->ModeFade / FINISHLEVELFADETIME));
		else if (g->Status & DYING)
			glColor4f(0.0, 0.0, 0.0, (g->ModeFade / DEATHFADETIME));
		
		glLoadIdentity();
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glDisable(GL_DEPTH_TEST);
		glBegin(GL_QUADS);
			glVertex3f(-10.0, 10.0, -2.0);
			glVertex3f(10.0, 10.0, -2.0);
			glVertex3f(10.0, -10.0, -2.0);
			glVertex3f(-10.0, -10.0, -2.0);
		glEnd();
		glEnable(GL_DEPTH_TEST);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	}
	
	if(g->w.IsFullScreen)
		aglSwapBuffers(g->w.FullScreen);
	else
		aglSwapBuffers(g->w.Windowed);
	
}

static void ResumeGame(VGame * g)
{
	g->lastTime = GetCurrentEventTime();
	g->FrameTimer = TickCount();
	if (g->Status != MENU && g->w.IsFullScreen) HideCursor();
	else ShowCursor();
}

static pascal OSStatus MenuSelected(EventHandlerCallRef r, 
	EventRef e, void * d)
{
	#pragma unused(r)
	VGame * g;
	HICommand TheCommand;
	g = (VGame *)d;
	
	GetEventParameter(e, kEventParamDirectObject, typeHICommand, NULL, 
		sizeof(HICommand), NULL, &TheCommand);
	
	switch(TheCommand.commandID)
	{
		case 'FScn':
			if (g->w.IsFullScreen) {
				CreateWindowedContext(&g->w);
				ShowCursor();
				if (g->Status & PLAYING && g->Paused) {
					glClear(GL_DEPTH_BUFFER_BIT);
					DrawWorld(&g->world, 1.0);
					aglSwapBuffers(g->w.Windowed);
				}
			} else {
				CreateFullScreenContext(&g->w);
				if (g->Status != MENU) HideCursor();
				if (g->Paused) {
					glClear(GL_DEPTH_BUFFER_BIT);
					DrawWorld(&g->world, 1.0);
					aglSwapBuffers(g->w.FullScreen);
				}
			}
			
			return noErr;
			break;
		case 'EndG':
			if (g->Status & PLAYING) {
			  SwitchMode(g, MENU);
			}
			break;
		case kHICommandAbout:
			OpenAboutWindow(&g->m);
			return noErr;
			break;
		case kHICommandPreferences:
			if (!g->w.IsFullScreen) OpenSettingsWindow(&g->Settings);
			return noErr;
			break;
	}
	return eventNotHandledErr;
}

static pascal OSStatus KeyDown(EventHandlerCallRef nextEvent, EventRef theEvent, void * userData) {
	#pragma unused (nextEvent)
	VGame * g;
	VSettings * s;
	unsigned char charCode;
	UInt32 modifiers;
	SInt16 key;
	
	g = (VGame *) userData;
	s = GetSettings();
	GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &charCode);
	GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
	key = KeyWithModifiers(charCode, modifiers);
	if (charCode == 27) { /* Escape */
		if (g->w.IsFullScreen) {
			CreateWindowedContext(&g->w);
			ShowCursor();
			if (g->Status & PLAYING && g->Paused) {
				glClear(GL_DEPTH_BUFFER_BIT);
				DrawWorld(&g->world, 1.0);
				aglSwapBuffers(g->w.Windowed);
			}
		}
	}
	if (!(g->Status & PLAYING) || (g->Status & DYING)) return eventNotHandledErr;
	if (SettingsIsEquivalentKey(s, key, s->Left)) {
		g->Rotation &= ~RIGHT;
		g->Rotation |= LEFT;
	}
	if (SettingsIsEquivalentKey(s, key, s->Right)) {
		g->Rotation &= ~LEFT;
		g->Rotation |= RIGHT;
	}
	if (SettingsIsEquivalentKey(s, key, s->Up)) {
		g->Rotation &= ~DOWN;
		g->Rotation |= UP;
	}
	if (SettingsIsEquivalentKey(s, key, s->Down)) {
		g->Rotation &= ~UP;
		g->Rotation |= DOWN;
	}
	if (SettingsIsEquivalentKey(s, key, s->Forward)) {
		g->Direction = FORWARDS;
	}
	if (SettingsIsEquivalentKey(s, key, s->Backward)) {
		g->Direction = BACKWARDS;
	}
	if (SettingsIsEquivalentKey(s, key, s->Brake)) {
		g->Direction |= STOP;
	}
	if (SettingsIsEquivalentKey(s, key, s->Bullet)) {
		g->world.Player.Firing = 1;
		g->world.Player.NextFireTime = 0.0;
	}
	if (SettingsIsEquivalentKey(s, key, s->Rocket)) {
	  if (g->world.Player.NumRockets > 0) PlaySound(GetSoundManager(), LAUNCHROCKETSOUND);
		AddWeapon(&g->world, WEAPONTYPEROCKET);
	}
	if (SettingsIsEquivalentKey(s, key, s->RocketTarget)) {
	  PlaySound(GetSoundManager(), ROCKETTARGETSOUND);
	  FindTarget(&g->world);
	}
	if (SettingsIsEquivalentKey(s, key, s->Pause)) {
	  if (g->Paused) {
      //SetEventLoopTimerNextFireTime(g->timerRef, (kEventDurationSecond / 120.0));
      ResumeGame(g);
	  } else {
      //SetEventLoopTimerNextFireTime(g->timerRef, kEventDurationForever);
      InitCursor();
	  }
	  g->Paused = !g->Paused;
	  g->world.Console.Paused = g->Paused;
	}
	
	return eventNotHandledErr;
}

static pascal OSStatus KeyUp(EventHandlerCallRef nextEvent, EventRef theEvent, void * userData) {
	#pragma unused(nextEvent)
	VGame * g;
	VSettings * s;
	unsigned char charCode;
	
	g = (VGame *) userData;
	s = GetSettings();
	GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &charCode);
	if (!(g->Status & PLAYING)) return eventNotHandledErr;
	if (charCode == (s->Left & 0xFF)) {
		g->Rotation &= ~LEFT;
	}
	if (SettingsIsEquivalentKey(s, charCode, (s->Right & 0xFF))) {
		g->Rotation &= ~RIGHT;
	}
	if (SettingsIsEquivalentKey(s, charCode, (s->Up & 0xFF))) {
		g->Rotation &= ~UP;
	}
	if (SettingsIsEquivalentKey(s, charCode, (s->Down & 0xFF))) {
		g->Rotation &= ~DOWN;
	}
	if (SettingsIsEquivalentKey(s, charCode, (s->Forward & 0xFF)) || SettingsIsEquivalentKey(s, charCode, (s->Backward & 0xFF))) {
		g->Direction &= ~(FORWARDS | BACKWARDS);
	}
	if (SettingsIsEquivalentKey(s, charCode, (s->Brake & 0xFF))) {
		g->Direction &= ~STOP;
	}
	if (SettingsIsEquivalentKey(s, charCode, (s->Bullet & 0xFF))) {
		g->world.Player.Firing = 0;
	}
	
	return eventNotHandledErr;
}

static pascal OSStatus AppActivated(EventHandlerCallRef nextEvent, EventRef theEvent, void * userData) {
	#pragma unused(nextEvent, theEvent)
	VGame * g;
	
	g = (VGame *) userData;
	SetEventLoopTimerNextFireTime(g->timerRef, (kEventDurationSecond / 120.0));
	ResumeGame(g);
	return eventNotHandledErr;
}

static pascal OSStatus AppDeactivated(EventHandlerCallRef nextEvent, EventRef theEvent, void * userData) {
	#pragma unused(nextEvent, theEvent)
	VGame * g;
	
	g = (VGame *) userData;
	SetEventLoopTimerNextFireTime(g->timerRef, kEventDurationForever);
	ShowCursor();
	return eventNotHandledErr;
}

void InitGame(VGame * g)
{
	EventTypeSpec eventType;
	
	InitCursor();
	InstallEventLoopTimer(GetMainEventLoop(), 0, (kEventDurationSecond / 120.0), 
		NewEventLoopTimerUPP(Timer), g, &g->timerRef);
	
	eventType.eventClass = kEventClassCommand;
	eventType.eventKind = kEventProcessCommand;
	InstallEventHandler(GetApplicationEventTarget(), 
		NewEventHandlerUPP(MenuSelected), 1, &eventType, g, NULL);
	eventType.eventClass = kEventClassKeyboard;
	eventType.eventKind = kEventRawKeyDown;
	InstallApplicationEventHandler(NewEventHandlerUPP(KeyDown), 1, &eventType, g, NULL);
	eventType.eventClass = kEventClassKeyboard;
	eventType.eventKind = kEventRawKeyUp;
	InstallApplicationEventHandler(NewEventHandlerUPP(KeyUp), 1, &eventType, g, NULL);
	
	SetQDGlobalsRandomSeed(TickCount());
	
	InitSettings(&g->Settings);
	InitMenu(&g->m);
	InitWindow(&g->w);
	InitInterface(&g->Interface, &g->w, g);
	
	GetSoundManager(); /* Make sure SM is inited */
	
	eventType.eventClass = kEventClassWindow;
	eventType.eventKind = kEventWindowActivated;
	InstallWindowEventHandler(g->w.TheWindow, NewEventHandlerUPP(AppActivated), 1, &eventType, g, NULL);
	eventType.eventClass = kEventClassWindow;
	eventType.eventKind = kEventWindowDeactivated;
	InstallWindowEventHandler(g->w.TheWindow, NewEventHandlerUPP(AppDeactivated), 1, &eventType, g, NULL);
	
	g->Status = MENU;
	SetOrtho(&g->w);
	g->w.quitOnClose = true;
	g->lastTime = GetCurrentEventTime();
	g->slop = 0.0;
	g->Paused = false;
	g->ModeFade = 0.0;
	g->Frames = 0;
	g->FramesPerSecond = -1;
	g->FrameTimer = TickCount();
}

void RunGame(VGame * g)
{
	InitGame(g);
	RunApplicationEventLoop();
	CleanUpGame(g);
}

void CleanUpGame(VGame * g)
{
	RemoveEventLoopTimer(g->timerRef);

	CleanUpWindow(&g->w);
	CleanUpMenu(&g->m);
	CleanUpSettings(&g->Settings);
}

void SwitchMode(VGame * g, int Mode)
{
	int previousMode;
	
	previousMode = g->Status;
	g->Status = Mode;
	if (Mode == MENU && previousMode != MENU)
	{
		SetOrtho(&g->w);
		ShowCursor();
		CleanUpWorld(&g->world);
		CleanUpConsole(&g->world.Console);
	} else if ((Mode & PLAYING) && !(previousMode & PLAYING))
	{
		SetPerspective(&g->w);
		if (g->w.IsFullScreen) HideCursor();
		InitConsole(&g->world.Console, &g->world.Player, &g->FramesPerSecond);
		InitWorld(&g->world);
		g->Direction = STATIONARY;
		g->Rotation = STATIONARY;
		g->lastTime = GetCurrentEventTime();
		g->Paused = false;
		g->Frames = 0;
		g->FramesPerSecond = -1;
		g->FrameTimer = TickCount();
	}
	g->ModeFade = 0.0;
}
