/*
 * This file is part of OpenModelica.
 *
 * Copyright (c) 1998-CurrentYear, Open Source Modelica Consortium (OSMC),
 * c/o Linköpings universitet, Department of Computer and Information Science,
 * SE-58183 Linköping, Sweden.
 *
 * All rights reserved.
 *
 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR
 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2.
 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE
 * OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
 *
 * The OpenModelica software and the Open Source Modelica
 * Consortium (OSMC) Public License (OSMC-PL) are obtained
 * from OSMC, either from the above address,
 * from the URLs: http://www.ida.liu.se/projects/OpenModelica or
 * http://www.openmodelica.org, and in the OpenModelica distribution.
 * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without
 * even the implied warranty of  MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
 *
 * See the full OSMC Public License conditions for more details.
 *
 */

#ifdef __cplusplus
extern "C" {
#endif

#include "FMICommon.h"
#include "FMI1Common.h"

void* FMI1ModelExchangeConstructor_OMC(int fmi_log_level, char* working_directory, char* instanceName, int debugLogging)
{
  FMI1ModelExchange* FMI1ME = malloc(sizeof(FMI1ModelExchange));
  FMI1ME->FMILogLevel = fmi_log_level;
  /* JM callbacks */
  FMI1ME->JMCallbacks.malloc = malloc;
  FMI1ME->JMCallbacks.calloc = calloc;
  FMI1ME->JMCallbacks.realloc = realloc;
  FMI1ME->JMCallbacks.free = free;
  FMI1ME->JMCallbacks.logger = importlogger;
  FMI1ME->JMCallbacks.log_level = FMI1ME->FMILogLevel;
  FMI1ME->JMCallbacks.context = 0;
  FMI1ME->FMIImportContext = fmi_import_allocate_context(&FMI1ME->JMCallbacks);
  /* FMI callback functions */
  FMI1ME->FMICallbackFunctions.logger = fmi1logger;
  FMI1ME->FMICallbackFunctions.allocateMemory = calloc;
  FMI1ME->FMICallbackFunctions.freeMemory = free;
  /* parse the xml file */
  FMI1ME->FMIWorkingDirectory = (char*) malloc(strlen(working_directory)+1);
  strcpy(FMI1ME->FMIWorkingDirectory, working_directory);
  FMI1ME->FMIImportInstance = fmi1_import_parse_xml(FMI1ME->FMIImportContext, FMI1ME->FMIWorkingDirectory);
  if(!FMI1ME->FMIImportInstance) {
    fprintf(stderr, "Error parsing the XML file contained in %s\n", FMI1ME->FMIWorkingDirectory);
    return 0;
  }
  /* Load the binary (dll/so) */
  jm_status_enu_t status;
  status = fmi1_import_create_dllfmu(FMI1ME->FMIImportInstance, FMI1ME->FMICallbackFunctions, 0);
  if (status == jm_status_error) {
    fprintf(stderr, "Could not create the DLL loading mechanism(C-API).\n");
    return 0;
  }
  FMI1ME->FMIInstanceName = (char*) malloc(strlen(instanceName)+1);
  strcpy(FMI1ME->FMIInstanceName, instanceName);
  FMI1ME->FMIDebugLogging = debugLogging;
  fmi1_import_instantiate_model(FMI1ME->FMIImportInstance, FMI1ME->FMIInstanceName);
  fmi1_import_set_debug_logging(FMI1ME->FMIImportInstance, FMI1ME->FMIDebugLogging);
  FMI1ME->FMIToleranceControlled = fmi1_true;
  FMI1ME->FMIRelativeTolerance = 0.001;
  FMI1ME->FMIEventInfo = malloc(sizeof(fmi1_event_info_t));
  fmi1_import_initialize(FMI1ME->FMIImportInstance, FMI1ME->FMIToleranceControlled, FMI1ME->FMIRelativeTolerance, FMI1ME->FMIEventInfo);
  return FMI1ME;
}

void FMI1ModelExchangeDestructor_OMC(void* in_fmi1me)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_terminate(FMI1ME->FMIImportInstance);
  fmi1_import_free_model_instance(FMI1ME->FMIImportInstance);
  fmi1_import_destroy_dllfmu(FMI1ME->FMIImportInstance);
  fmi1_import_free(FMI1ME->FMIImportInstance);
  fmi_import_free_context(FMI1ME->FMIImportContext);
  free(FMI1ME->FMIWorkingDirectory);
  free(FMI1ME->FMIInstanceName);
  free(FMI1ME->FMIEventInfo);
}

/*
 * Wrapper for the FMI function fmiSetTime.
 * Returns status.
 */
double fmi1SetTime_OMC(void* in_fmi1me, double time)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_set_time(FMI1ME->FMIImportInstance, time);
  return time;
}

/*
 * Wrapper for the FMI function fmiGetContinuousStates.
 * parameter flowParams is dummy and is only used to run the equations in sequence.
 * Returns states.
 */
void fmi1GetContinuousStates_OMC(void* in_fmi1me, int numberOfContinuousStates, double flowParams, double* states)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_get_continuous_states(FMI1ME->FMIImportInstance, (fmi1_real_t*)states, numberOfContinuousStates);
}

/*
 * Wrapper for the FMI function fmiSetContinuousStates.
 * parameter flowParams is dummy and is only used to run the equations in sequence.
 * Returns status.
 */
double fmi1SetContinuousStates_OMC(void* in_fmi1me, int numberOfContinuousStates, double flowParams, double* states)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_set_continuous_states(FMI1ME->FMIImportInstance, (fmi1_real_t*)states, numberOfContinuousStates);
  return flowParams;
}

/*
 * Wrapper for the FMI function fmiGetEventIndicators.
 * parameter flowStates is dummy and is only used to run the equations in sequence.
 * Returns events.
 */
void fmi1GetEventIndicators_OMC(void* in_fmi1me, int numberOfEventIndicators, double flowStates, double* events)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_get_event_indicators(FMI1ME->FMIImportInstance, (fmi1_real_t*)events, numberOfEventIndicators);
}

/*
 * Wrapper for the FMI function fmiGetDerivatives.
 * parameter flowStates is dummy and is only used to run the equations in sequence.
 * Returns states.
 */
void fmi1GetDerivatives_OMC(void* in_fmi1me, int numberOfContinuousStates, double flowStates, double* states)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_get_derivatives(FMI1ME->FMIImportInstance, (fmi1_real_t*)states, numberOfContinuousStates);
}

/*
 * Wrapper for the FMI function fmiEventUpdate.
 * parameter flowStates is dummy and is only used to run the equations in sequence.
 * Returns FMI Event Info i.e fmi1_event_info_t
 */
int fmi1EventUpdate_OMC(void* in_fmi1me, int intermediateResults, double flowStates)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_import_eventUpdate(FMI1ME->FMIImportInstance, intermediateResults, FMI1ME->FMIEventInfo);
  return FMI1ME->FMIEventInfo->stateValuesChanged;
}

/*
 * Wrapper for the FMI function fmiEventUpdate.
 * parameter flowStates is dummy and is only used to run the equations in sequence.
 * Returns FMI EventInfo nextEventTime
 */
double fmi1nextEventTime_OMC(void* in_fmi1me, double flowStates)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  return FMI1ME->FMIEventInfo->nextEventTime;
}

/*
 * Wrapper for the FMI function fmiCompletedIntegratorStep.
 */
int fmi1CompletedIntegratorStep_OMC(void* in_fmi1me, double flowStates)
{
  FMI1ModelExchange* FMI1ME = (FMI1ModelExchange*)in_fmi1me;
  fmi1_boolean_t callEventUpdate = fmi1_false;
  fmi1_import_completed_integrator_step(FMI1ME->FMIImportInstance, &callEventUpdate);
  return callEventUpdate;
}

#ifdef __cplusplus
}
#endif
