/*                             syscalls_ansi.c
                              -----------------
*/

/* Date:   19-Mar-2010
   Author: Bourdin.KIS (Bourdin@KIS.Uni-Freiburg.de)
   Description:
 ANSI C and standard library callable function wrappers for use in Fortran.
 Written to compensate for inadequatenesses in the Fortran95/2003 standards.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>

#include "headers_c.h"
#include <stdbool.h>

/* ---------------------------------------------------------------------- */
void FTNIZE(extract_string_c)(char *extract_cmd, char *result, const FINT *size)
{
//  Extracts a string (e.g., from a file) by extract_cmd.
//  31-mar-21/MR: coded

  FILE *pipe = popen(extract_cmd, "r");
  if (pipe == NULL) {
    perror("popen");
    exit(EXIT_FAILURE);
  }
  char* ret=fgets(result, *size, pipe);
  pclose(pipe);
}
/* ---------------------------------------------------------------------- */

int FTNIZE(readlink_c) (const char *linkfile, char *link, const FINT *size)
{
//  Returns the contents of a symbolic link.
//  31-mar-21/MR: coded

  int ret;

  ret=readlink(linkfile,link,*size);
  return ret;
}
/* ---------------------------------------------------------------------- */

int FTNIZE(islink_c) (char *filename)
{
//  Tests whether filename is a symbolic link.
//  21-mar-20/MR: coded

  struct stat fileStat;
  int ret = -1;

  if (lstat(filename, &fileStat) == 0) { 
    ret = (fileStat.st_mode & S_IFMT) == S_IFLNK ? 1 : 0;
  }
  return ret;
}
/* ---------------------------------------------------------------------- */

void FTNIZE(file_size_c)
     (char *filename, FINT *bytes)
/* Determines the size of a file.
   Returns:
   * positive integer containing the file size of a given file
   * -2 if the file could not be found or opened
   * -1 if retrieving the file size failed
*/
{
  struct stat fileStat;
  int file = -1;

  *bytes = -2;
  file = open (filename, O_RDONLY);
  if (file == -1) return;

  *bytes = -1;
  if (fstat (file, &fileStat) < 0) { close (file); return; }
  close (file);

  *bytes = fileStat.st_size;
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller1)
     (void (**func)(void*), void* par1)
{
  (*func)(par1);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller2)
     (void (**func)(void*,void*), void* par1, void* par2)
{
  (*func)(par1,par2);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller2_str1)
     (void (**func)(char*,int*,void*), char* str, int len, void* par1)
{
  (*func)(str,&len,par1);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller3)
     (void (**func)(void*,void*,void*), void* par1, void* par2, void* par3)
{
  (*func)(par1,par2,par3);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller1_str)
     (void (**func)(char*,int*), char* str, int len)
{
  (*func)(str,&len);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller3_str1)
     (void (**func)(char*,int*,void*,void*), char* str, int len, void* par2, void* par3)
{
  (*func)(str,&len,par2,par3);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller4)
     (void (**func)(void*,void*,void*,void*), void* par1, void* par2, void* par3, void* par4)
{
  (*func)(par1,par2,par3,par4);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller4_str1)
     (void (**func)(char*,int*,void*,void*,void*), char* str, int len, void* par2, void* par3, void* par4)
{
  (*func)(str,&len,par2,par3,par4);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller5)
     (void (**func)(void*,void*,void*,void*,void*), void* par1, void* par2, void* par3, void* par4, void* par5)
{
  (*func)(par1,par2,par3,par4,par5);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller5_str5)
     (void (**func)(void*,void*,void*,void*,char*,int*), 
      void* par1, void* par2, void* par3, void* par4, char* str, int len)
{
  (*func)(par1,par2,par3,par4,str,&len);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller6)
     (void (**func)(void*,void*,void*,void*,void*,void*), void* par1, void* par2, void* par3, void* par4, void* par5, void* par6)
{
  (*func)(par1,par2,par3,par4,par5,par6);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller7_str67)
     (void (**func)(void*,void*,void*,void*,void*,char*,int*,char*,int*), 
      void* par1, void* par2, void* par3, void* par4, void* par5, char* str1, int len1, char* str2, int len2)
{
  (*func)(par1,par2,par3,par4,par5,str1,&len1,str2,&len2);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller)
     (void (**func)(void*, ... ), FINT* npar, ... )
{
  va_list ap;
  va_start(ap, npar);

  switch(*npar)
  {
    case 1: (*func)(va_arg(ap,void*)); break;
    case 2: (*func)(va_arg(ap,void*),va_arg(ap,void*)); break;
    //case 2: printf("f,p: %p %p \n",va_arg(ap,void*),va_arg(ap,void*)); break;
    case 3: (*func)(va_arg(ap,void*),va_arg(ap,void*),va_arg(ap,void*)); break;
    case 4: (*func)(va_arg(ap,void*),va_arg(ap,void*),va_arg(ap,void*),va_arg(ap,void*)); break;
    case 5: (*func)(va_arg(ap,void*),va_arg(ap,void*),va_arg(ap,void*),va_arg(ap,void*),va_arg(ap,void*)); break;
    default: return;
  }
  va_end(ap);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(caller0)
     (void (**func)(void))
{
  (*func)(); 
}
/* ---------------------------------------------------------------------- */
int FTNIZE(func_int_caller0)
     (int (**func)(void))
{
  return (*func)(); 
}
/* ---------------------------------------------------------------------- */
void *FTNIZE(dlopen_c)(const char *filename, FINT *flag)
{
  const int ALLNOW=1;
  void *pointer;
  char *error; 

  dlerror();
  if (*flag==ALLNOW) {
    pointer=dlopen(filename, RTLD_GLOBAL|RTLD_NOW); 
  } else {
    pointer=dlopen(filename, RTLD_GLOBAL|RTLD_LAZY); 
  }

  error=dlerror();
  if (error!=NULL) {
    printf("Error - %s\n", error);
    return NULL;
  }

  return pointer;
}
/* ---------------------------------------------------------------------- */
void *FTNIZE(dlsym_c)(void **handle, const char *symbol)
{
  void *pointer;
  char *error; 

//printf("symbol address= %p\n", dlsym(*handle, symbol));
  dlerror();
  pointer=dlsym(*handle, symbol); 

  error=dlerror();
  if (error!=NULL) {
   printf("Error - %s\n", error);
   return NULL;
  }

  return pointer;
}
/* ---------------------------------------------------------------------- */
void FTNIZE(dlclose_c)(void **handle)
{
  dlclose(*handle);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(dlerror_c)(void)
{
  char *error=dlerror();
  printf("error = %s\n", error);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(write_binary_file_c)
     (char *filename, FINT *bytes, char *buffer, FINT *result)
/* Writes a given buffer to a binary file.
   Returns:
   * positive integer containing the number of written bytes
   * -2 if the file could not be opened
   * -1 if writing the buffer failed
*/
{
  int file = -1;
  int written = 0;

  *result = -2;
  file = open (filename, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);

  if (file == -1) return;

  *result = -1;

  written = (int) write (file, buffer, (size_t) *bytes);
  close (file);
  if (written != *bytes) return;
  *result = written;
}

/* ---------------------------------------------------------------------- */

void FTNIZE(get_pid_c)
     (FINT *pid)
/* Determines the PID of the current process.
   Returns:
   * integer containing the PID of the current process
   * -1 if retrieving the PID failed
*/
{
  pid_t result;

  *pid = -1;
  result = getpid ();
  if (result) *pid = (int) result;
}

/* ---------------------------------------------------------------------- */

void FTNIZE(directory_exists_c)
     (char *path, FINT *exists)
/* Checks for existence of a directory.
   Returns:
   * 1, if 'path' points to a directory
   * -1, on error
   * 0, otherwise
*/
{
  int status;
  struct stat result;

  *exists = 0;
  status = stat (path, &result);
  if (status == -1) *exists = -1;
  if (S_ISDIR (result.st_mode)) *exists = 1;
}

/* ---------------------------------------------------------------------- */

void FTNIZE(is_nan_c)
     (REAL *value, FINT *result)
/* Determine if value is not a number.
   Returns:
   * not 0, if value is NaN
   * 0, otherwise
*/
{
  // C99 standard uses a macro that is independent of type
  *result = isnan (*value);
}

/* ---------------------------------------------------------------------- */

void FTNIZE(system_c) (char *command)
/* Date:   04-Nov-2011
   Author: MR (matthias.rheinhardt@helsinki.fi)
   Description: function wrapper for ANSI C function system.
*/
{
  int res=system(command);
  if (res == -1) return; // some error handling is missing here [Bourdin.KIS]
}

/* ---------------------------------------------------------------------- */

void FTNIZE(sizeof_real_c)
     (REAL *value, FINT *result)
/* Determine the number of bytes used for value.
   Returns:
   * the number of bytes used for value
*/
{
  *result = sizeof (*value);
}
/* ---------------------------------------------------------------------- */
void FTNIZE(copy_addr_c)(void *src, void **dest)
{
  *dest=src;
}
/* ---------------------------------------------------------------------- */
void FTNIZE(copy_addr_c_bool)(int *src, void **dest, int* n)
{
  bool* res = malloc(sizeof(bool)*(*n));
  for(int i = 0; i < (*n); ++i) res[i] = (src[i] > 0);
  *dest=(void*)res;
}
/* ---------------------------------------------------------------------- */
FINT FTNIZE(mem_usage_c)()
{
  #include <sys/resource.h>

  struct rusage usage;
  int res=getrusage(RUSAGE_SELF,&usage);

  return usage.ru_maxrss;
}
/* ---------------------------------------------------------------------- */
void FTNIZE(py_init)()
{
#ifdef PYTHONPATH
  #include <Python.h>
  Py_Initialize();
#endif
}
/* ---------------------------------------------------------------------- */
void FTNIZE(py_initialize)(const char *pymodule,const char *pyfunction,void *pModule,void *pFunction)
{
#ifdef PYTHONPATH
  #include <Python.h>

  // Import the Python module
  pModule = (void*) PyImport_ImportModule(pymodule);
  if (pModule != NULL) {
    // Get a reference to the Python function
    pFunction = (void*) PyObject_GetAttrString((PyObject*) pModule,pyfunction);
  } else {
    PyErr_Print();
  }
#endif
}
/* ---------------------------------------------------------------------- */
void FTNIZE(py_call)(void *pFunction, int *arg)
{
#ifdef PYTHONPATH
  #include <Python.h>

  // Call the Python function with the provided file path
  PyObject *pArgs = NULL;  //PyTuple_Pack(1, Py_BuildValue("s", file_path));
  PyObject_CallObject((PyObject *) pFunction, pArgs);
#endif
}
/* ---------------------------------------------------------------------- */
void FTNIZE(py_finalize)(void *pModule,void *pFunction)
{
#ifdef PYTHONPATH
  #include <Python.h>

  if (pFunction != NULL) Py_XDECREF((PyObject*) pFunction);
  if (pModule != NULL) Py_XDECREF((PyObject*) pModule);

  Py_Finalize();
#endif
}
/* ---------------------------------------------------------------------- */
REAL* FTNIZE(pos_real_ptr_c)(REAL* ptr,int ind)
{
  return ptr+ind;
}
/* ---------------------------------------------------------------------- */
