/**
 * @file
 * Provide functions to interact with SIC variables.
 */

/*****************************************************************************
 *                        Definitions & Macros                               *
 *****************************************************************************/

#define ALIAS_DEFINED -6

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

#undef _DEBUG // for python52 in release mode
#include <Python.h>
#include <string.h>
/* "sic_structures.h" provides 'SICVLN' integer */
#include "gcore/gwidget.h"
#include "gsys/cfc.h"
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#include "numpy/arrayobject.h"
#include "pygildas.h"


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

/**
 * Return the SIC dictionary size.
 * The dictionary size (number of declared variables) is stored in the
 * data value PFSYM(27) of the Fortran module 'sic_dictionaries'. The
 * 'sic_dictsize()' Fortran function returns this value.
 *
 * @return the dictionary size as a PyInt.
 */
PyObject* gpy_dictsize(void) {
   return PyLong_FromLong((long) sic_dictsize()); /* New reference */
}

/**
 * Return the name and execution of i-th variable in dictionary.
 * Python input is the position number in the SIC dictionary. The
 * Fortran routine 'sic_getname' calls 'gag_haslis' and access the
 * the i-th element of the returned list.
 *
 * @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 variable name and execution level as a PyTuple.
 */
PyObject* gpy_getname(PyObject *self, PyObject *args, PyObject *kwds) {
   int       varnum, length, varlevel;
   char      varname[SICVLN];
   PyObject* tuple;

   static char *kwlist[] = {"num",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &varnum))
      return NULL;

   /* Get name and its length */
   sic_getname(&varnum,&varname[0],&length,&varlevel);

   tuple = PyTuple_New(2);
#if PY_MAJOR_VERSION >= 3
   varname[length] = '\0';
   PyTuple_SetItem(tuple, 0, PyUnicode_FromString(varname));
#else
   PyTuple_SetItem(tuple, 0, PyString_FromStringAndSize(varname,length));
#endif
   PyTuple_SetItem(tuple, 1, PyLong_FromLong((long) varlevel));

   return tuple; /* New reference */
}

/**
 * Return the address of the variable descriptor.
 * Python inputs are the variable name and its execution level.
 *
 * @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 descriptor address as a PyInt.
 */
PyObject* gpy_descptr(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level;
   address_t *address;

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&address,strlen(varname));

   if ( (long) address == -1 )
      fprintf(stderr,"Error in 'gpy_descptr': %s not found at level %d\n",varname,level);

   return PyLong_FromLong((long) address); /* New reference */
}

/**
 * Return the address of the variable data.
 * Python inputs are the variable name and its execution level.
 *
 * @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 data address as a PyInt.
 */
PyObject* gpy_dataptr(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level;
   address_t *desc, address=NULL;

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   if ( (long) desc == -1 ) {
      fprintf(stderr,"Error in 'gpy_dataptr': %s not found at level %d\n",varname,level);
   } else {
      address = desc[1];
   }

   return PyLong_FromLong((long) address); /* New reference */
}

/**
 * Check if input variable exists.
 * Python inputs are the variable name and its execution level.
 *
 * @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 existence flag as a PyBool.
 */
PyObject* gpy_varexist(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level;
   address_t *desc; /* Descriptor address */

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   if ( (long) desc == -1 ) {
      Py_RETURN_FALSE;
   } else {
      Py_RETURN_TRUE;
   }

}

/**
 * Return type code of a variable.
 * Python inputs are the variable name and its execution level.
 *
 * @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 type code as a PyInt.
 */
PyObject* gpy_vartype(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level, vartyp=-999;
   address_t *desc; /* Descriptor address */

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   if ( (long) desc == -1 ) {
      fprintf(stderr,"Error in 'gpy_vartype': %s not found at level %d\n",varname,level);
   } else {
      vartyp = (long) desc[0];
   }

   return PyLong_FromLong((long) vartyp); /* New reference */
}

/**
 * Return the dimensions (number and values) of a variable.
 * Python inputs are the variable name and its execution level.
 *
 * @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 dimensions (number and values) in a PyTuple.
 */
PyObject* gpy_vardims(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level;
   address_t *desc; /* Descriptor address */
   PyObject  *tuple;

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   tuple = PyTuple_New(1+SIC_MAXDIMS);

   /* This code assumes SIC_MAXDIMS == 7 */
   if ( (long) desc != -1 ) {
      PyTuple_SetItem(tuple, 0, Py_BuildValue("i", (int) (long) desc[5]));
      PyTuple_SetItem(tuple, 1, Py_BuildValue("i", (int) (long) desc[6]));
      PyTuple_SetItem(tuple, 2, Py_BuildValue("i", (int) (long) desc[7]));
      PyTuple_SetItem(tuple, 3, Py_BuildValue("i", (int) (long) desc[8]));
      PyTuple_SetItem(tuple, 4, Py_BuildValue("i", (int) (long) desc[9]));
      PyTuple_SetItem(tuple, 5, Py_BuildValue("i", (int) (long) desc[10]));
      PyTuple_SetItem(tuple, 6, Py_BuildValue("i", (int) (long) desc[11]));
      PyTuple_SetItem(tuple, 7, Py_BuildValue("i", (int) (long) desc[12]));
   } else {
      fprintf(stderr,"Error in 'gpy_vardims': %s not found at level %d\n",varname,level);
      PyTuple_SetItem(tuple, 0, Py_BuildValue("i", -1));
      PyTuple_SetItem(tuple, 1, Py_BuildValue("i",  0));
      PyTuple_SetItem(tuple, 2, Py_BuildValue("i",  0));
      PyTuple_SetItem(tuple, 3, Py_BuildValue("i",  0));
      PyTuple_SetItem(tuple, 4, Py_BuildValue("i",  0));
      PyTuple_SetItem(tuple, 5, Py_BuildValue("i",  0));
      PyTuple_SetItem(tuple, 6, Py_BuildValue("i",  0));
      PyTuple_SetItem(tuple, 7, Py_BuildValue("i",  0));
   }

   return tuple; /* New reference */
}

/**
 * Return the read-only (1) or read-and-write (0) status of a variable.
 * Python inputs are the variable name and its execution level.
 *
 * @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 0 or 1 for the read-only status, -1 on failure, as a PyInt.
 */
PyObject* gpy_isro(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level, isro=-1;
   address_t *desc; /* Descriptor address */

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   if ( (long) desc == -1 ) {
      fprintf(stderr,"Error in 'gpy_isro': %s not found at level %d\n",varname,level);
   } else {
      isro = (long) desc[4];
   }

   return PyLong_FromLong((long) isro); /* New reference */
}

/**
 * Return 1 or 0 if input variable is an image or not.
 * Python inputs are the name and execution level of the variable.
 *
 * @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 1 or 0, as a PyInt, if input variable is an image or not.
 */
PyObject* gpy_isimage(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level, isimage=0;
   address_t *desc; /* Descriptor address */

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   if ( (long) desc == -1 ) {
      fprintf(stderr,"Error in 'gpy_isimage': %s not found at level %d\n",varname,level);
   } else {
      if ( (long) desc[3] > 0 )  /* status > 0 */
         isimage = 1;
   }

   return PyLong_FromLong((long) isimage); /* New reference */
}

/**
 * Return 1 or 0 if input variable is an alias or not.
 * Python inputs are the name and execution level of the variable.
 *
 * @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 1 or 0, as a PyInt, if input variable is an alias or not.
 */
PyObject* gpy_isalias(PyObject *self, PyObject *args, PyObject *kwds) {
   char      *varname;
   int        level, isalias=0;
   address_t *desc; /* Descriptor address */

   static char *kwlist[] = {"name","level",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &varname, &level))
      return NULL;

   sic_descptr(varname,&level,&desc,strlen(varname));

   if ( (long) desc == -1 ) {
      fprintf(stderr,"Error in 'gpy_dataptr': %s not found at level %d\n",varname,level);
   } else {
      if ( (long) desc[3] == ALIAS_DEFINED )
         isalias = 1;
   }

   return PyLong_FromLong((long) isalias); /* New reference */
}

/**
 * Define a standard variable in SIC.
 * Python inputs are the type code, the name, and the global-or-not
 * status of the variable. 'sic_defvariable' is the internal Fortran
 * routine called by SIC when user creates a variable.
 *
 * @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 error code (0 on success, 1 on failure) as a PyInt.
 */
PyObject* gpy_defvar(PyObject *self, PyObject *args, PyObject *kwds) {
   char *vname;
   int   length, vtype, visglob, error;

   static char *kwlist[] = {"type","name","isglobal",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "isi", kwlist, &vtype, &vname, &visglob))
      return NULL;

   length = strlen(vname);
   sic_defvariable(&vtype,vname,&visglob,&error,length);

   return PyLong_FromLong((long) error); /* New reference */
}

/**
 * Define a structure in SIC.
 * Python inputs are the name and the global-or-not status of the
 * variable. 'sic_defstructure' is the internal Fortran routine 
 * called by SIC when user creates a structure.
 *
 * @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 error code (0 on success, 1 on failure) as a PyInt.
 */
PyObject* gpy_defstruct(PyObject *self, PyObject *args, PyObject *kwds) {
   char *vname;
   int   length, visglob, error;

   static char *kwlist[] = {"name","isglobal",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, &vname, &visglob))
      return NULL;

   length = strlen(vname);
   sic_crestructure(vname,&visglob,&error,length);

   return PyLong_FromLong((long) error); /* New reference */
}

/**
 * Delete a variable in SIC.
 * Python inputs are the name and the read-only (or not) status of the
 * variable. 'sic_delvariable' is the internal Fortran routine called by
 * SIC when a variable is deleted.
 *
 * @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 error code (0 on success, 1 on failure) as a PyInt.
 */
PyObject* gpy_delvar(PyObject *self, PyObject *args, PyObject *kwds) {
   char *vname;
   int   user, error;

   static char *kwlist[] = {"name",NULL};
   if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &vname))
      return NULL;

   error = 0;
   user = 1;  /* User request */
   sic_delvariable(vname,&user,&error,strlen(vname));

   return PyLong_FromLong((long) error); /* New reference */
}

/**
 * Return the current execution level of SIC.
 * The Fortran routine 'sic_varlevel()' returns the value 'VAR_LEVEL'
 *
 * @return the execution level as a PyInt.
 */
PyObject* gpy_varlevel(void) {
   return PyLong_FromLong((long) sic_varlevel()); /* New reference */
}

/**
 * Return the dictionary of the Python __main__.
 *
 * @return __main__ dictionary as a PyDict, or Py_None on failure.
 */
PyObject* gpy_maindict(void) {
   PyObject *module;

   if (! (module = PyImport_AddModule("__main__"))) {
      fprintf(stderr," E-PYTHON, Failed to load Python __main__\n");
      PyErr_Print();
      Py_RETURN_NONE;
   }

   return PyModule_GetDict(module); /* Borrowed reference */
}
