//-----------------------------------------------------------------------------------------------
// Application.hpp
//
#pragma once
#ifndef include_Application
#define include_Application

//-----------------------------------------------------------------------------------------------
#include "Engine/Core/EngineSubsystem.hpp"


/////////////////////////////////////////////////////////////////////////////////////////////////
struct ApplicationPreInitSettings
{
	std::string		m_appName;
	std::string		m_appTitle;
	bool			m_createRenderWindow;
	float			m_preferredMaxFramerate;
	bool			m_isFullscreenPreferred;
	float			m_preferredAspectRatio;
	IntVector2		m_preferredResolution;
};


/////////////////////////////////////////////////////////////////////////////////////////////////
class Application : public EngineSubsystem
{
public:
	Application();
	virtual ~Application();
//	virtual void PreConfigure( const NamedProperties& configParams );
	virtual void		StartUp();
	virtual void		Main( CommandLineCommands& commandLineCommands );
	virtual void		BeginFrame();
	virtual void		EndFrame();
	virtual void		ShutDown();

	virtual void		CreateOSWindow( bool isFullscreen, const IntVector2& clientAreaSize ) = 0;
	virtual void		DestroyOSWindow() = 0;
	virtual IntVector2	GetDesktopResolution() const = 0;
	virtual void		SleepSeconds( double secondsToSleep ) = 0;

	// #ToDo: Which (if any) of the following should be allowed to be overridden (virtual)?
	void				SwapBuffers();
	void				WaitUntilNextFrame( float maxFramesPerSecond );
	void				MainLoop();
	void				RunFrame();
	void				CreateRequestedEngineSystems();
	void				DestroyEngineSystems();
	void				StartUpRequestedEngineSystems();
	void				ShutDownEngineSystems();
	void				StartUpEngineSubsystemIfItExists( EngineSubsystem* subsystem );
	void				BeginFrameOnEngineSubsystemIfItExists( EngineSubsystem* subsystem );
	void				EndFrameOnEngineSubsystemIfItExists( EngineSubsystem* subsystem );
	void				ShutDownEngineSubsystemIfItExists( EngineSubsystem* subsystem );
	void				DestroyEngineSubsystemIfItExists( EngineSubsystem* subsystem );
	void				Quit();
	bool				ProcessSystemEventPreGame( const SystemEvent& systemEvent );
	bool				ProcessSystemEventPostGame( const SystemEvent& systemEvent );
	const ButtonState&	GetKeyButtonState( const KeyButton& keyButton ) const;
	bool				IsKeyButtonDown( const KeyButton& keyButton ) const;
	bool				IsShiftDown() const;
	bool				IsControlDown() const;
	bool				IsAltDown() const;
	bool				HasFocus() const { return m_hasFocus; }
	virtual void		SetWindowClientAspectAndResolution();
	IntVector2			GetPhysicalWindowClientAreaResolution() const { return m_windowClientResolution; }
	float				GetPhysicalWindowClientAreaAspectRatio() const { return m_windowClientResolution.CalcAspect(); }
	bool				IsFullscreen() const { return m_isFullscreen; }
	const std::string&	GetApplicationName() const { return m_appName; }
	const std::string&	GetApplicationTitle() const { return m_appTitle; }
	void				SetMaxFramerate( float maxFramesPerSecond ) { m_maxFramerate = maxFramesPerSecond; }
	void				SetCursorVisibility( bool isVisible );
	Vector2				GetCursorNormalizedCoords() const { return m_cursorNormalizedCoords; }
	virtual void		SnapCursorToNormalizedCoords( const Vector2& newNormalizedCoords );
	virtual IntVector2	GetCursorPhysicalClientCoords() const = 0;
	virtual void		SnapCursorToPhysicalClientCoords( const IntVector2& newCursorClientCoords ) = 0;
	void				Command_Quit( const NamedProperties& params );

public:
	ApplicationPreInitSettings	m_preInitSettings;
	Profiler					m_profiler;

protected:
	virtual void		UpdateCursorNormalizedCoords() = 0;
	virtual void		SetNativeCursorVisibility( bool isVisible ) = 0;
	void				ConfigureFromPreInitSettings();

protected:
	bool				m_isQuitting;
	bool				m_hasFocus;
	std::string			m_appName;
	std::string			m_appTitle;
	bool				m_createRenderWindow;
	IntVector2			m_windowClientResolution;
	float				m_windowClientAspect;
	bool				m_isFullscreen;
	float				m_maxFramerate;
	ButtonState			m_keyButtonStates[ BUTTON_MAX_NUM_BUTTONS ];
	Vector2				m_cursorNormalizedCoords;
	IntVector2			m_cursorClientCoords;
	bool				m_isCursorVisible;
};


extern Application* theApplication;



//-----------------------------------------------------------------------------------------------
inline const ButtonState& Application::GetKeyButtonState( const KeyButton& keyButton ) const
{
	return m_keyButtonStates[ keyButton ];
}


//-----------------------------------------------------------------------------------------------
inline bool Application::IsKeyButtonDown( const KeyButton& keyButton ) const
{
	return m_keyButtonStates[ keyButton ].m_isPressed;
}


//-----------------------------------------------------------------------------------------------
inline bool Application::IsShiftDown() const
{
	return m_keyButtonStates[ BUTTON_KEYBOARD_LEFT_SHIFT ].m_isPressed || m_keyButtonStates[ BUTTON_KEYBOARD_RIGHT_SHIFT ].m_isPressed;
}


//-----------------------------------------------------------------------------------------------
inline bool Application::IsControlDown() const
{
	return m_keyButtonStates[ BUTTON_KEYBOARD_LEFT_CTRL ].m_isPressed || m_keyButtonStates[ BUTTON_KEYBOARD_RIGHT_CTRL ].m_isPressed;
}


//-----------------------------------------------------------------------------------------------
inline bool Application::IsAltDown() const
{
	return m_keyButtonStates[ BUTTON_KEYBOARD_LEFT_ALT ].m_isPressed || m_keyButtonStates[ BUTTON_KEYBOARD_RIGHT_ALT ].m_isPressed;
}



#endif // include_Application
