#ifndef __H5_ERRORHANDLING_H
#define __H5_ERRORHANDLING_H

#include <stdarg.h>

#ifdef __cplusplus
extern "C" {
#endif

enum h5_rtypes {
	e_int = 0,
	e_ssize_t,
	e_char_p,
	e_void_p,
	e_h5_err_t,
	e_h5_int64_t,
	e_h5_id_t,
	e_h5_ssize_t,
	e_h5_errorhandler_t,
	e_h5_file_p,
	e_h5t_lvl_idx_t,
	e_h5t_iterator_p,
	e_h5_loc_id_t,
	e_h5_loc_idx_t,
	e_hid_t,
	e_H5O_type_t,
	e_herr_t
};

struct call_stack_entry {
	char* name;
	enum h5_rtypes type;
};

struct call_stack {
	int level;
	struct call_stack_entry entry[1024];
};

extern h5_int32_t h5_debug_level;
extern int h5_initialized;
extern struct call_stack h5_call_stack;
extern h5_err_t	h5_errno;

void
h5_initialize (
	void
	);

#define CHECK_FILEHANDLE( f )					\
	if ( h5_check_filehandle ( f ) != H5_SUCCESS )		\
		return h5_get_errno();


#define CHECK_WRITABLE_MODE( f )					\
	if ( f->mode==H5_O_RDONLY )					\
		return h5_error (					\
			H5_ERR_INVAL,					\
			"Attempting to write to read-only file" );

#define is_writable(f)	(f->mode != H5_O_RDONLY)


#define CHECK_READONLY_MODE( f )					\
	if ( ! f->mode==H5_O_RDONLY )					\
		return h5_error (					\
			H5_ERR_INVAL,					\
			"Operation is not allowed on writable files." );

#define CHECK_TIMEGROUP( f )						\
	if ( f->step_gid <= 0 )						\
		return h5_error (					\
			H5_ERR_INVAL,					\
			"Time step is invalid! Have you set the time step?");

#define h5_error_not_implemented()				     \
	h5_error(						     \
		H5_ERR_NOT_IMPLEMENTED,				     \
		"%s: Function '%s', line %d not yet implemented!", \
		__FILE__, __func__, __LINE__);

#define h5_error_internal()   \
	h5_error(				   \
		H5_ERR_INTERNAL,		   \
		"%s: Internal error: %s line %d!", \
		__FILE__, __func__, __LINE__)


h5_err_t
h5_set_debuglevel (
	const h5_id_t level
	);

h5_err_t
h5_get_debuglevel (
	void
	);

h5_err_t
h5_set_errorhandler (
	const h5_errorhandler_t errorhandler
	);

h5_errorhandler_t
h5_get_errorhandler (
	void
	);

h5_err_t
h5_get_errno (
	void
	);

void
h5_set_errno (
	const h5_err_t h5_errno
	);

static inline void
h5_call_stack_init (
	const char* fname,
	enum h5_rtypes type
	) {
	h5_call_stack.level = 0;
	h5_call_stack.entry[0].name = (char *)fname;
	h5_call_stack.entry[0].type = type;
}

static inline void
h5_call_stack_push (
	const char* fname,
	enum h5_rtypes type
	) {
	h5_call_stack.entry[h5_call_stack.level].name = (char *)fname;
	h5_call_stack.entry[h5_call_stack.level].type = type;
	h5_call_stack.level++;
}

static inline const char*
h5_call_stack_pop (
	void
	) {
	return h5_call_stack.entry[--h5_call_stack.level].name;
}

static inline const char*
h5_call_stack_get_name (
	void
	) {
	return h5_call_stack.entry[h5_call_stack.level-1].name;
}

static inline const char*
h5_get_funcname (
	void
	) {
	return h5_call_stack.entry[0].name;
}

static inline enum h5_rtypes
h5_call_stack_get_type (
	void
	) {
	return h5_call_stack.entry[h5_call_stack.level-1].type;
}

static inline int
h5_call_stack_get_level (
	void
	) {
	return h5_call_stack.level;
}

static inline const char*
h5_call_stack_reset (
	void
	) {
	h5_call_stack.level = 0;
	return h5_call_stack.entry[0].name;
}

h5_err_t
h5_report_errorhandler (
	const char *fmt,
	va_list ap
	);

h5_err_t
h5_abort_errorhandler (
	const char *fmt,
	va_list ap
	);

void
h5priv_vprintf (
	FILE* f,
	const char* prefix,
	const char* __funcname,
	const char* fmt,
	va_list ap
	);

h5_err_t
h5_error (
	const h5_err_t error_no,
	const char *fmt,
	...
	)
#ifdef __GNUC__
	__attribute__ ((format (printf, 2, 3)))
#endif
;

void
h5_verror (
	const char* fmt,
	va_list ap
	);

/*!
  \ingroup h5_core_errorhandling

  Print a warning message to \c stderr.
*/

static inline h5_err_t
h5_warn (
	const char *fmt,
	...
	)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
;
static inline h5_err_t
h5_warn (
	const char* fmt,
	...
	) {
	if (h5_debug_level >= 2) {
		va_list ap;
		va_start (ap, fmt);
		h5priv_vprintf (stderr, "W", h5_get_funcname(), fmt, ap);
		va_end (ap);
	}
	return H5_NOK;
}

/*!
  \ingroup h5_core_errorhandling

  Print an informational message to \c stdout.
*/
static inline void
h5_info (
	const char *fmt,
	...
	)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
;
static inline void
h5_info (
	const char* fmt,
	...
	) {
	if (h5_debug_level >= 3) {
		va_list ap;
		va_start (ap, fmt);
		h5priv_vprintf (stdout, "I", h5_get_funcname(), fmt, ap);
		va_end (ap);
	}
}

/*!
  \ingroup h5_core_errorhandling

  Print a debug message to \c stdout.
*/
static inline void
h5_debug (
	const char *fmt,
	...
	)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
	;

static inline void
h5_debug (
	const char *fmt,
	...
	) {
	if (h5_debug_level >= 4) {
		char prefix[1024];
		snprintf (prefix, sizeof(prefix), "%*s %s",
			  h5_call_stack_get_level(), "",
			  h5_call_stack_get_name());
		va_list ap;
		va_start (ap, fmt);
		h5priv_vprintf (stdout, "D", prefix, fmt, ap);
		va_end (ap);
	}
}

#ifdef __cplusplus
}
#endif
	
#endif
