/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		This is the core game class. Handles the high level game logic.
//=====================================================================================================================

#include "Precompiled.h"
#include "Game\GameCore.h"
#include "Game\GameObjectManager.h"
#include "Game\Level.h"
#include "Game\GameHud.h"
#include "Game\GamePostFX.h"
#include "Windows\Window.h"
#include "Graphics\Display.h"
#include "Graphics\Camera2D.h"
#include "Graphics\BitmapFont.h"
#include "Graphics\PrimitivesRenderer.h"
#include "Graphics\BufferedPointRenderer.h"
#include "Graphics\BufferedLineRenderer.h"
#include "Graphics\ParticleEmitter.h"
#include "Resources\TextureManager.h"
#include "Resources\PixelShaderManager.h"
#include "Resources\VertexShaderManager.h"
#include "Resources\BitmapFontManager.h"
#include "GameObjects\PlayerShip.h"
#include "GameObjects\EnemyFactoryManager.h"
#include "GameObjects\EnemyWaspManager.h"
#include "GameObjects\EnemyPredatorManager.h"
#include "Input\GamePad.h"

using namespace Game;

//=====================================================================================================================
// Constants
//=====================================================================================================================

/// Title for the game

const char * GameCore::GAME_TITLE = "Trig Wars V 1.01 By Darragh Coy";

/// Near plane value for the camera

const float GameCore::CAM_Z_NEAR = 1;

/// Far plane value for the camera

const float GameCore::CAM_Z_FAR = 1000;

/// Native view area: any screen resolutions other than this are scaled - keeping aspect correctness

const Vec2i GameCore::NATIVE_RES = Vec2i(1024,1024);

/// Area of the map: this is the half extent of the map area about the origin

const Vec2f GameCore::MAP_AREA = Vec2f(3072,3072);
				
/// Target refresh interval in between game ticks - in seconds.

const float GameCore::FRAME_INTERVAL = 0.01666666666f;

/// Maximum number of frames that we can try to make up from frame skipping if we are behind real time

const unsigned GameCore::MAX_CATCHUP_FRAMES = 4;

#ifdef USE_DEBUG_CONSOLE

	///	Debug only flag. Show map boundaries ?

	bool GameCore::s_show_map_boundaries = false;

	/// Debug only flag. Show a wrap/unwrap test ?

	bool GameCore::s_show_unwrap_test = false;
	
	/// Debug only flag. Profile rendering ?
	
	bool GameCore::s_profile_render = false;
	
	/// Debug only flag. Profile update ?
	
	bool GameCore::s_profile_update = false;

	/// Debug only flag. Zoom out and draw the whole map ?

	bool GameCore::s_show_whole_map = false;
	
	/// Debug only flag. Should enemies stop firing at the player ?
	
	bool GameCore::s_no_target = false;

#endif

//=====================================================================================================================
// Statics
//=====================================================================================================================

/// Pointer to an instance of the game

GameCore * GameCore::s_this = 0;

//=====================================================================================================================
/// @brief		Constructor. Creates the game.
/// @param		window - window used by the game
/// @pre		There must not be another game in existance
/// @pre		Given window must not be null 
/// @pre		Window display must be valid
/// @remarks	The game does NOT assume ownership of this window. This is to be managed by the app.
//=====================================================================================================================

GameCore::GameCore( Window * window ) : m_stack(0x100000) , m_secondary_stack(0x100000)
{
	// Make sure ok:
	
	ENSURE( s_this == 0 , "There must not be another game in existance" );

	// Make sure ok:
	
	ENSURE( window , "Given window must not be null" );
	
	// Make sure the display is valid:
	
	ENSURE( window->GetDisplay() && window->GetDisplay()->IsValid() , "Window display must be valid" )

	// Save a static ref to us:

	s_this = this;	
	
	// Player has no score to begin with
	
	m_player_score = 0;
	
	// Save the window:
	
	m_window = window;
	
	// Make this the current window listener
	
	m_window->SetEventListener(this);
	
	// Do an immediate update
	
	m_time_since_update = FRAME_INTERVAL;
	
	// Didn't render a frame yet
	
	m_rendered_last_frame = false;
	
	// Create all the managers:
		
	m_texture_manager = new TextureManager( m_window->GetDisplay() );
	m_pixel_shader_manager = new PixelShaderManager( m_window->GetDisplay() );
	m_vertex_shader_manager = new VertexShaderManager( m_window->GetDisplay() );
	
	m_bitmap_font_manager = new BitmapFontManager
	(
		m_window->GetDisplay()	,
		m_vertex_shader_manager	,
		m_pixel_shader_manager	,
		m_texture_manager	
	);
	
	// Create the game fonts:
	
	m_font					= new BitmapFont( m_bitmap_font_manager->GetBitmapFont("Fonts\\Game48.xml")				);
	m_small_font			= new BitmapFont( m_bitmap_font_manager->GetBitmapFont("Fonts\\Game32.xml")				);
	m_reading_font			= new BitmapFont( m_bitmap_font_manager->GetBitmapFont("Fonts\\Reading48.xml")			);
	m_reading_shadow_font	= new BitmapFont( m_bitmap_font_manager->GetBitmapFont("Fonts\\Reading48Shadow.xml")	);
	
	// Game object manager:
	
	m_game_object_manager = new GameObjectManager();
	
	// Make the primitives renderer:

	m_primitives_renderer = new PrimitivesRenderer
	(
		m_window->GetDisplay()	,
		m_vertex_shader_manager	,
		m_pixel_shader_manager
	);	

	// Make the buffered points renderer and the buffered line renderer

	m_buffered_point_renderer = new BufferedPointRenderer
	( 
		m_window->GetDisplay()	, 
		m_vertex_shader_manager , 
		m_pixel_shader_manager
	);
	
	m_buffered_line_renderer = new BufferedLineRenderer
	(
		m_window->GetDisplay()	, 
		m_vertex_shader_manager , 
		m_pixel_shader_manager		
	);
	
	// Particle emitter
	
	m_particle_emitter = new ParticleEmitter( m_window->GetDisplay() , m_vertex_shader_manager , m_pixel_shader_manager );

	// Make the camera

	m_camera = new Camera2D
	(
		m_window,
		Vec2f(0,200),
		Vec2f( float(NATIVE_RES.x),  float(NATIVE_RES.y) ),
		2.0f,
		1000.0f
	);

	// Populate the level

	Level::PopulateLevel(m_game_object_manager);

	// Make the hud:

	m_hud = new GameHud();
	
	// Post processing fx unit:
	
	m_post_fx = new GamePostFX( m_window->GetDisplay() , m_vertex_shader_manager , m_pixel_shader_manager );
	
	// Console commands in debug:
	
	#ifdef USE_DEBUG_CONSOLE
	
		DebugConsole::AddCommand( "G_ShowMapBoundaries" , ConCmd_G_ShowMapBoundaries	);
		DebugConsole::AddCommand( "G_ShowUnwrapTest"	, ConCmd_G_ShowUnwrapTest		);
		DebugConsole::AddCommand( "G_NoTarget"			, ConCmd_G_NoTarget				);
		DebugConsole::AddCommand( "G_Profile"			, ConCmd_G_Profile				);
		DebugConsole::AddCommand( "R_Profile"			, ConCmd_R_Profile				);
		DebugConsole::AddCommand( "R_EagleView"			, ConCmd_R_EagleView			);
	
	#endif
	
	// Set current game state:
	
	m_current_state = STATE_SHOW_INSTRUCTIONS;
	
	// Hack: set the camera position to the player position because game is paused initially:
	
	{
		// Get the player:
		
		PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
		
		// Set the camera position:
		
		if ( player ) m_camera->SetPosition(player->GetPosition());
	}
}

//=====================================================================================================================
/// @brief		Destructor. Destroys the game.
//=====================================================================================================================

GameCore::~GameCore()
{	
	// Done with the fx unit
	
	m_post_fx->RemoveReference();

	// Done with the hud

	m_hud->RemoveReference();

	// If we are the current event listener then remove us:
	
	if ( m_window->GetEventListener() == this )
	{
		m_window->SetEventListener(0);
	}
	
	// Get rid of the camera

	m_camera->RemoveReference();
	
	// Particle emitter:
	
	m_particle_emitter->RemoveReference();
	
	// Be sure to clear all the game objects:
	
	m_game_object_manager->Clear();

	// Done with these buffered renderers

	m_buffered_point_renderer->RemoveReference();
	m_buffered_line_renderer->RemoveReference();
	
	// Done with the primitives renderer:
	
	m_primitives_renderer->RemoveReference();
	
	// Done with our fonts:
	
	delete m_font;
	delete m_small_font;
	delete m_reading_font;
	delete m_reading_shadow_font;
	
	// Done with all our managers:
	
	m_game_object_manager->RemoveReference();
	m_bitmap_font_manager->RemoveReference();
	m_texture_manager->RemoveReference();
	m_pixel_shader_manager->RemoveReference();
	m_vertex_shader_manager->RemoveReference();

	// Remove static ref to us:

	s_this = 0;
	
	// Console commands in debug:
	
	#ifdef USE_DEBUG_CONSOLE
	
		DebugConsole::RemoveCommand( "G_ShowMapBoundaries"	);
		DebugConsole::RemoveCommand( "G_ShowUnwrapTest"		);
		DebugConsole::RemoveCommand( "G_NoTarget"			);
		DebugConsole::RemoveCommand( "G_Profile"			);
		DebugConsole::RemoveCommand( "R_Profile"			);
		DebugConsole::RemoveCommand( "R_EagleView"			);
	
	#endif	
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * GameCore::GetTypeName() const
{
	return "GameCore";
}

//=====================================================================================================================
/// @brief		Window redraw event - does the game loop
//=====================================================================================================================

void GameCore::OnWindowRedrawing()
{
	// If we are out of focus then keep reseting the timing values in the timer and pause the game:
	
	if ( m_window->HasFocus() == false )
	{
		// Reset timing deltas:
		
		HighResolutionTimer::Reset();
		
		// If we are not mimized then refresh the display:
		
		if ( m_window->IsMinimized() == false ) OnRender();
		
		// Give up a bit of processor time
		
		Sleep(10);
	}
	else
	{
		// Update the timing values in the timer:
		
		HighResolutionTimer::Update();
		
		// Add the time delta to time elapsed:
		
		m_time_since_update += HighResolutionTimer::GetTimeDelta();
		
		// See if time for another frame:
		
		if ( m_time_since_update >= FRAME_INTERVAL )
		{
			// See if we are more than one frame behind:
			
			if ( m_time_since_update >= FRAME_INTERVAL * 2.0f )
			{
				// Two or more frames behind: if we didn't skip the last frame skip this one
				
				if ( m_rendered_last_frame == true )
				{
					// Only do an update:
					
					OnUpdate();
					
					// Didn't render this frame:
					
					m_rendered_last_frame = false;
					
					// Simulated this much time:
					
					m_time_since_update -= FRAME_INTERVAL;					
				}
				else
				{
					// Do an update and render:
					
					OnUpdate();
					OnRender();
					
					// Rendered this frame:
					
					m_rendered_last_frame = true;
					
					// Simulated this much time:
					
					m_time_since_update -= FRAME_INTERVAL;					
				}
				
				// If we are falling way behind real time then limit the number of frames we try to catch up with:
				
				if ( m_time_since_update >= FRAME_INTERVAL * float(MAX_CATCHUP_FRAMES) )
				{
					// Figure out how much time we are going to discount:
					
					float discount_time = m_time_since_update - ( FRAME_INTERVAL * float(MAX_CATCHUP_FRAMES) );
					
					// Discount the time: we can't keep trying to catch up in an impossible situation
					
					m_time_since_update -= discount_time;
				}
			}
			else
			{
				// Not behind: draw and render as usual
				
				OnUpdate();
				OnRender();
				
				// Rendered this frame:
				
				m_rendered_last_frame = true;
				
				// Simulated this much time:
				
				m_time_since_update -= FRAME_INTERVAL;
			}
		}
		else
		{
			// Not time for an update: give up this processor time slice
			
			Sleep(0);
		}
	}
}

//=====================================================================================================================
/// @brief		Called when the game is to do an update
//=====================================================================================================================

void GameCore::OnUpdate()
{
	// Begin profile if enabled:
	
	#ifdef USE_DEBUG_CONSOLE
	
		if ( s_profile_update ) DebugConsole::BeginProfile("Update");
	
	#endif
	
	// Only do this if playing or game over:
	
	if ( m_current_state == STATE_GAME_PLAYING || m_current_state == STATE_GAME_OVER )
	{
		// Update all objects:
		
		m_game_object_manager->Update(FRAME_INTERVAL);
		
		// Update all particles:
		
		m_particle_emitter->Update(FRAME_INTERVAL);
	}
	
	// Update the current state of the game
	
	UpdateGameState();
	
	// If select is pressed on the gamepad then close the window
	
	Gamepad::GamepadState gps = Gamepad::GetState(Gamepad::PLAYER_ONE);
	
		// See if pressed:
		
		if ( gps.m_connected && gps.m_back )
		{
			m_window->Close();
		}
	
	// End profile if enabled:
	
	#ifdef USE_DEBUG_CONSOLE
	
		if ( s_profile_update ) DebugConsole::EndProfile("Update");
	
	#endif		
}

//=====================================================================================================================
/// @brief		Called when the game is to do a render
//=====================================================================================================================

void GameCore::OnRender()
{
	// Begin profile if enabled:
	
	#ifdef USE_DEBUG_CONSOLE
	
		if ( s_profile_render ) DebugConsole::BeginProfile("Render");
	
	#endif

	// Clear the display:

	m_window->GetDisplay()->Clear
	(
		Color::Black()	,
		0				,
		CAM_Z_FAR
	);

	// If we are using eagle vision then make sure we can see the entire map with the camera

	#ifdef USE_DEBUG_CONSOLE

		if ( s_show_whole_map )
		{
			m_camera->SetReferenceViewArea
			(
				MAP_AREA * 2.5f
			);
		}

	#endif

	// Begin rendering:

	m_window->GetDisplay()->BeginDraw();
	
		// Begin post processing fx:
		
		m_post_fx->BeginPostProcessing();

			// Draw all game objects:

			m_game_object_manager->Draw( m_camera );
			
			// Create matrix to translate z negatively: needed for particle emitter
			
			Matrix4f translation = Matrix4f::Translation(0,0,-500);
			
			// Get view projection matrix for the camera:
			
			Matrix4f view_proj = m_camera->GetViewProjectionMatrix();
			
			// Combine the two matrices:
			
			Matrix4f combined = translation * view_proj;
			
			// Draw all particles:
			
			m_particle_emitter->Draw( combined , m_camera->GetPosition() );
			
		// End post processing fx and display to the screen:
		
		m_post_fx->EndPostProcessing();	
			
		// Draw the hud:

		m_hud->Draw();
		
	// Debug drawing
	
	#ifdef USE_DEBUG_CONSOLE
	
		if ( s_show_map_boundaries	) DrawMapBoundaries();
		if ( s_show_unwrap_test		) DrawWrapUnwrapTest();
	
	#endif
	
	// End profile if enabled:
	
	#ifdef USE_DEBUG_CONSOLE
	
		if ( s_profile_render ) DebugConsole::EndProfile("Render");
	
	#endif
	
	// End rendering:

	m_window->GetDisplay()->EndDraw();

	// Present the rendered frame:
	
	m_window->GetDisplay()->Present();

	// If in debug then make sure we make the reference view area back to normal:

	#ifdef USE_DEBUG_CONSOLE

		m_camera->SetReferenceViewArea(NATIVE_RES);

	#endif
}

//=====================================================================================================================
///	@brief		Called when the keyboard is pressed
/// @param		key_code - Virtual key code of the key pressed
//=====================================================================================================================

void GameCore::OnKeyboardReleased( Keyboard::Key key_code )
{
	// Quit if escape is pressed:
	
	if ( key_code == Keyboard::KB_ESCAPE )
	{
		m_window->Close();
	}
}

//=====================================================================================================================
/// @brief		Updates what the current state of the game is
//=====================================================================================================================

void GameCore::UpdateGameState()
{
	// Store whether these keys were pressed previously:
	
	static bool gp_prev_a		= false;
	static bool gp_prev_start	= false;
	static bool kb_prev_a		= false;
	static bool kb_prev_enter	= false;
	static bool kb_prev_pause	= false;
	static bool kb_prev_p		= false;

	// Get the current gamepad state
	
	Gamepad::GamepadState gps = Gamepad::GetState(Gamepad::PLAYER_ONE);
	
	// Get the player:

	PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");	

	// See what the state of the game is:
	
	switch ( m_current_state )
	{
		// Showing the instructions screen:
		
		case STATE_SHOW_INSTRUCTIONS:	

			// See if connected and if A is just pressed:
			
			if ( gps.m_connected && gps.m_a && gp_prev_a == false )
			{
				// See if the player is dead: if so we need to recreate the level
				
				if ( player == 0 )
				{
					// Clear the map:
					
					m_game_object_manager->Clear();
					
					// Recreate:
					
					Level::PopulateLevel(m_game_object_manager);
					
					// Clear the player's score:
					
					m_player_score = 0;
					
					// Hack: set the camera position to the player position because game is paused initially:
					
					{
						// Get the player:
						
						PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
						
						// Set the camera position:
						
						if ( player ) m_camera->SetPosition(player->GetPosition());
					}	
				}
			
				// Start the game:
			
				m_current_state = STATE_GAME_PLAYING;
			}
			else if (  ( Keyboard::IsKeyPressed(Keyboard::KB_A) && kb_prev_a == false ) || ( Keyboard::IsKeyPressed(Keyboard::KB_ENTER) && kb_prev_enter == false ) )
			{
				// See if the player is dead: if so we need to recreate the level
				
				if ( player == 0 )
				{
					// Clear the map:
					
					m_game_object_manager->Clear();
					
					// Recreate:
					
					Level::PopulateLevel(m_game_object_manager);
					
					// Clear the player's score:
					
					m_player_score = 0;	
					
					// Hack: set the camera position to the player position because game is paused initially:
					
					{
						// Get the player:
						
						PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
						
						// Set the camera position:
						
						if ( player ) m_camera->SetPosition(player->GetPosition());
					}									
				}			
			
				// Start the game
			
				m_current_state = STATE_GAME_PLAYING;				
			}
		
		break;
	
		// Playing the game:
		
		case STATE_GAME_PLAYING:

			// See if paused key pressed:
			
			if ( gps.m_connected && gps.m_start && gp_prev_start == false )
			{
				// Pause the game
				
				m_current_state = STATE_GAME_PAUSED;
			}
			else if ( ( Keyboard::IsKeyPressed(Keyboard::KB_PAUSE) && kb_prev_pause == false ) || ( Keyboard::IsKeyPressed(Keyboard::KB_P) && kb_prev_p == false ) )
			{
				// Pause the game
				
				m_current_state = STATE_GAME_PAUSED;
			}
			else
			{
				// See if the player is dead
				
				if ( m_game_object_manager->FindByType<PlayerShip>("PlayerShip") == 0 )
				{
					// Player is dead
					
					m_current_state = STATE_GAME_OVER;
				}
				else
				{
					// See if all enemies are dead:
					
					if ( AllEnemiesDead() )
					{
						// Player has won the game
						
						m_current_state = STATE_GAME_WON;
					}
				}
			}		
	
		break;
		
		// Game is over:
		
		case STATE_GAME_OVER:

			// See if connected and if A is pressed:
			
			if ( gps.m_connected && gps.m_a && gp_prev_a == false )
			{
				// Show the instructions screen
			
				m_current_state = STATE_SHOW_INSTRUCTIONS;
			}
			else if ( ( Keyboard::IsKeyPressed(Keyboard::KB_A) && kb_prev_a == false ) || ( Keyboard::IsKeyPressed(Keyboard::KB_ENTER) && kb_prev_enter == false ) )
			{
				// Start the game
			
				m_current_state = STATE_SHOW_INSTRUCTIONS;				
			}		
		
		break;
		
		// Won the game:
		
		case STATE_GAME_WON:

			// See if connected and if A is pressed:
			
			if ( gps.m_connected && gps.m_a && gp_prev_a == false )
			{
				// Clear the map:
				
				m_game_object_manager->Clear();
				
				// Recreate:
				
				Level::PopulateLevel(m_game_object_manager);
				
				// Clear the player's score:
				
				m_player_score = 0;	
				
				// Hack: set the camera position to the player position because game is paused initially:
				
				{
					// Get the player:
					
					PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
					
					// Set the camera position:
					
					if ( player ) m_camera->SetPosition(player->GetPosition());
				}						
			
				// Show the instructions screen
			
				m_current_state = STATE_SHOW_INSTRUCTIONS;
			}
			else if ( ( Keyboard::IsKeyPressed(Keyboard::KB_A) && kb_prev_a == false ) || ( Keyboard::IsKeyPressed(Keyboard::KB_ENTER) && kb_prev_enter == false ) )
			{
				// Clear the map:
				
				m_game_object_manager->Clear();
				
				// Recreate:
				
				Level::PopulateLevel(m_game_object_manager);
				
				// Clear the player's score:
				
				m_player_score = 0;	
				
				// Hack: set the camera position to the player position because game is paused initially:
				
				{
					// Get the player:
					
					PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
					
					// Set the camera position:
					
					if ( player ) m_camera->SetPosition(player->GetPosition());
				}			
			
				// Start the game
			
				m_current_state = STATE_SHOW_INSTRUCTIONS;				
			}		
		
		break;
		
		// Game is paused:
		
		case STATE_GAME_PAUSED:

			// See if the player is there:
			
			PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
			
			// See if connected and if pause is pressed:
			
			if ( gps.m_connected && gps.m_start && gp_prev_start == false )
			{
				// Go to the game or dead state:
				
				if ( player )
				{
					// Only if there are enemies left:
					
					if ( AllEnemiesDead() )
					{
						m_current_state = STATE_GAME_WON;
					}
					else
					{				
						m_current_state = STATE_GAME_PLAYING;
					}
				}
				else
				{
					m_current_state = STATE_GAME_OVER;
				}
			}
			else if ( ( Keyboard::IsKeyPressed(Keyboard::KB_PAUSE) && kb_prev_pause == false ) || ( Keyboard::IsKeyPressed(Keyboard::KB_P) && kb_prev_p == false ) )
			{
				// Go to the game or dead state:
				
				if ( player )
				{
					// Only if there are enemies left:
					
					if ( AllEnemiesDead() )
					{
						m_current_state = STATE_GAME_WON;
					}
					else
					{				
						m_current_state = STATE_GAME_PLAYING;
					}
				}
				else
				{
					m_current_state = STATE_GAME_OVER;
				}
			}		
		
		break;		
	}
	
	// Save if these keys were pressed previously:
	
	if ( gps.m_connected )
	{
		gp_prev_a		= gps.m_a;
		gp_prev_start	= gps.m_start;
	}
	else
	{
		gp_prev_a		= false;
		gp_prev_start	= false;
	}
	
	kb_prev_a		= Keyboard::IsKeyPressed(Keyboard::KB_A);
	kb_prev_enter	= Keyboard::IsKeyPressed(Keyboard::KB_ENTER);
	kb_prev_pause	= Keyboard::IsKeyPressed(Keyboard::KB_PAUSE);
	kb_prev_p		= Keyboard::IsKeyPressed(Keyboard::KB_P);	
}

//=====================================================================================================================
/// @brief		Tells if all the enemies in the game have been killed
//=====================================================================================================================

bool GameCore::AllEnemiesDead()
{
	// Grab all the managers from the level:
	
	EnemyFactoryManager *	factory_mgr		= m_game_object_manager->FindByType<EnemyFactoryManager>("EnemyFactoryManager");
	EnemyWaspManager *		wasp_mgr		= m_game_object_manager->FindByType<EnemyWaspManager>("EnemyWaspManager");
	EnemyPredatorManager *	predator_mgr	= m_game_object_manager->FindByType<EnemyPredatorManager>("EnemyPredatorManager");	
	
	// See whats left:
	
	if ( factory_mgr && factory_mgr->GetUsedObjectCount() > 0 ) 
	{
		// There are factories left
	
		return false;
	}
	
	if ( wasp_mgr && wasp_mgr->GetUsedObjectCount() > 0 ) 
	{
		// There are wasps left
	
		return false;
	}	
	
	if ( predator_mgr && predator_mgr->GetUsedObjectCount() > 0 ) 
	{
		// There are predators left
	
		return false;
	}	
	
	// If we got to here then there is nothing left: return true
	
	return true;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables showing map boundaries
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

#ifdef USE_DEBUG_CONSOLE

bool GameCore::ConCmd_G_ShowMapBoundaries( bool arg , bool arg_given )
{
	if ( arg_given ) s_show_map_boundaries = arg; return s_show_map_boundaries;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables the unwrap test
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool GameCore::ConCmd_G_ShowUnwrapTest( bool arg , bool arg_given )
{
	if ( arg_given ) s_show_unwrap_test = arg; return s_show_unwrap_test;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables profiling of updates
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool GameCore::ConCmd_G_Profile( bool arg , bool arg_given )
{
	if ( arg_given ) s_profile_update = arg; return s_profile_update;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables profiling of rendering
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool GameCore::ConCmd_R_Profile( bool arg , bool arg_given )
{
	if ( arg_given ) s_profile_render = arg; return s_profile_render;
}

//=====================================================================================================================
/// @brief		Debug only console command: enables / disables zoom and rendering of the entire map
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool GameCore::ConCmd_R_EagleView( bool arg , bool arg_given )
{
	if ( arg_given ) s_show_whole_map = arg; return s_show_whole_map;
}

//=====================================================================================================================
/// @brief		Debug only console command: turns off enemies firing at the player
/// @param		arg - if an argument was given
/// @param		arg - argument from the console
/// @param		arg_given - if an argument was given
/// @returns	value of the parameter
//=====================================================================================================================

bool GameCore::ConCmd_G_NoTarget( bool arg , bool arg_given )
{
	if ( arg_given ) s_no_target = arg; return s_no_target;
}

//=====================================================================================================================
/// @brief		Debug only: draws map boundaries
//=====================================================================================================================

void GameCore::DrawMapBoundaries() const
{
	m_primitives_renderer->DrawLine
	(
		m_camera,
		Vec2f( - MAP_AREA.x , + MAP_AREA.y ),
		Vec2f( + MAP_AREA.x , + MAP_AREA.y ),
		0xff00ff00
	);
	
	m_primitives_renderer->DrawLine
	(
		m_camera,
		Vec2f( + MAP_AREA.x , + MAP_AREA.y ),
		Vec2f( + MAP_AREA.x , - MAP_AREA.y ),
		0xff00ff00
	);
	
	m_primitives_renderer->DrawLine
	(
		m_camera,
		Vec2f( + MAP_AREA.x , - MAP_AREA.y ),
		Vec2f( - MAP_AREA.x , - MAP_AREA.y ),
		0xff00ff00
	);
	
	m_primitives_renderer->DrawLine
	(
		m_camera,
		Vec2f( - MAP_AREA.x , - MAP_AREA.y ),
		Vec2f( - MAP_AREA.x , + MAP_AREA.y ),
		0xff00ff00
	);				
}

//=====================================================================================================================
/// @brief		Debug only: draws the unwrap test
//=====================================================================================================================

void GameCore::DrawWrapUnwrapTest() const
{
	// Get the player object:
	
	PlayerShip * player = m_game_object_manager->FindByType<PlayerShip>("PlayerShip");
	
	// See if there:
	
	if ( player )
	{
		// Get a point past the the players position in current direction
		
		Vec2f p = player->GetPosition() + player->GetDirection() * 100.0f;
		
		// Wrap:
		
		p = PositionalObject::WrapCoordinates(p);
		
		// Now unwrap:
		
		p = PositionalObject::UnwrapCoordinates( player->GetPosition() , p );
		
		// Draw line:
		
		m_primitives_renderer->DrawLine(m_camera,p,player->GetPosition(),0xffffffff);
	}			
}

#endif	// #ifdef USE_DEBUG_CONSOLE