#ifndef __IMPSIG_H__
#define __IMPSIG_H__ 1

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>

typedef struct IMP_EVENT {
  int event;
  int subevent;
  int extra;
} IMP_EVENT;

typedef struct ONEVENT_HANDLER ONEVENT_HANDLER;
typedef struct ONEVENT_HANDLER {
  int eventmask;
  ONEVENT_HANDLER *parent_handler;
  jmp_buf env;
} ONEVENT_HANDLER;

extern IMP_EVENT EVENT; // global
extern ONEVENT_HANDLER *global_handler; // only declared and installed in an onevent block!

extern void signal_event(int event, int subevent, int extra);
extern int caught_event(int event, int eventmask);
extern int eventmask(int bitpos, ...);

// Some randomly invented event,subevent codes during development :-)

// EVENT
#define IMPSIG_IO 9
   // SUBEVENT
   #define IMP_FOPEN_IN_ERROR 1
   #define IMP_FOPEN_OUT_ERROR 2
   #define IMP_EOF_ERROR 3
   #define IMP_FERROR 4
#define IMPSIG_LIB 5
   // SUBEVENT
   #define IMP_MALLOC_ERROR 1

#ifndef IMPSIG_BODY
// Trying to look as much like Imp's %on %event n,n,n, %start as possible!

#ifdef RUNTIME_TRACE
#define on_event(...) ( {                                                         \
  static ONEVENT_HANDLER this_handler;                                            \
  this_handler = (ONEVENT_HANDLER){.eventmask=eventmask(__VA_ARGS__),             \
                                   .parent_handler=global_handler} ; /* chain */  \
  global_handler = &this_handler;                                                 \
  handler_at_this_level = 1;                                                      \
  setjmp(this_handler.env) && caught_event(EVENT.event,this_handler.eventmask);   \
    } )
extern void imp_trace_enter(int line, char *file, const char *funcname);
extern void imp_trace_exit(int line, char *file, const char *funcname);
#define TRACEIN() imp_trace_enter(__LINE__, __FILE__, __PRETTY_FUNCTION__)
#define TRACEOUT() imp_trace_exit(__LINE__, __FILE__, __PRETTY_FUNCTION__)
#define ENTER() int handler_at_this_level = 0; do { TRACEIN(); } while(0)
#define nreturn do {if (global_handler && handler_at_this_level) global_handler = global_handler->parent_handler; TRACEOUT(); return;} while(0)
#define preturn(x) do {if (global_handler && handler_at_this_level) global_handler = global_handler->parent_handler; TRACEOUT(); return x;} while(0)
#else
#define on_event(...) ( {                                                         \
  static ONEVENT_HANDLER this_handler;                                            \
  this_handler = (ONEVENT_HANDLER){.eventmask=eventmask(__VA_ARGS__),             \
                                   .parent_handler=global_handler} ; /* chain */  \
  global_handler = &this_handler;                                                 \
  handler_at_this_level = 1;                                                      \
  setjmp(this_handler.env) && caught_event(EVENT.event,this_handler.eventmask);   \
    } )
#define TRACEIN() 
#define TRACEOUT() 
#define ENTER() int handler_at_this_level = 0; (void)handler_at_this_level
#define nreturn do {if (global_handler && handler_at_this_level) global_handler = global_handler->parent_handler; TRACEOUT(); return;} while(0)
#define preturn(x) do {if (global_handler && handler_at_this_level) global_handler = global_handler->parent_handler; TRACEOUT(); return x;} while(0)
#endif

#endif

#endif
