/*
 * OpenTurns wrapper code generated by OpenModelica
 * for model: <%fullModelName%>
 */
/**
 *  @file  wrapper.c
 *  @brief The wrapper adapts the interface of OpenTURNS and of the wrapped code
 *
 */

#include "Wrapper.h"

#define WRAPPERNAME <%wrapperName%>
#define MODELNAMESTR "<%fullModelName%>"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#define DEBUG      1
#define MAX_BUFFER 8000

long callOpenModelicaModel(STATE p_state, INPOINT inPoint, OUTPOINT outPoint, EXCHANGEDDATA p_exchangedData, ERROR p_error)
{
    int idx = 0;
    long exitCode = 0;
    char systemCommand[MAX_BUFFER] = {0}, buf[MAX_BUFFER] = {0};
    char *variableName = NULL;
    unsigned long variableType = 0;
    /* read the model name from the included model_name.h file */
    char *modelName = MODELNAMESTR;
    char *errorMsg = 0;
    double variableValue = 0, stopTime = 0;
    struct WrapperVariableList *varLst = p_exchangedData->variableList_;
    char *currentWorkingDirectory = 0;
    char *prefix = MODELNAMESTR;
    char *tmpStr = NULL, *strToken = NULL;
    FILE *resultOutput = NULL;

    /* We save the current working directory for a future come back */
    currentWorkingDirectory = getCurrentWorkingDirectory( p_error );
    if (currentWorkingDirectory == NULL) return WRAPPER_EXECUTION_ERROR;

    if (varLst->variable_ == NULL)
    {
        SETERROR("The input variables structure is NULL");
        return WRAPPER_EXECUTION_ERROR;
    }

    // make it empty!
    systemCommand[0] = '\0';
    /* move to start */
    idx = 0;
    varLst = p_exchangedData->variableList_;
    while (varLst != NULL)
    {
      variableName = varLst->variable_->id_;
      variableType = varLst->variable_->type_;
      /* filter the output variables */
      if (variableType == 0)
      {
        variableValue = inPoint->data_[idx];
        /* construct the override string */
        tmpStr = strdup(systemCommand);
        sprintf(systemCommand, "%s%s=%.20g,",tmpStr,variableName,variableValue);
        free(tmpStr);
        idx++;
      }
      /* move to next */
      varLst = varLst->next_;
    }
    /* remove the last comma */
    if (systemCommand[strlen(systemCommand)-1] == ',')
    {
      systemCommand[strlen(systemCommand)-1] = '\0';
    }
    /* add stepSize=1
    tmpStr = strdup(systemCommand);
    sprintf(systemCommand, "%sstepSize=1.0",tmpStr);
    free(tmpStr);
    */

    /* build the command */
    tmpStr = strdup(systemCommand);
    sprintf(systemCommand, "\"%s/%s\" -noemit -override %s -output ", currentWorkingDirectory, MODELNAMESTR, tmpStr);
    free(tmpStr);

    idx = 0;
    varLst = p_exchangedData->variableList_;
    /* generate the -output string */
    while (varLst)
    {
      variableName = varLst->variable_->id_;
      variableType = varLst->variable_->type_;
      /* filter the output variables */
      if (variableType == 1)
      {
        tmpStr = strdup(systemCommand);
        sprintf(systemCommand, "%s%s,", tmpStr, variableName);
        free(tmpStr);
        idx++;
      }
      /* move to next */
      varLst = varLst->next_;
    }
    /* remove the last comma */
    if (systemCommand[strlen(systemCommand)-1] == ',')
    {
      systemCommand[strlen(systemCommand)-1] = '\0';
    }

    /* popen the command to a file */
    resultOutput = popen(systemCommand, "r");
    if (!resultOutput)
    {
      SETERROR( "Error executing or opening the output stream of the Modelica model, command %s", systemCommand );
      return WRAPPER_EXECUTION_ERROR;
    }

    if (!fgets(buf, MAX_BUFFER, resultOutput))
    {
      SETERROR( "Error reading the output stream of the simulation process %s", systemCommand );
      return WRAPPER_EXECUTION_ERROR;
    }
    /* remove newline */
    if (buf[strlen(buf)-1] == '\n')
    {
        buf[strlen(buf)-1] = '\0';
    }

    if (pclose(resultOutput) != 0)
    {
      SETERROR( "Error closing the simulation process output stream %s", systemCommand );
      return WRAPPER_EXECUTION_ERROR;
    }

    idx = 0;
    strToken = strtok(strdup(buf), ",");
    /* ignore first (time) */
    strToken = strtok(NULL, ",");
    while (strToken)
    {
      outPoint->data_[idx] = atof(strchr(strToken, '=')+1);
      strToken = strtok(NULL, ",");
      idx++;
    }

    if (DEBUG)
    {
      /* CantiLever example result checking
      double F, E, L, I, y;
      E = inPoint->data_[0];
      F = inPoint->data_[1];
      L = inPoint->data_[2];
      I = inPoint->data_[3];
      y = (F*L*L*L)/(3.0*E*I);
      fprintf(stderr, "%s\n\t -> %s\n\t ->          %.20g !=\n\t ->          %.20g\n", systemCommand, buf, outPoint->data_[0], y); fflush(NULL);
      */
      fprintf(stderr, "%s\n\t -> %s\n\t ->          %.20g\n", systemCommand, buf, outPoint->data_[0]); fflush(NULL);
    }

    free ( currentWorkingDirectory );

    return exitCode;
}

BEGIN_C_DECLS
WRAPPER_BEGIN

/*
 *  This is the declaration of function named 'myWrapper' into the wrapper.
 */

/*
*********************************************************************************
*                                                                               *
*                             myWrapper function                                *
*                                                                               *
*********************************************************************************
*/

  /* The wrapper information informs the NumericalMathFunction object that loads the wrapper of the
   * signatures of the wrapper functions. In particular, it hold the size of the input
   * NumericalPoint (inSize_) and of the output NumericalPoint (outSize_).
   * Those information are also used by the gradient and hessian functions to set the correct size
   * of the returned matrix and tensor.
   */

  /* The getInfo function is optional. Except if you alter the description of the wrapper, you'd better
   * use the standard one automatically provided by the platform. Uncomment the following definition if
   * you want to provide yours instead. */
  /* FUNC_INFO( WRAPPERNAME , { } ) */

  /* The state creation/deletion functions allow the wrapper to create or delete a memory location
   * that it will manage itself. It can save in this location any information it needs. The OpenTURNS
   * platform only ensures that the wrapper will receive the state (= the memory location) it works
   * with. If many wrappers are working simultaneously or if the same wrapper is called concurrently,
   * this mechanism will avoid any collision or confusion.
   * The consequence is that NO STATIC DATA should be used in the wrapper OR THE WRAPPER WILL BREAKE
   * one day. You may think that you can't do without static data, but in general this is the case
   * of a poor design. But if you persist to use static data, do your work correctly and make use
   * of mutex (for instance) to protect your data against concurrent access. But don't complain about
   * difficulties or poor computational performance!
   */


  /* The createState function is optional. If you need to manage an internal state, uncomment the following
   * definitions and adapt the source code to your needs. By default Open TURNS provides default ones. */
  /* FUNC_CREATESTATE( WRAPPERNAME , {
     CHECK_WRAPPER_MODE( WRAPPER_STATICLINK );
     CHECK_WRAPPER_IN(   WRAPPER_ARGUMENTS  );
     CHECK_WRAPPER_OUT(  WRAPPER_ARGUMENTS  );

     COPY_EXCHANGED_DATA_TO( p_p_state );

     PRINT( "My message is here" );
     } ) */

  /* The deleteState function is optional. See FUNC_CREATESTATE for explanation. */
  /* FUNC_DELETESTATE( WRAPPERNAME , {
     DELETE_EXCHANGED_DATA_FROM( p_state );
     } ) */

  /* Any function declared into the wrapper may declare three actual functions prefixed with
   * 'init_', 'exec_' and 'finalize_' followed by the name of the function, here 'myWrapper'.
   *
   * The 'init_' function is only called once when the NumericalMathFunction object is created.
   * It allows the wrapper to set some internal state, read some external file, prepare the function
   * to run, etc.
   *
   * The 'exec_' function is intended to execute what the wrapper is done for: compute an mathematical
   * function or anything else. It takes the internal state pointer as its first argument, the input
   * NumericalPoint pointer as the second and the output NumericalPoint pointer as the third.
   *
   * The 'finalize_' function is only called once when the NumericalMathFunction object is destroyed.
   * It allows the wrapper to flush anything before unloading.
   *
   * Only the 'exec_' function is mandatory because the other ones are automatically provided by the platform.
   */


  /**
   * Initialization function
   * This function is called once just before the wrapper first called to initialize
   * it, ie create a temparary subdirectory (remember that the wrapper may be called
   * concurrently), store exchanged data in some internal repository, do some
   * pre-computational operation, etc. Uncomment the following definition if you want to
   * do some pre-computation work.
   */
   FUNC_INIT( WRAPPERNAME , {
       //struct WrapperVariableList   *varLst = p_exchangedData->variableList_;
       //fprintf(stderr, "CRAP_: %p\n", varLst->variable_); fflush(NULL);
   } )


  /**
   * Execution function
   * This function is called by the platform to do the real work of the wrapper. It may be
   * called concurrently, so be aware of not using shared or global data not protected by
   * a critical section.
   * This function has a mathematical meaning. It operates on one vector (aka point) and
   * returns another vector.
   *
   * This definition is MANDATORY.
   */
  FUNC_EXEC( WRAPPERNAME ,
    {
      long rc = callOpenModelicaModel(p_state, inPoint, outPoint, p_exchangedData, p_error);
      if (rc)
      {
        PRINT( "Error in calling the OpenModelica simulation code" );
        return WRAPPER_EXECUTION_ERROR;
      }
    } )

  /**
   * Finalization function
   * This function is called once just before the wrapper is unloaded. It is the place to flush
   * any output file or free any allocated memory. When this function returns, the wrapper is supposed
   * to have all its work done, so it is not possible to get anymore information from it after that.
   * Uncomment the following definition if you need to do some post-computation work. See FUNC_INIT. */
  /* FUNC_FINALIZE( WRAPPERNAME , {} ) */


WRAPPER_END
END_C_DECLS

