#ifndef APPLICATION_H
#define APPLICATION_H

namespace Salamander
{

class IManager;
class PlatformEvent;

/*
** This class is an instance of the engine. Use Salamander by subclassing 'IManager' and using 
** 'addManager()' to register your class with the engine.
*/
class Application
{
public:
	/*
	** The destructor
	*/
    virtual ~Application();

	/*
	** Static access to the application class.
	*/
	static Application& application();

	/*
	** Calling this method runs the main game loop. It will attempt to call 'startup()' to 
	** initialise everything. It will then run a tight game loop until the platform tells it
	** to quit.
	*/
    void run();

	/*
	** This method returns the time difference between the current frame and the previous frame.
	** Updates within the game should be multiplied by this factor to create framerate independent
	** motion.
	*/
	double delta() const;

	/*
	** This method handles platform events, such as window management, flow control and user input.
	** It can be used by the user to force program state changes, such as quitting.
	*/
	void onPlatformEvent(const PlatformEvent& event);

	/*
	** These methods should be used to add custom types of manager subclassed from IManager. While
	** 'getManager()' can be used to retrieve a manager of a specified type, it is suggested that
	** managers are registered globally by using 'registerGlobalManager()'.
	*/
	bool addManager(IManager& manager);
	IManager& getManager(const UniqueId& managerId);

private:
	/*
	** This is a singleton. It cannot be instantiated except by itself.
	*/
	Application();

	/*
	** Startup is called at the time the application is run. It initialises
	** all static instances, local variables, sets up memory, etc. This method
	** will call the 'startup()' function for each registered manager.
	*/
    bool startup();

	/*
	** Shutdown is called immediately after the main game loop terminates. It should
	** free any memory allocated in 'startup()' ready for the application quit. This 
	** method will call the 'shutdown()' function for each registered manager.
	*/
    bool shutdown();

	/*
	** Frame is called internally once every cycle of the game loop. It delegates frame
	** updates to all registered managers.
	*/
	void frame();

	/*
	** This flag is polled once every game loop. When set to 'false' the game will end
	** and 'shutdown()' will be run.
	*/
	bool running_;

	/*
	** Frame delta is calculated once at the beginning of every frame and stored so that
	** calls to delta() return the cached value, saving repeated calculation.
	*/
	double delta_;

	/*
	** Clock stores the current CPU tick. This is used to calculate frame differences and
	** timestamp internal messages.
	*/
	long clock_;

	/*
	** This map stores managers based on their UniqueId. A map is used for fast retrieval.
	*/
	std::map<UniqueId, IManager*> managerMap_;
	typedef std::map<UniqueId, IManager*>::iterator ManagerMapIterator;

	/*
	** We also use a vector to store managers, because order of iteration/evaluation is very important.
	** This vector stores the same managers as the map, but in a different order.
	*/
	std::vector<IManager*> managerVector_;
	typedef std::vector<IManager*>::iterator ManagerVectorIterator;
};

/*
** Application is a singleton and can be accessed via 'application()'.
*/
static Application& application()
{
	return Application::application();
}

}

#endif // APPLICATION_H
