/*
test8.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test8.so -fPIC test8.c

Il faut indiquer une fois pour toute où se trouve la librairie "Python.h"
Pour ajouter des répertoires de "include" (.h) :
C_INCLUDE_PATH="/usr/include/python3.5"
export C_INCLUDE_PATH

Depuis Python, exécuter le script test8.py qui est donné dans un autre fichier.
*/

#include "Python.h"
#include <string.h>
// c.f. https://www.tutorialspoint.com/c_standard_library/string_h.htm

static PyObject* test8_code(PyObject *self, PyObject *args) {
//===========================================================
// Définit la fonction en C qui sera appelée depuis Python
// Reçoit une chaîne de caractères et retourne une chaîne de
// caractères qui indique le code utf-8 de chaque caractère.
char *pstrS;
char astrS[50];
char astrS2[200];
char astrTemp[10];
int nLen;
int nn;

if (!PyArg_ParseTuple(args, "s", &pstrS)) return NULL;
   // c.f https://docs.python.org/2.0/ext/parseTuple.html
   // c.f. https://docs.python.org/3.3/c-api/arg.html#PyArg_ParseTuple

strcpy(astrS, pstrS); // Copie la chaîne reçue en entrée
nLen = strlen(astrS);  // retourne la longueur de la chaine de caractères
    // Plus exactement, le nombre d'octets qui code cette chaîne de caractères

// Recopie la chaîne de départ
sprintf(astrS2, "%s : ",astrS); 

// Ajoute à la chaîne astr2, les codes utf-8 des caractères.
for (nn=0; nn<nLen; nn++) {
  // Pour les formats possibles, c.f. 
  // https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm
  sprintf(astrTemp, "%x ", (unsigned char)astrS[nn]);
  strcat(astrS2, astrTemp);
  }

//return PyUnicode_FromFormat("chaine= %s", astrS2); 
return PyUnicode_FromString(astrS2); 
} // test8_code

static PyObject* test8_inverse(PyObject *self, PyObject *args) {
//===========================================================
// Définit la fonction en C qui sera appelée depuis Python
// Reçoit une liste de nombres entiers, 
// retourne leur somme.
PyObject *py_list; // Pointeur sur un objet Python, qui est une "list"
PyObject *py_int; // Pointeur sur un objet Python, qui est un "int" ou "long"
PyObject *py_newlist; // Pointeur sur un objet Python, qui est une "list"
long anTable[100];
long nLen = 77;
int nn;
long nSum;

if (!PyArg_ParseTuple(args, "O", &py_list)) return NULL;
// &py_list  est un pointeur sur "py_list",
// qui lui-même est un pointeur sur un objet Python.
// Ceci permet à la fonction de modifier la valeur du pointeur py_list
// Il n'y a qu'un seul objet dans le "Tuple",
// qui est un objet "liste"

nLen = PyList_Size(py_list); // Longueur de la liste
// On supposera, sans le tester, que c'est une liste d'entiers.
// anTable = malloc(nLen*sizeof(nLen)); // Si on veut allouer dynamiquement de la mémoire.

nSum = 0;
nn = nLen;
while (nn--) {
  py_int = PyList_GetItem(py_list, nn); // Pointeur sur un objet "int" de Python
  anTable[nn] = PyLong_AsLong(py_int); // transforme l'objet "int" en langage C
  // Information sur les lists :
  // https://docs.python.org/3.3/c-api/list.html
  nSum += anTable[nn];
  }

// Création d'un nouvel objet "list" de Python
// c.f. :   // https://docs.python.org/3.3/c-api/list.html
py_newlist = PyList_New(0); // Le paramètre est la taille initiale de la liste

// Inverse la liste des nombres.
for (nn=nLen-1; nn>=0; nn--) {
  PyList_Append(py_newlist, PyLong_FromLong(anTable[nn]));
  }

// Ajoute la somme à la fin
PyList_Append(py_newlist, PyLong_FromLong(nSum));  
// Pour : PyObject* PyLong_FromLong(long v)
// c.f. https://docs.python.org/3.3/c-api/long.html
// Existe aussi : PyObject* PyFloat_FromDouble(double v)
// c.f. https://docs.python.org/3.3/c-api/float.html

//return PyFloat_FromDouble(nSum*1.0); // Si on veut retourner un Float
//return PyLong_FromLong(nSum);
// https://docs.python.org/3.3/c-api/long.html?highlight=pylong_aslong#PyLong_AsLong
//return py_long; // Si on veut tester le type de cet objet Python.
return py_newlist;
} // test8_inverse

// Définit l'aide associée à la fonction.
// Obtenu avec : print(test8.compte.__doc__)
#define AIDE_code "code(string) retourne une chaîne de caractères qui contient\n\
les codes utf-8 des caractères du string donnée en argument."

#define AIDE_inverse "inverse(une liste) reçoit une liste de nombres entiers\n\
et retourne la liste dans l'ordre inverse, suivit de la somme des entiers."

static PyMethodDef test8Methods[] = {
//===================================
{"code",  test8_code, METH_VARARGS, AIDE_code},
{"inverse",  test8_inverse, METH_VARARGS, AIDE_inverse},
{NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test8module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test8",   /* name of module */
  NULL,  //test8_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test8Methods
};

PyMODINIT_FUNC PyInit_test8(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test8module);
} // PyInit_test8

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test8", PyInit_test8);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test8");

PyMem_RawFree(program);
return 0;
} // main
