/**\file n_exceptions.h
*
* Exception management for C
*
*\author Castagnier Micka�l
*
*\version 1.0
*
*\date 20/11/09
*
*/



#ifndef ____EXCEPTION___MANAGEMENT__FOR_C
	#define ____EXCEPTION___MANAGEMENT__FOR_C

#ifdef __cplusplus
	extern "C"
	{
#endif


	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>	
	#include <setjmp.h>



	/*!
	 * Possibly Thrown value, everything is fine
	 */
	#define NO_EXCEPTION		0		
	/*!
	 * Possibly Thrown value, we checked an out of bound operation
	 */
	#define ARRAY_EXCEPTION		1		
	/*!
	 * Possibly Thrown value, we check a divide by zero operation
	 */
	#define DIVZERO_EXCEPTION	2	
	/*!
	 * Possibly Thrown value, we checked an overflow in our arrays
	 */
	#define OVERFLOW_EXCEPTION	4		
	/*!
	 * Possibly Thrown value, we checked an error during a char * parsing
	 */
	#define PARSING_EXCEPTION	8
	/*!
	 * Possibly Thrown value, we checked an error during a IO operation
	 */
	#define IO_EXCEPTION		16




	
	/*!
	 * All possible exceptions in one check
	 */
	#define ALL_EXCEPTIONS (ARRAY_EXCEPTION|DIVZERO_EXCEPTION|OVERFLOW_EXCEPTION|PARSING_EXCEPTION)

	

	/*! Exception stack structure */
	typedef struct ExceptionContextList
	{
		/*! saving the current context */
		jmp_buf context;
		/*! possibly saving a custom message */
		char *custom_msg ;
		/*! saving function, file, line of the exception */
		char *std_msg ;
		/*! pointer to the next stacked context */
		struct ExceptionContextList* head;
	}ExceptionContextList;



	/*! Globale variable for exception stack management */
	extern ExceptionContextList *__Exceptions;



	/*
	 *	Add an exception in the stack 
	 */
	void push_exception(void);

	/*
	 *	Pop an exceptions from the stack
 	 */
	void pop_exception(int);



	/*!
	 *	Macro for replacing try
	 */
	#define Try							\
		{							\
			int __exc_ ;				\
			push_exception();				\
			__exc_ = setjmp( __Exceptions -> context );	\
			if( __exc_ == NO_EXCEPTION ) 			\
			{						
	/*!
	 *	Macro for replacing catch
	 */
	#define Catch( X , MsgPointer ) 	\
			}			\
			if( __exc_ & (X) )	\
			{			\
				int len = 0 ;   \
				 __exc_ = 0 ;	\
				if( __Exceptions -> custom_msg ) \
				{ \
					len = snprintf( NULL , 0 , "%s: %s" , __Exceptions -> std_msg , __Exceptions -> custom_msg ); \
					(MsgPointer) = (char *)calloc( len + 1 , sizeof( char ) ); \
					sprintf( (MsgPointer) , "%s: %s" , __Exceptions -> std_msg , __Exceptions -> custom_msg ); \
				} \
				else \
				{ \
					(MsgPointer) = strdup( __Exceptions -> std_msg ); \
				}
				

	/*!
	 *	Macro helper for closing the try-catch block
	 */
	#define EndTry 					\
			}				\
				pop_exception( __exc_ );\
		}

	/*!
	 *	Macro helper for adding exception throwing in custom functions
	 */
	#define Throw( X , Message ) \
			if( (Message) != NULL ) \
				__Exceptions -> custom_msg = strdup( Message ) ; \
			__Exceptions -> std_msg = (char *)calloc( snprintf(NULL, 0, "From %s in %s line %d", __func__ , __FILE__ , __LINE__ ) + 1 , sizeof( char ) ); \
			sprintf( __Exceptions -> std_msg , "From %s in %s line %d", __func__ , __FILE__ , __LINE__ ); \
			longjmp( __Exceptions -> context , (X) )


#ifdef __cplusplus
	}
#endif


#endif /* #ifndef ____EXCEPTION___MANAGEMENT__FOR_C  */
