/*
    Copyright (C) 2012-2023 Modelon AB

    This program is free software: you can redistribute it and/or modify
    it under the terms of the BSD style license.

     This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    FMILIB_License.txt file for more details.

    You should have received a copy of the FMILIB_License.txt file
    along with this program. If not, contact Modelon AB <http://www.modelon.com>.
*/

#ifndef JM_CALLBACKS_H
#define JM_CALLBACKS_H
#include <stddef.h>
#include <stdarg.h>

#include <fmilib_config.h>

#include "jm_types.h"

#ifdef __cplusplus
extern "C" {
#endif
/** \file jm_callbacks.h Definition of ::jm_callbacks and supporting functions
    *
    * \addtogroup jm_utils
    * @{
        \addtogroup jm_callbacks
    * @}
*/
/** \addtogroup jm_callbacks Definition of callbacks struct and supporting functions
* @{  */
typedef struct jm_callbacks jm_callbacks;

/** \name Memory management callbacks
* jm_malloc_f, jm_realloc_f, jm_calloc_f, jm_free_f function
* types correspond to the standard C memory management functions
* @{
*/
/** \brief Allocation function type. */
typedef jm_voidp (*jm_malloc_f)(size_t size);

/** \brief Re-allocation function type. */
typedef jm_voidp (*jm_realloc_f)(void *ptr, size_t size);

/** \brief Zero-initialized allocation function type. */
typedef jm_voidp (*jm_calloc_f)(size_t numitems, size_t itemsize);

/** \brief Free memory function type. */
typedef void (*jm_free_f)(jm_voidp p);
/** @}
*/


/**
*
* \brief Logger callback type.
*
* The logger callback is used to report errors. Note that this function is
* by default only used in FMI standard independent code (e.g., fmi_import_get_fmi_version()).
* Since logging functions are different between different standard versions separate
* logging functions are necessary for each FMI implementation.\n
* Defaults are provided for each standard.
*/
typedef void (*jm_logger_f)(jm_callbacks* c, jm_string module, jm_log_level_enu_t log_level, jm_string message);

/** \brief Maximum message size that can be stored in the ::jm_callbacks struct */
#define JM_MAX_ERROR_MESSAGE_SIZE 2000

/** \brief The callbacks struct is sent to all the modules in the library */
struct jm_callbacks {
    /** \brief Allocate non-initialized memory */
    jm_malloc_f malloc;
    /** \brief Allocate zero initialized memory */
    jm_calloc_f calloc;
    /** \brief Re-allocate memory */
    jm_realloc_f realloc;
    /** \brief Free-allocated memory */
    jm_free_f free;
    /** \brief Logging callback */
    jm_logger_f logger;
    /** \brief Logging level */
    jm_log_level_enu_t log_level;
    /** \brief Arbitrary context pointer passed to the logger function  */
    jm_voidp context;
    /** \brief The buffer used along with jm_get_last_error() */
    char errMessageBuffer[JM_MAX_ERROR_MESSAGE_SIZE];
};

/**
* \brief Get the last log message produced by the library.
*
* An alternative way to get error information is to use jm_get_last_error(). This is only meaningful
* if logger function is not present.
*/
static jm_string jm_get_last_error(jm_callbacks* cb) {return cb->errMessageBuffer; }

/**
    \brief Clear the last generated log message.
*/
static void jm_clear_last_error(jm_callbacks* cb) { cb->errMessageBuffer[0] = 0; }

/**
\brief Set the structure to be returned by jm_get_default_callbacks().

    @param c - a pointer to initialized struct to be used as default later on. If this is NULL
        library default implementation will be used.
*/
FMILIB_EXPORT
void jm_set_default_callbacks(jm_callbacks* c);

/**
    \brief Get default callbacks. The function never returns NULL.
    @return Default ::jm_callbacks struture. Either the one supplied by the library of the one set with jm_set_default_callbacks().
*/
FMILIB_EXPORT
jm_callbacks* jm_get_default_callbacks(void);

/**
    \brief The default logger implementation prints messages to stderr.
*/
FMILIB_EXPORT
void jm_default_logger(jm_callbacks* c, jm_string module, jm_log_level_enu_t log_level, jm_string message);

/**
    \brief Send a message to the logger function.
    @param cb - callbacks to be used for reporting;
    @param module - a name of reporting module;
    @param log_level - message kind;
    @param fmt - "printf" type of format followed by the arguments.
*/
FMILIB_EXPORT
void jm_log(jm_callbacks* cb, const char* module, jm_log_level_enu_t log_level, const char* fmt, ...);

/** \copydoc jm_log()
    @param ap - variable size argument list.
*/
FMILIB_EXPORT
void jm_log_v(jm_callbacks* cb, const char* module, jm_log_level_enu_t log_level, const char* fmt, va_list ap);

/** \brief Send a fatal error message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_fatal_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap);
/** \brief Send a fatal error message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_fatal(jm_callbacks* cb, const char* module, const char* fmt, ...);

/** \brief Send a error message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_error_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap);

/** \brief Send a error message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_error(jm_callbacks* cb, const char* module, const char* fmt, ...);

/** \brief Send a warning message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_warning_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap);

/** \brief Send a warning message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_warning(jm_callbacks* cb, const char* module, const char* fmt, ...);

/** \brief Send an info message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_info_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap);
/** \brief Send an info message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_info(jm_callbacks* cb, const char* module, const char* fmt, ...);

/** \brief Send a verbose message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_verbose_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap);
/** \brief Send a verbose message to the logger function. See jm_log() for details.
*/
FMILIB_EXPORT
void jm_log_verbose(jm_callbacks* cb, const char* module, const char* fmt, ...);

/** \brief Convert log level into a string */
FMILIB_EXPORT
const char* jm_log_level_to_string(jm_log_level_enu_t level);


#ifdef FMILIB_ENABLE_LOG_LEVEL_DEBUG
/** \brief Send a debug message to the logger function. See jm_log() for details.

    Note that the function is only active if the library is configure with FMILIB_ENABLE_LOG_LEVEL_DEBUG=ON
*/
FMILIB_EXPORT
void jm_log_debug_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap);
/** \brief Send a debug message to the logger function. See jm_log() for details.

    Note that the function is only active if the library is configure with FMILIB_ENABLE_LOG_LEVEL_DEBUG=ON
*/
FMILIB_EXPORT
void jm_log_debug(jm_callbacks* cb, const char* module, const char* fmt, ...);
#else
/** \brief Send a debug message to the logger function. See jm_log() for details.

    Note that the function is only active if the library is configure with FMILIB_ENABLE_LOG_LEVEL_DEBUG=ON
*/
static void jm_log_debug_v(jm_callbacks* cb, const char* module, const char* fmt, va_list ap) {}
/** \brief Send a debug message to the logger function. See jm_log() for details.

    Note that the function is only active if the library is configure with FMILIB_ENABLE_LOG_LEVEL_DEBUG=ON
*/
static void jm_log_debug(jm_callbacks* cb, const char* module, const char* fmt, ...) {}
#endif


/** @}
*/

#ifdef __cplusplus
}
#endif
/* JM_CONTEXT_H */
#endif
