// LibGAP API - API for using GAP as shared library.

#ifndef LIBGAP_API_H
#define LIBGAP_API_H

#include "gap.h"

#ifdef __GNUC__
#define unlikely(x)     __builtin_expect(!!(x), 0)
#else
#define unlikely(x)     (x)
#endif


#ifndef GAP_ENTER_DEBUG
#define GAP_ENTER_DEBUG 0
#endif


extern syJmp_buf * _GAP_GetReadJmpError(void);
extern Int  _GAP_GetEnterStackCount(void);
extern void _GAP_IncEnterStackCount(void);
extern void _GAP_DecEnterStackCount(void);
extern void _GAP_SetEnterStackCount(Int count);


#if GAP_ENTER_DEBUG
#define GAP_ENTER_DEBUG_MESSAGE(message, file, line) \
    fprintf(stderr, "%s: %d; %s:%d\n", message, _GAP_EnterStackCount, file, line);
#else
#define GAP_ENTER_DEBUG_MESSAGE(message, file, line)
#endif


#define GAP_EnterStack() \
    GAP_ENTER_DEBUG_MESSAGE("EnterStack", __FILE__, __LINE__); \
    Int _gap_tmp_enter_stack_count = _GAP_GetEnterStackCount(); \
    if (_gap_tmp_enter_stack_count < 0) { \
        _GAP_SetEnterStackCount(-_gap_tmp_enter_stack_count); \
    } else { \
        if (_gap_tmp_enter_stack_count == 0) { \
            MarkStackBottomBags(); \
        } \
        _GAP_IncEnterStackCount(); \
    }


#define GAP_LeaveStack() \
    _GAP_DecEnterStackCount(); \
    GAP_ENTER_DEBUG_MESSAGE("LeaveStack", __FILE__, __LINE__);


static inline int _GAP_Error_Prejmp(const char* file, int line) {
#if GAP_ENTER_DEBUG
    GAP_ENTER_DEBUG_MESSAGE("Error_Prejmp", file, line);
#endif
    if (_GAP_GetEnterStackCount() > 0) {
        return 1;
    }
    return 0;
}


static inline int _GAP_Error_Postjmp(int JumpRet)
{
    if (unlikely(JumpRet != 0)) {
        /* This only should have been called from the outer-most
         * GAP_EnterStack() call so make sure it resets the EnterStackCount; We
         * set EnterStackCount to its negative  which indicates to
         * GAP_EnterStack that we just returned from a long jump and should
         * reset EnterStackCount to its value at the return point rather than
         * increment it again */
        Int tmp_count = _GAP_GetEnterStackCount();
        if (tmp_count > 0) {
            _GAP_SetEnterStackCount(-tmp_count);
        }
        return 0;
    }

    return 1;
}

#define GAP_Error_Setjmp() (_GAP_Error_Prejmp(__FILE__, __LINE__) || \
        _GAP_Error_Postjmp(sySetjmp(*_GAP_GetReadJmpError())))


#define GAP_Enter() GAP_Error_Setjmp(); GAP_EnterStack()
#define GAP_Leave() GAP_LeaveStack()


////
//// Setup and initialisation
////
typedef void (*CallbackFunc)(void);

void GAP_Initialize(int          argc,
                    char **      argv,
                    char **      env,
                    CallbackFunc markBagsCallback,
                    CallbackFunc errorCallback);

Obj GAP_ValueGlobalVariable(const char * name);
Obj GAP_EvalString(const char * cmd);

#endif
