/*
 * Author : Pierre Schnizer <schnizer@users.sourceforge.net>
 *
 * $Id:
 */
#ifndef PyGSL_API_INTERN_H
#define PyGSL_API_INTERN_H 1

#include <Python.h>
#include <pygsl_lite/numpy_api_version.h>
/* todo: how to handle global config */
#include <gsl/gsl_errno.h>
// #include <pygsl/pygsl_config.h>
// #define PyGSL_SET_GSL_ERROR_HANDLER
#define GSL_DISABLE_DEPRECATED
// #define DEBUG 0
/* end pygsl config */

#include <pygsl_lite/transition.h>
#include <pygsl_lite/capsuletrunk.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

#ifndef PyGSL_PY3K
#error "Currently only python 3.0"
#endif

#ifndef PyGSL_SET_GSL_ERROR_HANDLER
#define PyGSL_SET_GSL_ERROR_HANDLER 1
#endif

#ifndef GSL_DISABLE_DEPRECATED
#define GSL_DISABLE_DEPRECATED
#endif


__BEGIN_DECLS

/*
#define PyGSL_API_UNIQUE_SYMBOL PyGSL_API
*/
/*
 * This API import trick was copied from Numeric/arrayobject.h
 */
#if defined(PyGSL_API_UNIQUE_SYMBOL)
#define PyGSL_API PyGSL_API_UNIQUE_SYMBOL
#endif

/* C API address pointer */
#if defined(PyGSL_NO_IMPORT_API)
extern void **PyGSL_API;
#else
#if defined(PyGSL_API_UNIQUE_SYMBOL)
void **PyGSL_API;
#else
static void **PyGSL_API;
#endif /* PyGSL_API_UNIQUE_SYMBOL */
#endif /* PyGSL_NO_IMPORT_API */


#ifndef _PyGSL_API_MODULE
#define PyGSL_API_EXTERN extern
#else /* _PyGSL_API_MODULE */
#define PyGSL_API_EXTERN static
#endif /* _PyGSL_API_MODULE */



/*
 * The version of the API. Allows to check if the used 
 * API is correct.
 */
#define PyGSL_API_VERSION       ((unsigned long) ( 0x3))
/* Functions found in the file <pygsl_lite/error_helpers.h> */
#define PyGSL_api_version_NUM                           0
#define PyGSL_error_flag_NUM                            1
#define PyGSL_error_flag_to_pyint_NUM                   2
#define PyGSL_warning_NUM                               3
#define PyGSL_add_traceback_NUM                         4
#define PyGSL_module_error_handler_NUM                  5

/* float, complex, int conversions */
#define PyGSL_pyfloat_to_double_NUM                     6
#define PyGSL_pylong_to_ulong_NUM                       7
#define PyGSL_pylong_to_uint_NUM                        8
#define PyGSL_check_python_return_NUM                   9
#define PyGSL_clear_name_NUM                           10

#define PyGSL_PyComplex_to_gsl_complex_NUM             11
#define PyGSL_PyComplex_to_gsl_complex_float_NUM       12
#define PyGSL_PyComplex_to_gsl_complex_long_double_NUM 13

/* vector matrix conversion */
#define PyGSL_stride_recalc_NUM                        14
#define PyGSL_PyArray_new_NUM                          15
#define PyGSL_PyArray_copy_NUM                         16

#define PyGSL_PyArray_prepare_gsl_vector_view_NUM      17
#define PyGSL_PyArray_prepare_gsl_matrix_view_NUM      18
#define PyGSL_PyArray_generate_gsl_vector_view_NUM     19
#define PyGSL_PyArray_generate_gsl_matrix_view_NUM     20
#define PyGSL_copy_pyarray_to_gslvector_NUM            21
#define PyGSL_copy_pyarray_to_gslmatrix_NUM            22
#define PyGSL_copy_gslvector_to_pyarray_NUM            23
#define PyGSL_copy_gslmatrix_to_pyarray_NUM            24

#define PyGSL_vector_or_double_NUM                     25

/* rng object */
#define PyGSL_RNG_ObjectType_NUM                       26
#define PyGSL_gsl_rng_from_pyobject_NUM                27
#define PyGSL_function_wrap_helper_NUM                 28

/* solver type */
#define PyGSL_solver_type_NUM                          29
#define PyGSL_solver_ret_int_NUM                       30
#define PyGSL_solver_ret_double_NUM                    31
#define PyGSL_solver_ret_size_t_NUM                    32
#define PyGSL_solver_ret_vec_NUM                       33
#define PyGSL_solver_dn_init_NUM                       34
#define PyGSL_solver_vd_i_NUM                          35
#define PyGSL_solver_vvdd_i_NUM                        36
#define PyGSL_Callable_Check_NUM                       37
#define PyGSL_solver_func_set_NUM                      38

#define PyGSL_function_wrap_OnOn_On_NUM                39
#define PyGSL_function_wrap_Op_On_Opn_NUM              40
#define PyGSL_function_wrap_On_O_NUM                   41
#define PyGSL_function_wrap_Op_On_NUM                  42
#define PyGSL_function_wrap_Op_Opn_NUM                 43
#define PyGSL_solver_n_set_NUM                         44
#define PyGSL_solver_set_f_NUM                         45
#define PyGSL_solver_getset_NUM                        46

/* to be resorted */
#define PyGSL_vector_check_NUM                         50
#define PyGSL_matrix_check_NUM                         51
#define PyGSL_array_check_NUM                          52

/* Add Debug Number */
#define PyGSL_register_debug_flag_NUM                  61

#define PyGSL_error_string_for_callback_NUM            62

#define PyGSL_pyint_to_int_NUM                         63
#define PyGSL_string_as_string_NUM                     64
#define PyGSL_NENTRIES_NUM                             65

#ifndef _PyGSL_API_MODULE
#endif /* _PyGSL_API_MODULE */

/*
 * Entries in the API.
 * WARNING: This is the length of the entries.
 * All Entries defined here with _NUM must be smaller than this number!!
 */


/*
 * I define the error handler here as the api import shall also set the error
 * handler. The error handler must be set in each module speratley as on some
 * platforms GSL is linked statically, thus each module has its seperate error
 * gsl handler.
 * And I think the other platforms can spare these extra cycles, so I do that
 * on all platforms.
 */
PyGSL_API_EXTERN void
PyGSL_module_error_handler(const char *reason, /* name of function */
			   const char *file,   /* from CPP */
			   int line,           /* from CPP */
			   int gsl_error);     /* real "reason" */


/*
 * Used to inform the module of the approbriate number
 */
PyGSL_API_EXTERN int
PyGSL_register_debug_flag(int *, const char * module_name);

#ifndef _PyGSL_API_MODULE
#define PyGSL_module_error_handler\
 (*(void (*)(const char *, const char *, int, int)) PyGSL_API[PyGSL_module_error_handler_NUM])
#define PyGSL_register_debug_flag \
 (*(int (*)(int *, const char *)) PyGSL_API[PyGSL_register_debug_flag_NUM])
#endif  /* _PyGSL_API_MODULE */

#if DEBUG == 1
static int pygsl_lite_debug_level = 0;
#define PyGSL_DEBUG_LEVEL() (pygsl_lite_debug_level)
#define PyGSL_init_debug()  (PyGSL_register_debug_flag(&pygsl_lite_debug_level, __FILE__))
#else /*  DEBUG == 1 */
#define PyGSL_DEBUG_LEVEL() (DEBUG)
#define PyGSL_init_debug()  (GSL_SUCCESS)
#endif /*  DEBUG == 1 */


/* #define PyGSL_SET_GSL_ERROR_HANDLER 1 */
/* define the following variable if you want to have the old style error handler */
/* #define _PyGSL_MODULE_ERROR_HANDLER_OLD_STYLE 1 */
#define _PyGSL_MODULE_ERROR_HANDLER_FUNC \
    (*(void (*)(const char *, const char *, int, int))PyGSL_API[PyGSL_module_error_handler_NUM])
#ifdef PyGSL_SET_GSL_ERROR_HANDLER
#define PyGSL_SET_ERROR_HANDLER() \
        gsl_set_error_handler(_PyGSL_MODULE_ERROR_HANDLER_FUNC)
#define PyGSL_CHECK_ERROR_HANDLER() \
    if(PyGSL_SET_ERROR_HANDLER() != _PyGSL_MODULE_ERROR_HANDLER_FUNC){\
        fprintf(stderr, "Installation of error handler failed! In File %s\n", __FILE__);\
    }
#else  /* PyGSL_SET_GSL_ERROR_HANDLER */ 
#define PyGSL_SET_ERROR_HANDLER() gsl_set_error_handler_off()
#define PyGSL_CHECK_ERROR_HANDLER() 
#endif /* PyGSL_SET_GSL_ERROR_HANDLER */

#define pygsl_lite_error(reason, file, line, pygsl_lite_errorno)  \
     _PyGSL_MODULE_ERROR_HANDLER_FUNC((reason), (file), (line), (pygsl_lite_errorno))


#define PyGSL_ERROR_VAL(reason, gsl_errno, value) \
       do { \
       pygsl_lite_error((reason), __FILE__, __LINE__, (gsl_errno)) ; \
       return (value) ; \
       } while (0)

#define PyGSL_ERROR(reason, gsl_errno)      PyGSL_ERROR_VAL((reason), (gsl_errno), (gsl_errno))
#define PyGSL_ERROR_NULL(reason, gsl_errno) PyGSL_ERROR_VAL((reason), (gsl_errno), NULL)

static const char pygsl_lite_api_name[] = "pygsl_lite_api";

#ifndef _PyGSL_API_MODULE

/**
 * @todo: pass filename in which the marco is evaluated?
 */
static int _init_pygsl_lite(const char* filename, const char * func_name, const int lineno)
{
   PyObject *pygsl_lite = NULL, *c_api = NULL, *md = NULL;
   unsigned long version;

   if(! (pygsl_lite = PyImport_ImportModule("pygsl_lite.init")) ){
       fprintf(stderr, "FAILED %s:@%d %s import of pygsl_lite.init: failed to import module\n",  filename,  lineno, func_name);
       return -1;
   }
   if (!  (md = PyModule_GetDict(pygsl_lite)) ){
       fprintf(stderr, "FAILED %s:@%d %s import of pygsl_lite.init: failed to get module dictionary\n",  filename,  lineno, func_name);
       return -1;
   }
   if (! (c_api = PyDict_GetItemString(md, "_PYGSL_API")) ){
       fprintf(stderr, "FAILED %s:@%d %s import of pygsl_lite.init: failed to get c_api object\n",  filename,  lineno, func_name);
       return -1;
   }
   if( ! PyCapsule_CheckExact(c_api) ) {
       fprintf(stderr, "FAILED %s:@%d %s import of pygsl_lite.init: c_api object is not a python capsule \n",  filename,  lineno, func_name);
       return -1;
   }

   PyGSL_API = (void **)PyCapsule_GetPointer(c_api, pygsl_lite_api_name);
   if(!PyGSL_API) {
       fprintf(stderr, "FAILED %s:@%d %s import of pygsl_lite.init: c_api capsule contained NULL pointer \n",  filename,  lineno, func_name);
       return -1;
   }
   version = (unsigned long) PyGSL_API[PyGSL_api_version_NUM];
   if (PyGSL_API_VERSION != version ){
       fprintf(stderr, "%s@%d pygsl_lite: init compiled for PyGSL_API_VERSION 0x%lx but found 0x%lx!\n", filename, lineno, PyGSL_API_VERSION, version);
   }
   PyGSL_SET_ERROR_HANDLER();
   PyGSL_CHECK_ERROR_HANDLER();
   if((PyGSL_init_debug()) != GSL_SUCCESS) {
       PyGSL_API = NULL;
       fprintf(stderr, "FAILED %s:@%d %s import of pygsl_lite.init: failed to register debug switch\n",  filename,  lineno, func_name);
   }
   return 0;
}


#define init_pygsl_lite1(ret) { \
if (_init_pygsl_lite(__FILE__, __FUNCTION__, __LINE__) < 0) { \
    PyErr_Print(); \
    PyErr_SetString( \
        PyExc_ImportError, \
        "pygsl_lite.init failed to import" \
    ); \
    return ret; \
  } \
}

#define init_pygsl_lite() init_pygsl_lite1(NULL)
#endif  /* _PyGSL_API_MODULE */



__END_DECLS

#endif  /*  PyGSL_API_INTERN_H */
