//-----------------------------------------------------------------------------------------------
// TheGame.cpp
//

//-----------------------------------------------------------------------------------------------
#include "Game/TheGame.hpp"
#include "Engine/Core/Application.hpp"
#include "Engine/Console/Console.hpp"
#include "Engine/Audio/Audio.hpp"
#include "Engine/Renderer/Renderer.hpp"
#include "Engine/Gameplay/Grid2D.hpp"
#include "Engine/Core/Raycast2D.hpp"
#include "Engine/Renderer/Renderer_OpenGL3_Win32.hpp" // Fixme: HACK to use OpenGL directly, as our Renderer interface isn't fully mature yet


//-----------------------------------------------------------------------------------------------
TheGame theGame;


//-----------------------------------------------------------------------------------------------
ShaderProgram* g_testShaderProgram = nullptr;
LogicalTexture g_testRedTile;
LogicalTexture g_testGreyTile;
LogicalTexture g_testPurpleTile;
LogicalTexture g_testBlueTile;


//-----------------------------------------------------------------------------------------------
// Return true if application should create and start up on all requested engine subsystems.
// Return false to bypass normal startup and shutdown commands; engine systems must be created,
//	started, shut down, and destroyed manually by the game.  The game's PreStartup and
//	PostStartup methods will also not be called automatically by the application.  This behavior
//	can be useful for special cases such as tools and commandlets.
//
bool TheGame::ProcessCommandLine( CommandLineCommands& commandLineCommands )
{
	UNUSED( commandLineCommands );
	return true;
}


//-----------------------------------------------------------------------------------------------
// Called after requested engine systems have been constructed, but before they are started up.
// Useful for Game code to get a chance to make early calls into engine systems, to affect their
//	initialization during startup; for example, window resolution and fullscreen settings.
//
// Set values in each engine system's m_preInitSettings struct to configure how it will start up.
//
void TheGame::PreStartup()
{
	theApplication->m_preInitSettings.m_appName = "Gypsy Engine Test";
	theApplication->m_preInitSettings.m_appTitle = "Gypsy Engine Test Game // Squirrel Eiserloh";
	theApplication->m_preInitSettings.m_preferredMaxFramerate = 60.f;
	theApplication->m_preInitSettings.m_isFullscreenPreferred = false;
	theApplication->m_preInitSettings.m_preferredAspectRatio = (16.f / 9.f);
	theApplication->m_preInitSettings.m_preferredResolution = IntVector2( 1280, 720 );
//	theRenderer->SetMaxCapabilityLevel( RENDERER_CAPABILITY_OPENGL15 );

	LoadGameAssets();
}


//-----------------------------------------------------------------------------------------------
// Called after requested engine systems have been started up and are fully operational.
//
void TheGame::PostStartup()
{
//	return;
	g_testShaderProgram = theRenderer->CreateOrGetShaderProgram( "UnlitBasic", "UnlitBasic" );
	SoundID testSoundID = theAudioSystem->CreateOrGetSound( "Data/Audio/TestSound.mp3" );
	theAudioSystem->PlaySound( testSoundID );
	Texture* testAtlasImage = theRenderer->CreateOrGetTextureFromFile( "Data/Images/TestAtlas.png" );
	g_testRedTile = LogicalTexture( testAtlasImage, AABB2( 0.75f, 0.00f, 1.00f, 0.25f ) );
	g_testGreyTile = LogicalTexture( testAtlasImage, IntVector2(2,1), IntVector2(4,4) );
	g_testBlueTile = theRenderer->CreateOrGetLogicalTextureFromFile( "Data/Images/TestAtlas.png", AABB2( 0.25f, 0.00f, 0.50f, 0.25f ) );
	g_testPurpleTile = theRenderer->CreateOrGetLogicalTextureFromFile( "Data/Images/TestAtlas.png", IntVector2(2,0), IntVector2(4,4) );
//	LogicalTexture testPurpleR = theRenderer->GetLogicalTexture( testAtlas );
//	LogicalTexture textRedR( testAtlas, IntVector2( 3, 0 ), IntVector2( 4, 4 ) );
//	LogicalTexture* ?? testPurpleR = theRenderer->CreateLogicalTexture( "Data/Images/TestAtlas.png", IntVector2( 2, 0 ), IntVector2( 4, 4 ) );

//	LoadGameAssets();
}


//-----------------------------------------------------------------------------------------------
void DrawDebugStuff()
{
	Texture* testTexture = theRenderer->CreateOrGetTextureFromFile( "Data/Images/TestAtlas.png" );
	theRenderer->UseShaderProgram( nullptr );
	theRenderer->Draw2dAABB( AABB2( 100.f, 100.f, 400.f, 400.f ), Rgba::ORGANIC_ORANGE );
	theRenderer->Draw2dTexturedAABB( AABB2( 500.f, 100.f, 800.f, 400.f ), Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::ORGANIC_ORANGE, *testTexture );
};


//-----------------------------------------------------------------------------------------------
// Main entry function into Game code.
// This function should not return until the game is exiting and it's time to shut everything down.
//
void TheGame::RunFrame()
{
	const Vector2 screenSize( 1600.f, 900.f );
	theRenderer->SetScreenVirtualOrtho( screenSize, false );

//	DrawDebugStuff();
//	return;

	double timeNow = 3.0 * GetCurrentTimeSeconds();
	float red = 0.5f + 0.5f * static_cast< float >( sin( timeNow + 0.0 ) );
	float green = 0.2f; // 0.2f + 0.1f * sin( timeNow + (fTWO_PI * 1.f/3.f) );
	float blue = 1.f - red; // 0.3f + 0.2f * sin( timeNow + (fTWO_PI * 2.f/3.f) );

	Rgba clearColor;
	clearColor.SetRGBAFloats( red, green, blue, 1.f );
	clearColor = Rgba::CHARCOAL;
	theRenderer->ClearScreen( clearColor );

	static double lastTime = GetCurrentTimeSeconds();
	double thisTime = GetCurrentTimeSeconds();
	float deltaSeconds = static_cast< float >( thisTime - lastTime );
	lastTime = thisTime;

	Texture* postTestTexture = theRenderer->CreateOrGetTextureFromFile( "Data/Images/RousFaceWithShadow-BlackOnTransparent-2048.png" );
	AABB2 testTextureBounds( 500.f, 100.f, 1200.f, 800.f );
	ShaderProgram* unlitBasic = theRenderer->CreateOrGetShaderProgram( "UnlitBasic", "UnlitBasic" );
	ShaderProgram* postEffect = theRenderer->CreateOrGetShaderProgram( "PostBasic", "PostBasic" );
	theRenderer->UseShaderProgram( unlitBasic );
	theRenderer->BindTextureToSampler( nullptr, 0 );
	theRenderer->Draw2dAABB( testTextureBounds, Rgba( 255, 128, 30, 255 ) );

	AABB2 testTextureBounds2( 100.f, 500.f, 200.f, 600.f );
	theRenderer->Draw2dTexturedAABB( testTextureBounds2, Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::WHITE, g_testGreyTile );

	AABB2 testTextureBounds3( 250.f, 500.f, 350.f, 600.f );
	theRenderer->Draw2dTexturedAABB( testTextureBounds3, Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::WHITE, g_testPurpleTile );

	AABB2 testTextureBounds4( 100.f, 650.f, 200.f, 750.f );
	theRenderer->Draw2dTexturedAABB( testTextureBounds4, Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::WHITE, g_testBlueTile );

	AABB2 testTextureBounds5( 250.f, 650.f, 350.f, 750.f );
	theRenderer->Draw2dTexturedAABB( testTextureBounds5, Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::WHITE, g_testRedTile );

	theRenderer->UseShaderProgram( postEffect );
	theRenderer->Draw2dTexturedAABB( testTextureBounds, Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::BLUE, *postTestTexture );

	static double radians = 0.0;
	radians += deltaSeconds;
	Vector2 center = testTextureBounds.CalcCenter();
	Vector2 size = testTextureBounds.CalcSize();
	float radius = 0.5f * GetMinFloat( size.x, size.y );
	Vector2 displacement;
	displacement.SetLengthAndYawRadians( radius, static_cast< float >( radians ) );
	Vector2 outer = center + displacement;

	theRenderer->UseShaderProgram( g_testShaderProgram );
	theRenderer->BindTextureToSampler( nullptr, 0 );
	theRenderer->SetLineThickness( 5.f );
	theRenderer->Draw2dLineSegment( center, outer, Rgba::PURPLE, Rgba::GREEN );

	ShaderProgram* textEffect = theRenderer->CreateOrGetShaderProgram( "TextBasic", "TextBasic" );
	theRenderer->UseShaderProgram( textEffect );
	float edgeThreshold = 0.5f + 0.2f * (float) sin( 0.4 * timeNow );
	float softness = 0.15f;

	// TEMP HACK
//	Vector2 mouseFractions = theApplication->GetCursorNormalizedCoords();
	IntVector2 cursorPos = theApplication->GetCursorPhysicalClientCoords();
	IntVector2 clientSize = theApplication->GetPhysicalWindowClientAreaResolution();
	float cursorNormalizedX = (float) cursorPos.x / (float) clientSize.x;
	float cursorNormalizedY = (float) cursorPos.y / (float) clientSize.y;
	edgeThreshold = Clamp( cursorNormalizedX, 0.f, 1.f );
	softness = Clamp( cursorNormalizedY, 0.f, 1.f );

	glUniform1f( textEffect->m_uniformLocation_edgeThreshold, edgeThreshold );
	glUniform1f( textEffect->m_uniformLocation_softness, softness );

	float fontHeight = 150.f;// + 120.f * (float) sin( 0.1 * timeNow );
	BitmapFont* testFont = theRenderer->CreateOrGetFont( "Gypsy" );
	theRenderer->Draw2dTextStringInBoxWordWrapped( "The quick brown fox jumped over the lazy dog.", AABB2( 10.f, 10.f, 1590.f, 490.f ), fontHeight, Rgba::WHITE, ALIGNMENT_2D_CENTER, testFont );

	theRenderer->UseShaderProgram( nullptr );
	theRenderer->Draw2dAABB( AABB2( 100.f, 810.f, 170.f, 880.f ), Rgba::WHITE );
	theRenderer->Draw2dTexturedAABB( AABB2( 200.f, 810.f, 270.f, 880.f ), Vector2::ZERO, Vector2::ONE, Rgba::WHITE, *theRenderer->GetDefaultTexture() );
	theRenderer->Draw2dTexturedAABB( AABB2( 300.f, 810.f, 370.f, 880.f ), Vector2::ZERO, Vector2::ONE, Rgba::WHITE, *theRenderer->GetDefaultDiffuseMap() );
	theRenderer->Draw2dTexturedAABB( AABB2( 400.f, 810.f, 470.f, 880.f ), Vector2::ZERO, Vector2::ONE, Rgba::WHITE, *theRenderer->GetDefaultNormalMap() );
	theRenderer->Draw2dTexturedAABB( AABB2( 500.f, 810.f, 570.f, 880.f ), Vector2::ZERO, Vector2::ONE, Rgba::WHITE, *theRenderer->GetDefaultSpecGlossEmitMap() );
	theRenderer->Draw2dTexturedAABB( AABB2( 600.f, 810.f, 670.f, 880.f ), Vector2::ZERO, Vector2::ONE, Rgba::WHITE, *theRenderer->GetMissingTextureTexture() );

	Texture* textureThatFailedToLoad = theRenderer->CreateOrGetTextureFromFile( "filePathToATextureThatDoesntExistForExample.png" );
	theRenderer->Draw2dTexturedAABB( AABB2( 1250.f, 450.f, 1580.f, 780.f ), Vector2( 0.f, 1.f ), Vector2( 1.f, 0.f ), Rgba::WHITE, *textureThatFailedToLoad );
}


//-----------------------------------------------------------------------------------------------
// Return true if the game consumed the event.
// Return false if the event should be passed on to the next in the Chain of Responsibility (e.g. DefWindowProc)
//
bool TheGame::ProcessSystemEvent( const SystemEvent& systemEvent )
{
	if( systemEvent.m_eventType == EVENT_TYPE_BUTTON_PRESSED )
	{
		DebuggerPrintf( "Button down event for #%d (0x%02x)\n", systemEvent.m_keyButtonCode, systemEvent.m_keyButtonCode );
		if( systemEvent.m_keyButtonCode == BUTTON_KEYBOARD_ESCAPE )
		{
			theApplication->Quit();
			return true;
		}
		else if( systemEvent.m_keyButtonCode == BUTTON_KEYBOARD_TILDE )
		{
			theConsole->ToggleFullConsole();
			return true;
		}
// 		else if( systemEvent.m_keyButtonCode == BUTTON_KEYBOARD_ENTER )
// 		{
// 			theConsole->ToggleMinimalConsole();
// 		}
	}

	return false;
}


//-----------------------------------------------------------------------------------------------
// Called before requested engine systems have been shut down.
//
void TheGame::PreShutdown()
{
}


//-----------------------------------------------------------------------------------------------
// Called after requested engine systems have been shut down, but before they are destroyed.
//
void TheGame::PostShutdown()
{
}


//-----------------------------------------------------------------------------------------------
void TheGame::LoadGameAssets()
{
	const char* testXmlFilePath = "Data/GameplayData/Test.xml";
	SquirrelXMLDocument testXML( testXmlFilePath );
	if( testXML.IsValid() ) 
	{
		DebuggerPrintf( "Loaded test XML document \"%s\".\n", testXmlFilePath );
	}
	else
	{
		FATAL_ERROR( Stringf( "ERROR: Failed to load required game asset \"%s\"!", testXmlFilePath ) );
	}

	SquirrelXMLElement* rootElement = testXML.GetRootElement();
	if( rootElement )
	XMLDebugDumpTree( *rootElement );
}



