/**
 * \file
 */

#ifndef __MONO_ERROR_INTERNALS_H__
#define __MONO_ERROR_INTERNALS_H__

#include <mono/metadata/object-forward.h>
#include "mono/utils/mono-compiler.h"

/*Keep in sync with MonoError*/
typedef struct {
	unsigned short error_code;
    unsigned short flags;

	/*These name are suggestions of their content. MonoError internals might use them for something else.*/
	const char *type_name;
	const char *assembly_name;
	const char *member_name;
	const char *exception_name_space;
	const char *exception_name;
	union {
		/* Valid if error_code != MONO_ERROR_EXCEPTION_INSTANCE.
		 * Used by type or field load errors and generic error specified by class.
		 */
		MonoClass *klass;
		/* Valid if error_code == MONO_ERROR_EXCEPTION_INSTANCE.
		 * Generic error specified by a managed instance.
		 */
		uint32_t instance_handle;
	} exn;
	const char *full_message;
	const char *full_message_with_fields;
	const char *first_argument;

	void *padding [3];
} MonoErrorInternal;

/* Invariant: the error strings are allocated in the mempool of the given image */
struct _MonoErrorBoxed {
	MonoError error;
	MonoImage *image;
};

/*
Historically MonoError initialization was deferred, but always had to occur,
	even in success paths, as cleanup could be done unconditionally.
	This was confusing.

ERROR_DECL (error)
	This is the overwhelmingly common case.
	Declare and initialize a local variable, named "error",
	pointing to an initialized MonoError (named "error_value",
	using token pasting).

ERROR_DECL_VALUE (foo)
	Declare and initialize a local variable, named "foo";
	no pointer is produced for it.

MONO_API_ERROR_INIT
	This is used for MonoError in/out parameter on a public interface,
	which must be presumed uninitialized. These are often
	marked with MONO_API, MONO_RT_EXTERNAL_ONLY, MONO_PROFILER_API, etc.
	Tnis includes functions called from dis, profiler, pedump, and driver.
	dis, profiler, and pedump make sense, these are actually external and
	uninitialized. Driver less so.

error_init
	Initialize a MonoError. These are historical and usually
	but not always redundant, and should be reduced/eliminated.
	All the non-redundant ones should be renamed and all the redundant
	ones removed.

error_init_reuse
	This indicates an error has been cleaned up and will be reused.
	Consider also changing mono_error_cleanup to call error_init_internal,
	and then remove these.

error_init_internal
	Rare cases without a better name.
	For example, setting up an icall frame, or initializing member data.

new0, calloc, static
	A zeroed MonoError is valid and initialized.
	Zeroing an entire MonoError is overkill, unless it is near other
	bulk zeroing.

All initialization is actually bottlenecked to error_init_internal.
Different names indicate different scenarios, but the same code.
*/
#define ERROR_DECL_VALUE(x) 		MonoError x; error_init_internal (&x)
#define ERROR_DECL(x) 			ERROR_DECL_VALUE (x##_value); MonoError * const x = &x##_value
#define error_init_internal(error) 	((void)((error)->init = 0))
#define MONO_API_ERROR_INIT(error) 	error_init_internal (error)
#define error_init_reuse(error) 	error_init_internal (error)

// Historical deferred initialization was called error_init.

// possible bug detection that did not work
//#define error_init(error) (is_ok (error))

// FIXME Eventually all error_init should be removed, however it is prudent
// to leave them in for now, at least most of them, while we sort out
// the few that are needed and to experiment with adding them back in bulk,
// i.e. in an entire source file. Some are obviously not needed.
//#define error_init(error) // nothing
#define error_init(error) error_init_internal (error)
// Function for experimentation, should go away.
//void error_init(MonoError*);

#define is_ok(error) ((error)->error_code == MONO_ERROR_NONE)

#define return_if_nok(error) do { if (!is_ok ((error))) return; } while (0)
#define return_val_if_nok(error,val) do { if (!is_ok ((error))) return (val); } while (0)

#define goto_if(expr, label) 	  do { if (expr) goto label; } while (0)
#define goto_if_ok(error, label)  goto_if (is_ok (error), label)
#define goto_if_nok(error, label) goto_if (!is_ok (error), label)

/* Only use this in icalls */
#define return_val_and_set_pending_if_nok(error, value) \
do { 							\
	if (mono_error_set_pending_exception ((error)))	\
		return (value); 			\
} while (0)						\

/*
 * Three macros to assert that a MonoError is ok:
 * 1. mono_error_assert_ok(e) when you just want to print the error's message on failure
 * 2. mono_error_assert_ok(e,msg) when you want to print "msg, due to <e's message>"
 * 3. mono_error_assertf_ok(e,fmt,args...) when you want to print "<formatted msg>, due to <e's message>"
 *    (fmt should specify the formatting just for args).
 *
 * What's the difference between mono_error_assert_msg_ok (e, "foo") and
 * mono_error_assertf_ok (e, "foo") ?  The former works as you expect, the
 * latter unhelpfully expands to
 *
 * g_assertf (is_ok (e), "foo, due to %s", ,  mono_error_get_message (err)).
 *
 * Note the double commas.  Turns out that to get rid of that extra comma
 * portably we would have to write really ugly preprocessor macros.
 */
#define mono_error_assert_ok(error)            g_assertf (is_ok (error), "%s", mono_error_get_message (error))
#define mono_error_assert_msg_ok(error, msg)   g_assertf (is_ok (error), msg ", due to %s", mono_error_get_message (error))
#define mono_error_assertf_ok(error, fmt, ...) g_assertf (is_ok (error), fmt ", due to %s", __VA_ARGS__, mono_error_get_message (error))

void
mono_error_dup_strings (MonoError *error, gboolean dup_strings);

/* This function is not very useful as you can't provide any details beyond the message.*/
void
mono_error_set_error (MonoError *error, int error_code, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(3,4);

void
mono_error_set_type_load_class (MonoError *error, MonoClass *klass, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(3,4);

void
mono_error_vset_type_load_class (MonoError *error, MonoClass *klass, const char *msg_format, va_list args);

void
mono_error_set_type_load_name (MonoError *error, const char *type_name, const char *assembly_name, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(4,5);

void
mono_error_set_out_of_memory (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_argument_format (MonoError *error, const char *argument, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(3,4);

void
mono_error_set_argument (MonoError *error, const char *argument, const char *msg);

void
mono_error_set_argument_null (MonoError *oerror, const char *argument, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(3,4);

void
mono_error_set_not_verifiable (MonoError *oerror, MonoMethod *method, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(3,4);

void
mono_error_set_generic_error (MonoError *error, const char * name_space, const char *name, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(4,5);

void
mono_error_set_execution_engine (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_not_implemented (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_not_supported (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_invalid_operation (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_exception_instance (MonoError *error, MonoException *exc);

void
mono_error_set_invalid_program (MonoError *oerror, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_member_access (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

void
mono_error_set_invalid_cast (MonoError *oerror);

static inline void
mono_error_set_remoting (MonoError *error, const char *message)
{
	mono_error_set_generic_error (error, "System.Runtime.Remoting", "RemotingException", "%s", message);
}

static inline void
mono_error_set_divide_by_zero (MonoError *error)
{
	mono_error_set_generic_error (error, "System", "DivideByZeroException", "");
}

static inline void
mono_error_set_index_out_of_range (MonoError *error)
{
	mono_error_set_generic_error (error, "System", "IndexOutOfRangeException", "");
}

static inline void
mono_error_set_overflow (MonoError *error)
{
	mono_error_set_generic_error (error, "System", "OverflowException", "");
}

static inline void
mono_error_set_synchronization_lock (MonoError *error, const char *message)
{
	mono_error_set_generic_error (error, "System.Threading", "SynchronizationLockException", "%s", message);
}

static inline void
mono_error_set_thread_interrupted (MonoError *error)
{
	mono_error_set_generic_error (error, "System.Threading", "ThreadInterruptedException", "");
}

static inline void
mono_error_set_null_reference (MonoError *error)
{
	mono_error_set_generic_error (error, "System", "NullReferenceException", "");
}

void
mono_error_set_argument_out_of_range (MonoError *error, const char *name);

MonoException*
mono_error_prepare_exception (MonoError *error, MonoError *error_out);

MonoException*
mono_error_convert_to_exception (MonoError *error);

void
mono_error_move (MonoError *dest, MonoError *src);

MonoErrorBoxed*
mono_error_box (const MonoError *error, MonoImage *image);

gboolean
mono_error_set_from_boxed (MonoError *error, const MonoErrorBoxed *from);

const char*
mono_error_get_exception_name (MonoError *oerror);

void
mono_error_set_specific (MonoError *error, int error_code, const char *missing_method);

void
mono_error_set_first_argument (MonoError *oerror, const char *first_argument);

#endif
