/// The game manages all game systems and runs the game loop
///
/// The systems managed by the game are the modules, resource handlers and the event system
/// The game loop is started by calling startGame() and ended by calling endGame()
/// When you start the game it will first initialize the modules, then load the first scene and then start updating all the modules.
/// Modules can get access to other systems when they are initialized.
/// The game can also be paused and the current scene can be both set and retrieved
class Game
{
public:
/// Constructor
Game();
/// Destructor
///
/// Frees all modules, resource handlers and the event system
~Game();
/// Adds a module to the game
///
/// Adds a module of the given type to the game
/// @tparam ModuleType The type of module to add
/// @tparam ConstructionArgs [deducible] The arguments necessary to construct the module
/// @param a_arguments The arguments used to construct the module, can be left empty
/// @returns A reference to the added module
/// @note You do not need to specify the types of construction arguments in the template list, you can simple write addModule<ExampleModule>(arg1, arg2, arg3)
template <typename ModuleType, typename... ConstructionArgs>
ModuleType& addModule(ConstructionArgs&&... a_arguments);
/// Returns the module of the given type
///
/// @tparam ModuleType The type of module to get
/// @returns A reference to the requested module
/// @warning The game must contain the module of the given type
/// @see hasModule()
template <typename ModuleType>
ModuleType& getModule();
/// Returns the module of the given type
///
/// @tparam ModuleType The type of module to get
/// @returns A const reference to the requested module
/// @warning The game must contain the module of the given type
/// @see hasModule()
template <typename ModuleType>
const ModuleType& getModule()const;
/// Returns true if the game has the module of the given type
/// @tparam ModuleType The type of module to check
/// @returns True if the game has the requested module
/// @see addModule()
template <typename ModuleType>
bool hasModule()const;
/// Adds a ResourceManager of the given type for the given type of resource using the given construction arguments
///
/// @tparam ResourceType The type of resource the handler will handle
/// @tparam ResourceHandlerType The type of resource handler to add
/// @tparam ConstructionArgs [deducible] The arguments used to call the constructor of the resource handler
/// @param a_arguments The arguments of the constructor of the resource handler
/// @returns A reference to the added resource handler
/// @see getHandlerFor() hasHandlerFor()
/// @warning Any given resource type can only have one handler at once
template <typename ResourceType, typename ResourceHandlerType, typename... ConstructionArgs>
ResourceHandlerType& addResourceHandler(ConstructionArgs&&... a_arguments);
/// Gets a reference to the current resource handler for the given type of resource
///
/// @tparam ResourceType The type of resource the requested handler is for
/// @returns A reference to the current resource handler for the given type of resource
/// @warning The game MUST contain a handler for the given type of resource
/// @see hasHandlerFor() addResourceHandler()
template <typename ResourceType>
IResourceHandler<ResourceType>& getHandlerFor();
/// Checks if the game contains a resource handler for the given type of resource
///
/// @tparam ResourceType The type of resource the requested handler is for
/// @returns True if the game contains a resource handler for the given type of resource
/// @see addResourceHandler()
template <typename ResourceType>
bool hasHandlerFor()const;
/// Registers the given module to the game loop
///
/// Will make the game call the module's update method every update
/// @param a_module The module that will be registered to the game loop
/// @param a_priority The priority of the module within the game loop - this determines the order of systems to update
/// @attention The given module must have a public method with the following signature: void update(float); the float being the elapsed time since the last update
/// @warning A module can only be registered to the game loop once
/// @see isInGameLoop()
void registerToGameLoop(IModule& a_module, const GameLoopPriority& a_priority, bool a_updateWhilePaused);
/// Checks if the given module is currently registered to the game loop
///
/// @param a_module The module to test
/// @returns True if the given module is currently registered to the game loop
/// @see registerToGameLoop()
/// @note This is a slow function and is meant mainly for debugging purposes
bool isInGameLoop(const IModule& a_module)const;
/// Starts the game loop
///
/// Will initialize all modules, load the first scene and start updating until the game ends
void startGameLoop();
/// Ends the game loop
///
/// Will make the game loop end as soon as the current update finishes
void endgameLoop();
/// Gets a reference to the event system
///
/// @returns A reference to the event system
inline EventSystem& getEventSystem();
/// Gets a const-reference to the event system
///
/// @returns A const-reference to the event system
inline const EventSystem& getEventSystem()const;
/// @brief Checks if the game has a valid current scene
/// @return True if the game has a valid current scene
bool hasScene()const;
/// Gets a reference to the current scene
///
/// @returns A reference to the current scene
Scene& getCurrentScene();
/// Gets a const-reference to the current scene
///
/// @returns A const-reference to the current scene
const Scene& getCurrentScene()const;
/// Switches to the given scene
///
/// Will switch to the given scene before the next update.
/// A SceneSwitchEvent will be emitted through the EventSystem when the switching occurs.
/// @param a_newScene A reference to the scene to switch to
/// @attention General scene ownership and loading is not managed by the game class, it only holds a reference to the current scene
/// @see getCurrentScene()
void switchToScene(const UniquePtrReference<Scene>& a_newScene);
/// Checks if the game is currently paused
///
/// @returns True if the game is currently paused
/// @see pauseGame() unpauseGame()
inline bool isPaused()const;
/// Pauses the game
///
/// Emits a GamePauseEvent through the EventSystem owned by this game
/// @note This does nothing if the game is already paused
/// @see isPaused()
void pauseGame();
/// Unpauses the game
///
/// Emits an GameUnpauseEvent through the EventSystem owned by this game
/// @note This does nothing if the game isn't paused
/// @see isPaused()
void unpauseGame();
}