/*
	Copyright (c) 2004/2005 KJK::Hyperion

	Permission is hereby granted, free of charge, to any person obtaining a
	copy of this software and associated documentation files (the "Software"),
	to deal in the Software without restriction, including without limitation
	the rights to use, copy, modify, merge, publish, distribute, sublicense,
	and/or sell copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
	DEALINGS IN THE SOFTWARE.
*/

#ifndef KJK_PSEH_FRAMEBASED_H_
#define KJK_PSEH_FRAMEBASED_H_

#if ((__GNUC__ > 4) && (__GNUC_MINOR__ > 1))
/* warning: this will hide uninitialized variable warnings in the following code */
#  pragma GCC diagnostic ignored "-Wuninitialized"
#endif

#include <pseh/framebased/internal.h>
#include <pseh/excpt.h>

#ifndef offsetof
#	include <stddef.h>
#endif

#if defined(_SEH_NO_NATIVE_NLG)
#	error PSEH setjmp/longjmp fallback is no longer supported
#endif

#if defined(__GNUC__)
#	define _SEHLongJmp __builtin_longjmp
#	define _SEHSetJmp __builtin_setjmp
	typedef void * _SEHJmpBuf_t[5];
#else
#	include <setjmp.h>
#	define _SEHLongJmp longjmp
#	define _SEHSetJmp setjmp
#	define _SEHJmpBuf_t jmp_buf
#endif

#ifdef __cplusplus
#	define _SEH_INIT_CONST static const
#else
#	define _SEH_INIT_CONST register const
#endif

typedef struct __SEHFrame
{
	_SEHPortableFrame_t SEH_Header;
	void * volatile SEH_Locals;
}
_SEHFrame_t;

typedef struct __SEHTryLevel
{
	_SEHPortableTryLevel_t ST_Header;
	_SEHJmpBuf_t ST_JmpBuf;
}
_SEHTryLevel_t;

static __declspec(noreturn) __inline void __stdcall _SEHCompilerSpecificHandler
(
	_SEHPortableTryLevel_t * trylevel
)
{
	_SEHTryLevel_t * mytrylevel;
	mytrylevel = _SEH_CONTAINING_RECORD(trylevel, _SEHTryLevel_t, ST_Header);
	_SEHLongJmp(mytrylevel->ST_JmpBuf, 1);
}

static const int _SEHScopeKind = 1;
static _SEHPortableFrame_t * const _SEHPortableFrame = 0;
static _SEHPortableTryLevel_t * const _SEHPortableTryLevel = 0;

/* SHARED LOCALS */
/* Access the locals for the current frame */
#define _SEH_ACCESS_LOCALS(LOCALS_) \
	_SEH_LOCALS_TYPENAME(LOCALS_) * _SEHPLocals; \
	_SEHPLocals = \
		_SEH_PVOID_CAST \
		( \
			_SEH_LOCALS_TYPENAME(LOCALS_) *, \
			_SEH_CONTAINING_RECORD(_SEHPortableFrame, _SEHFrame_t, SEH_Header) \
				->SEH_Locals \
		);

/* Access local variable VAR_ */
#define _SEH_VAR(VAR_) _SEHPLocals->VAR_

/* FILTER FUNCTIONS */
/* Declares a filter function's prototype */
#define _SEH_FILTER(NAME_) \
	long __stdcall NAME_ \
	( \
		struct _EXCEPTION_POINTERS * _SEHExceptionPointers, \
		struct __SEHPortableFrame * _SEHPortableFrame \
	)

/* Declares a static filter */
#define __SEH_STATIC_FILTER(ACTION_) ((ACTION_) + 2)
#define _SEH_STATIC_FILTER(ACTION_) ((_SEHFilter_t)(__SEH_STATIC_FILTER(ACTION_)))

/* Declares a PSEH filter wrapping a regular filter function */
#define _SEH_WRAP_FILTER(WRAPPER_, NAME_) \
	static __inline _SEH_FILTER(WRAPPER_) \
	{ \
		return (NAME_)(_SEHExceptionPointers); \
	}

/* FINALLY FUNCTIONS */
/* Declares a finally function's prototype */
#define _SEH_FINALLYFUNC(NAME_) \
	void __stdcall NAME_ \
	( \
		struct __SEHPortableFrame * _SEHPortableFrame \
	)

/* Declares a PSEH finally function wrapping a regular function */
#define _SEH_WRAP_FINALLY(WRAPPER_, NAME_) \
	_SEH_WRAP_FINALLY_ARGS(WRAPPER_, NAME_, ())

#define _SEH_WRAP_FINALLY_ARGS(WRAPPER_, NAME_, ARGS_) \
	static __inline _SEH_FINALLYFUNC(WRAPPER_) \
	{ \
		NAME_ ARGS_; \
	}

#define _SEH_WRAP_FINALLY_LOCALS_ARGS(WRAPPER_, LOCALS_, NAME_, ARGS_) \
	static __inline _SEH_FINALLYFUNC(WRAPPER_) \
	{ \
		_SEH_ACCESS_LOCALS(LOCALS_); \
		NAME_ ARGS_; \
	}

/* SAFE BLOCKS */
#ifdef __cplusplus
#	define _SEH_DECLARE_HANDLERS(FILTER_, FINALLY_) \
		static const _SEHHandlers_t _SEHHandlers = { (FILTER_), (FINALLY_) };
#else
#	define _SEH_DECLARE_HANDLERS(FILTER_, FINALLY_) \
		_SEHHandlers_t _SEHHandlers = { (0), (0) }; \
		_SEHHandlers.SH_Filter = (FILTER_); \
		_SEHHandlers.SH_Finally = (FINALLY_);
#endif

#define _SEH_SetExceptionCode(CODE_) (_SEHPortableFrame->SPF_Code = (CODE_))
#define _SEH_GetExceptionCode() (unsigned long)(_SEHPortableFrame->SPF_Code)

#define _SEH_GetExceptionPointers() \
	((struct _EXCEPTION_POINTERS *)_SEHExceptionPointers)

#define _SEH_AbnormalTermination() (_SEHPortableFrame->SPF_Code != 0)

#define _SEH_LEAVE break

#define _SEH_YIELD(STMT_) \
	for(;;) \
	{ \
		if(!_SEHScopeKind) \
			_SEHReturn(); \
	\
		STMT_; \
	}

#ifdef _ARM_

#define _SEH_TRY \
	for(;;) \
	{ \
		 \
		{ \
	\
			for(;;) \
			{ \
				if(1) \
				{ \
					for(;;) \
					{ \
						{

#define _SEH_EXCEPT(FILTER_) \
						} \
	\
						break; \
					} \
	\
					break; \
				} \
				else \
				{ \
					{ \
						break; \
					} \
				} \
	\
				break; \
			} \
	\
	\
			if(0) \
			{

#define _SEH_FINALLY(FINALLY_) \
						} \
	\
						break; \
					} \
	\
					break; \
				} \
				else \
				{ \
				} \
	\
				break; \
			} \
	\
			(FINALLY_)(&_SEHFrame.SEH_Header); \
	\
			if(0) \
			{

#define _SEH_END \
			} \
		} \
	\
	\
		break; \
	}

#else

#define _SEH_TRY \
	for(;;) \
	{ \
		_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0); \
		_SEHPortableFrame_t * const _SEHCurPortableFrame = _SEHPortableFrame; \
		_SEHPortableTryLevel_t * const _SEHPrevPortableTryLevel = _SEHPortableTryLevel; \
	 \
		{ \
			_SEH_INIT_CONST int _SEHScopeKind = 0; \
			register int _SEHState = 0; \
			register int _SEHHandle = 0; \
			_SEHFrame_t _SEHFrame; \
			_SEHTryLevel_t _SEHTryLevel; \
			_SEHPortableFrame_t * const _SEHPortableFrame = \
				_SEHTopTryLevel ? &_SEHFrame.SEH_Header : _SEHCurPortableFrame; \
			_SEHPortableTryLevel_t * const _SEHPortableTryLevel = &_SEHTryLevel.ST_Header; \
	\
			(void)_SEHScopeKind; \
			(void)_SEHPortableFrame; \
			(void)_SEHPortableTryLevel; \
			(void)_SEHHandle; \
	\
			for(;;) \
			{ \
				if(_SEHState) \
				{ \
					for(;;) \
					{ \
						{

#define _SEH_EXCEPT(FILTER_) \
						} \
	\
						break; \
					} \
	\
					break; \
				} \
				else \
				{ \
					if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0) \
					{ \
						_SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = (FILTER_); \
						_SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = 0; \
	\
						_SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel; \
						_SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header; \
	\
						if(_SEHTopTryLevel) \
						{ \
							if(&_SEHLocals != _SEHDummyLocals) \
								_SEHFrame.SEH_Locals = &_SEHLocals; \
	\
							_SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING); \
							_SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler; \
							_SEHEnterFrame(&_SEHFrame.SEH_Header); \
						} \
	\
						++ _SEHState; \
						continue; \
					} \
					else \
					{ \
						break; \
					} \
				} \
	\
				break; \
			} \
	\
			_SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel; \
	\
			if(_SEHHandle) \
			{

#define _SEH_FINALLY(FINALLY_) \
						} \
	\
						break; \
					} \
	\
					_SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel; \
					break; \
				} \
				else \
				{ \
					_SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = 0; \
					_SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = (FINALLY_); \
	\
					_SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel; \
					_SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header; \
	\
					if(_SEHTopTryLevel) \
					{ \
						if(&_SEHLocals != _SEHDummyLocals) \
							_SEHFrame.SEH_Locals = &_SEHLocals; \
	\
						_SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING); \
						_SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler; \
						_SEHEnterFrame(&_SEHFrame.SEH_Header); \
					} \
	\
					++ _SEHState; \
					continue; \
				} \
	\
				break; \
			} \
	\
			(FINALLY_)(&_SEHFrame.SEH_Header); \
	\
			if(0) \
			{

#define _SEH_END \
			} \
		} \
	\
		if(_SEHTopTryLevel) \
			_SEHLeaveFrame(); \
	\
		break; \
	}

#endif

#define _SEH_HANDLE _SEH_EXCEPT(_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER))

#define _SEH_EnableTracing(LEVEL_) ((void)(_SEHPortableFrame->SPF_Tracing = (LEVEL_)))
#define _SEH_DisableTracing() ((void)(_SEHPortableFrame->SPF_Tracing = _SEH_DO_TRACE_NONE))

#endif

/* EOF */
