/* **************************************************************************

 Author: Stefan Reutter
 Created: August 2013

****************************************************************************/
#ifndef DAMARIS_backend_config_reader_h
#define DAMARIS_backend_config_reader_h


#include <glib.h>
#include <stdexcept>

namespace DAMARIS
{

/**
 * \defgroup BackendConfigReader Backend Config Reader
 * \ingroup machines
 *
 * Allows reading in a backend configuration file implemented as a Glib Key-Value file
 */
/*@{*/

/**
 * Exception class for the config file.
 */
class BackendConfigException : public std::runtime_error
{
public:
	explicit BackendConfigException(const std::string& msg) throw() : std::runtime_error(msg) {};
	explicit BackendConfigException(const char* msg) throw() : std::runtime_error(msg) {};
	virtual ~BackendConfigException() throw() {};

};

/**
 * Class for reading a backend configuration file. Must be constructed with a c string providing the relative path to the config file.
 *
 * BackendConfigReader will first look in the XDG user config folder for the file, then in all system config folders. If no
 * config file is found in any of these locations, a BackendConfigException will be thrown.
 *
 * Example:
 * \code
 * DAMARIS::BackendConfigReader cfgReader("/damaris/backend.conf");
 * std::cout << cfgReader.getInteger("ADC", "id") << "\n";
 * \endcode
 *
 */
class BackendConfigReader
{
public:
	/**
	 * \param configPath C String containing the relative path to the config file
	 */
	BackendConfigReader(const gchar *configPath);
	
	~BackendConfigReader() { g_key_file_free(_keyFile); }; // I hope this is exception safe

	/* -------------------------------------------------------------------- */
	/* The following functions are encapsulating the underlying GKeyFile functionality */

	inline gboolean getBoolean(const gchar *groupName, const gchar *key) {GError* err = NULL; bool ret = g_key_file_get_boolean(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
	inline gdouble getDouble(const gchar *groupName, const gchar *key) {GError* err = NULL; double ret = g_key_file_get_double(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
	inline gint getInteger(const gchar *groupName, const gchar *key) {GError* err = NULL; int ret = g_key_file_get_integer(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
	inline gchar* getValue(const gchar *groupName, const gchar *key) {GError* err = NULL; gchar* ret = g_key_file_get_value(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
	inline gchar* getString(const gchar *groupName, const gchar *key) {GError* err = NULL; gchar* ret = g_key_file_get_string(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};

	/**
	 * This allows you to pass a function pointer to BackendConfigReader in order to call one of the functions on GKeyFile that are not driectly provided.
	 *
	 * Because a function pointer needs to know the full signature, the function pointer must reflect this.
	 *
	 * Example usage:
	 * \code
	 * 	gchar* groupName; // initialized
	 * 	gchar* key; // initialized
	 * 	gint64 (*f)(GKeyFile*, const gchar *, const gchar *, GError*) = &g_key_file_get_int64;
	 * 	gint64 value = getSomething<gint64> (f, groupName, key);
	 * \endcode
	 */
	template<typename returnType>
	returnType getSomething(returnType (*func)(GKeyFile*, const gchar *, const gchar *, GError*), const gchar *groupName, const gchar *key)
	{
		GError* err = NULL;
		returnType ret = func(_keyFile, groupName, key, &err);
		if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);}
		return ret;
	}

	/* -------------------------------------------------------------------- */

private:

	/**
	 * Attempt to load the defined relative config path.
	 *
	 * First, attempt to load the file from the user config dir. If that fails, attempt to load from the system config dir. If that fails as well, exit the program.
	 */
	void loadConfig();

	/**
	 * Load a specific config file. Returns true on success, false on file not found. Throws an error otherwise.
	 */
	bool loadConfigFile(const gchar *keyFileName, GKeyFileFlags flags, GError *error);
	
	/**
	 * (Relative) Path to the damaris backend config
	 */
	const gchar *_damarisConfigPath;

	GKeyFile *_keyFile;
};
/*@}*/


} // namespace DAMARIS

#endif // include guard
