/**
 * @file
 * 'pygildas' module definition and initialization functions.
 */

/*****************************************************************************
 *                              Dependencies                                 *
 *****************************************************************************/

#undef _DEBUG // for python52 in release mode
#include <Python.h>
#include <string.h>
#include "gsys/cfc.h"
#include "sic/gpackage-pyimport.h"
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#include "numpy/arrayobject.h"
/* NPY_ARRAY_CARRAY available since numpy 1.7.0,
   NPY_CARRAY deprecated since this version */
#ifndef NPY_ARRAY_CARRAY
#define NPY_ARRAY_CARRAY NPY_CARRAY
#endif
#include "pygildas.h"

int fmtr4, fmtr8, fmti4, fmti8, fmtl, fmtby, fmtc4; /* Global variables */


/*****************************************************************************
 *                             Function bodies                               *
 *****************************************************************************/

/**
 * Return a numpy array sharing data with the corresponding SIC
 * variable.
 * Python input is the address of the variable descriptor. The descriptor
 * is used to retrieve the fundamental values of the variable (ndims,
 * dims, type, data address).
 *
 * @param[in] self.
 * @param[in] args is a PyObject with input params values.
 * @param[in] kwds is a PyObject with input params optional keywords.
 * @return the PyArray sharing data with the corresponding SIC variable.
 */
PyObject* gpy_mkarray(PyObject *self, PyObject *args, PyObject *kwds) {
    address_t *desc;  /* Descriptor address */
    address_t  address;
    int        type,i,item_type,dims[1+SIC_MAXDIMS];
    int        ndims,dim1,dim2,dim3,dim4,dim5,dim6,dim7;
    npy_intp   trdims[1+SIC_MAXDIMS];

    static char *kwlist[] = {"desc",NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "l", kwlist, &desc))
       return NULL;

    if (desc) { /* desc == 0 would core dump */
       type    = (int) (long) desc[0];
       address = desc[1];
       ndims   = (int) (long) desc[5];
       dim1    = (int) (long) desc[6];
       dim2    = (int) (long) desc[7];
       dim3    = (int) (long) desc[8];
       dim4    = (int) (long) desc[9];
       dim5    = (int) (long) desc[10];
       dim6    = (int) (long) desc[11];
       dim7    = (int) (long) desc[12];
    } else {
       return NULL;
    }

    dims[0] = dim1;
    dims[1] = dim2;
    dims[2] = dim3;
    dims[3] = dim4;
    dims[4] = dim5;
    dims[5] = dim6;
    dims[6] = dim7;
    dims[7] = 0;

    if (type==fmti4) {
       item_type = NPY_INT32;
    } else if (type==fmti8) {
       item_type = NPY_INT64;
    } else if (type==fmtr4) {
       item_type = NPY_FLOAT32;
    } else if (type==fmtr8) {
       item_type = NPY_FLOAT64;
    } else if (type==fmtl) {
       item_type = NPY_INT32; /* Not NPY_BOOL */
    } else if (type>0) {
       item_type = NPY_STRING;
    } else {
       item_type = 0;
       fprintf(stderr," E-PYTHON, In 'gpy_mkarray': unknown type.\n");
    }

    for (i=0; i<ndims; i++) {
       trdims[i] = dims[ndims-i-1];
    }

    for (i=ndims; i<SIC_MAXDIMS+1; i++) {
       trdims[i] = 0;
    }

    /* PyArray_UpdateFlags may be called here to set write-protection *
     * 'NPY_WRITEABLE' flag, but this is not Numeric-compatible. Else *
     * this is done in Python with the setflags() method of the       *
     * numpy.ndarray. */

    /* 'PyArray_FromDimsAndData' segfaults if it is not in the module *
     * main source (the current 'pygildas.c'). Maybe 'import_array()' *
     * (numpy initialization) troubles.*/

    /* PyArray_FromDimsAndData is now obsolete in numpy. */
    if (type>0) {
       return PyArray_New(&PyArray_Type,
                          ndims,
                          trdims,
                          item_type,
                          NULL,
                          address,
                          type,
                          NPY_ARRAY_CARRAY,
                          NULL);
    } else {
       return PyArray_SimpleNewFromData(ndims,trdims,item_type,address);
    }
}


/**
 * Define the methods table associated to pygildas +Python module+.
 * For each method, its name in Python, the associated function, the Python
 * calling method, and its __doc__ string are given.
 */
static PyMethodDef pygildas_methods[] = {
    /* local methods: */
    {"mkarray",   (PyCFunction)gpy_mkarray,   METH_VARARGS | METH_KEYWORDS, "Incarnate a numpy.ndarray from SIC descriptor address."},
    /* pgvar methods: */
    {"dictsize",  (PyCFunction)gpy_dictsize,  METH_NOARGS,                  "Return the SIC dictionary size."},
    {"dataptr",   (PyCFunction)gpy_dataptr,   METH_VARARGS | METH_KEYWORDS, "Return the data address."},
    {"descptr",   (PyCFunction)gpy_descptr,   METH_VARARGS | METH_KEYWORDS, "Return the descriptor address and variable level."},
    {"getname",   (PyCFunction)gpy_getname,   METH_VARARGS | METH_KEYWORDS, "Return ith name in SIC dictionary."},
    {"isro",      (PyCFunction)gpy_isro,      METH_VARARGS | METH_KEYWORDS, "Is variable RO or RW?"},
    {"varexist",  (PyCFunction)gpy_varexist,  METH_VARARGS | METH_KEYWORDS, "Does the variable exist?"},
    {"vartype",   (PyCFunction)gpy_vartype,   METH_VARARGS | METH_KEYWORDS, "Return the variable typecode."},
    {"vardims",   (PyCFunction)gpy_vardims,   METH_VARARGS | METH_KEYWORDS, "Return the variable dimensions."},
    {"defvar",    (PyCFunction)gpy_defvar,    METH_VARARGS | METH_KEYWORDS, "Define a variable in SIC."},
    {"defstruct", (PyCFunction)gpy_defstruct, METH_VARARGS | METH_KEYWORDS, "Define a structure in SIC."},
    {"delvar",    (PyCFunction)gpy_delvar,    METH_VARARGS | METH_KEYWORDS, "Delete a variable in SIC."},
    {"varlevel",  (PyCFunction)gpy_varlevel,  METH_NOARGS,                  "Return the current variable level in SIC."},
    {"isimage",   (PyCFunction)gpy_isimage,   METH_VARARGS | METH_KEYWORDS, "Is variable an image or not?"},
    {"isalias",   (PyCFunction)gpy_isalias,   METH_VARARGS | METH_KEYWORDS, "Is variable an alias or not?"},
    /* pgcommand methods */
    {"command",   (PyCFunction)gpy_sicexec,       METH_VARARGS, "Send command line to current language."},
    {"sicexit",   (PyCFunction)gpy_onpythonexit,  METH_NOARGS,  "Finalize SIC."},
    {"getlogical",(PyCFunction)gpy_get_log_trans, METH_VARARGS, "Translate a SIC logical."},
    {"parsefile", (PyCFunction)gpy_parse_file,    METH_VARARGS, "Parse a file name or path for SIC logicals."},
    {"sicerror",  (PyCFunction)gpy_sicerror_set,  METH_NOARGS,  "Turn on the error flag, trapped by Sic when it gets hand back"},
    {"message",   (PyCFunction)gpy_message,       METH_VARARGS, "Print a Sic message"},
    /* libsic methods */
    {"enter",     (PyCFunction)gpy_enterprog, METH_NOARGS,  "Jump to GILDAS prompt."},
    {"exitloop",  (PyCFunction)gpy_exitloop,  METH_NOARGS,  "Exit Python interactive loop."},
    {NULL}  /* Sentinel */
};

/**
 * 'pygildas' initialization function (called once at first import).
 * It loads numpy in background, initializes 'pygildas', and adds
 * some attributes to the module.
 *
 * @return nothing.
 */
/* Macros loaded from gpackage-pyimport.h */
MOD_INIT(pygildas) {
    PyObject *m, *maindict;
    char *module_doc;
    void pygildas_set_initialized(void);
    MOD_DEF_DECL("pygildas", pygildas_methods)

    /* Imports the array (and ufunc) objects */
    import_array();
    /* import_ufunc(); */

    /* Creates the module and add the its methods */
    module_doc = "Provide GILDAS variables handlers through Numpy.ndarray's.";
    MOD_DEF_CODE(m, "pygildas", module_doc, pygildas_methods)
    /* Checks for errors */
    if (m == NULL) {
        Py_FatalError("E-PYTHON,  Could not initialize 'pygildas' module");
        return MOD_ERROR_VAL;
    }
    pygildas_set_initialized();

    /* Adds some objects to 'pygildas' module: */

    /* SIC types integers */
    sic_typecodes(&fmtr4, &fmtr8, &fmti4, &fmti8, &fmtl, &fmtby, &fmtc4);
    PyModule_AddIntConstant(m,"fmtby",(long) fmtby);
    PyModule_AddIntConstant(m,"fmtr4",(long) fmtr4);
    PyModule_AddIntConstant(m,"fmtr8",(long) fmtr8);
    PyModule_AddIntConstant(m,"fmti4",(long) fmti4);
    PyModule_AddIntConstant(m,"fmti8",(long) fmti8);
    PyModule_AddIntConstant(m,"fmtl", (long) fmtl);
    PyModule_AddIntConstant(m,"fmtc4",(long) fmtc4);
    /* __main__ dictionnary reference */
    maindict = gpy_maindict();
    Py_INCREF(maindict);
    PyModule_AddObject(m,"maindict", maindict);
    /* Define an internal temporary dictionary in which Gildas variables
       can be imported */
    PyModule_AddObject(m,"dict", PyDict_New());

    return MOD_SUCCESS_VAL(m);
}
