#ifndef INCLUDED_DUBIOUS_NATIVEMANAGER
#define INCLUDED_DUBIOUS_NATIVEMANAGER

#include <Math/Point.h>

#include <android_native_app_glue.h>

#include <vector>
#include <functional>
#include <string>
#include <memory>

//////////////////////////////////////////////////////////////
namespace Dubious {
namespace Utility {

class FilePath;
class NativeAssetFile;
class NativeAssetDir;
class NativeRootWindow;
typedef std::shared_ptr<NativeAssetFile> NativeAssetFilePtr;
typedef std::shared_ptr<NativeAssetDir> NativeAssetDirPtr;
typedef std::shared_ptr<NativeRootWindow> NativeRootWindowPtr;

/// @brief Handles the grungy bits of the Android Native Activity
///
/// Handles all of the standard glue required to create a native android
/// app.  Calls the call back functions where you can worry about app
/// specific stuff
class NativeManager
{
public:
	/// @brief Constructor
	/// @param State - [in] The android_app stats
	NativeManager( struct android_app* State );

	/// @brief Destructor
	~NativeManager();

	/// @brief Runs the application
	///
	/// This is where the main thread dissappears in order to run the
	/// application.  The general usage of this class is to create it, set
	/// up all the function callbacks, and then call Run.  Run will exit
	/// when the user wants to close the application
	void 				Run();

	/// @brief Opens a file asset
	///
	/// This function provides access to asset files in the installed
	/// .apk file.  See the NativeAssetFile for details on how to use it.
	/// @param File - [in] Path to the asset.  Note that the root of the
	/// path is the "assets" directory defined in an android app
	/// @returns A shared pointer to a NativeAssetFile
	NativeAssetFilePtr	OpenAsset( const FilePath& File ) const;

	/// @brief Opens an asset directory
	///
	/// Provides access to the asset directory object.  See the
	/// NativeAssetDir for details
	/// @param File - [in] Path to the asset dir.  Note that the root of
	/// the path is the "assets" directory defined in an android app
	/// @returns A shared pointer to a NativeAssetDir
	NativeAssetDirPtr	OpenAssetDir( const FilePath& File ) const;

	/// @brief Data about a touch point
	///
	/// The system for handling multi-touch on an Android app is unusual.
	/// Each touch point is assigned an ID by the system.  You have to keep
	/// track of each ID yourself.  This struct holds the ID and the current
	/// location of the pointer.
	struct PointerData
	{
		int		ID;
		Dubious::Math::Point Location;
	};
	typedef std::vector<PointerData> PointerDataVector;

	Dubious::Math::Size ScreenDimensions() const;

    std::function<void (const Dubious::Math::Size&)>& OnInitWindow(){ return m_OnInitWindow; }
	std::function<void ()>& OnDestroyWindow(){ return m_OnDestroyWindow; }
	std::function<void ()>& OnStart(){ return m_OnStart; }
	std::function<void ()>& OnResume(){ return m_OnResume; }
	std::function<void ()>& OnPause(){ return m_OnPause; }
	std::function<void ()>& OnStop(){ return m_OnStop; }
	std::function<void ()>& OnDestroy(){ return m_OnDestroy; }
	std::function<void ()>& OnDraw() { return m_OnDraw; }
	std::function<void (float)>& OnPhysics() { return m_OnPhysics; }
	std::function<void (const PointerData&)>& OnPointerDown() { return m_OnPointerDown; }
	std::function<void (const PointerData&)>& OnPointerUp() { return m_OnPointerUp; }
	std::function<void (const PointerDataVector&)>& OnPointerMove() { return m_OnPointerMove; }

private:
	NativeManager( const NativeManager& );
	NativeManager& operator= ( const NativeManager& );

	static void 		StaticHandleCmd( struct android_app* App, int32_t Cmd );
	void 				HandleCmd( struct android_app* App, int32_t Cmd );

	static int32_t		StaticHandleInput( struct android_app* App, AInputEvent* Event );
	int32_t				HandleInput( struct android_app* App, AInputEvent* Event );

	struct android_app* m_State;
	NativeRootWindowPtr m_RootWindow;

    std::function<void (const Dubious::Math::Size&)> m_OnInitWindow;
	std::function<void ()> m_OnDestroyWindow;
	std::function<void ()> m_OnStart;
	std::function<void ()> m_OnResume;
	std::function<void ()> m_OnPause;
	std::function<void ()> m_OnStop;
	std::function<void ()> m_OnDestroy;
	std::function<void ()> m_OnDraw;
	std::function<void (float)>m_OnPhysics;
	std::function<void (const PointerData&)> m_OnPointerDown;
	std::function<void (const PointerData&)> m_OnPointerUp;
	std::function<void (const PointerDataVector&)> m_OnPointerMove;
};

}
}

#endif
