/* The Clear BSD License
 *
 * Copyright (c) 2025 EdgeImpulse Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted (subject to the limitations in the disclaimer
 * below) provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
 * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _EI_CLASSIFIER_PORTING_H_
#define _EI_CLASSIFIER_PORTING_H_

#include <stdint.h>
#include <stdlib.h>
#include "edge-impulse-sdk/dsp/returntypes.h"

#if defined(__cplusplus) && EI_C_LINKAGE == 1
extern "C" {
#endif // defined(__cplusplus)

/* Private functions ------------------------------------------------------- */

EI_IMPULSE_ERROR ei_run_impulse_check_canceled();
void ei_serial_set_baudrate(int baudrate);

/* Public functions -------------------------------------------------------- */

/**
 * @defgroup ei_user_functions User-defined functions
 *
 * These functions are required to be implemented by the user for the target platform.
 * See [this porting guide](https://docs.edgeimpulse.com/docs/edge-ai-hardware/porting-guide) for more information. They are declared internally in the Edge Impulse
 * C++ SDK library, and they must be defined by the user.
 *
 * **Source**: [porting/ei_classifier_porting.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/ei_classifier_porting.h)
 *
 * **Examples**:
 * The following examples demonstrate possible implementations of this function for
 * various platforms. Note the `__attribute__((weak))` in most of the definitions, which
 * means that a user could override the implementation elsewhere in the program:
 * * [Arduino classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/arduino/ei_classifier_porting.cpp)
 * * [mbed classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/mbed/ei_classifier_porting.cpp)
 * * [POSIX classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/posix/ei_classifier_porting.cpp)
 * * [Silicon Labs classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/silabs/ei_classifier_porting.cpp)
 * * [STM32 classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/stm32-cubeai/ei_classifier_porting.cpp)
 * * [TI classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/ti/debug_log.cpp)
 * * [Zephyr classifier porting](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/porting/zephyr/ei_classifier_porting.cpp)
 *
 * @addtogroup ei_user_functions
 * @{
 */

/**
 * Cancelable sleep, can be triggered with signal from other thread
 */
/**
 * @brief Cancellable sleep, can be triggered with signal from other thread
 *
 * Allow the processor or thread to sleep or block for the given time.
 *
 * @param[in] time_ms Time in milliseconds to sleep
 *
 * @return `EI_IMPULSE_OK` if successful, error code otherwise
 */
EI_IMPULSE_ERROR ei_sleep(int32_t time_ms);

/**
 * Read the millisecond timer
 */
/**
 * @brief Read the millisecond timer
 *
 * This function should return the number of milliseconds that have passed since the
 * start of the program. If you do not need to determine the run times for DSP and
 * inference blocks, you can simply return 0 from this function. Your impulse will still
 * work correctly without timing information.
 *
 * @return The number of milliseconds that have passed since the start of the program
 */
uint64_t ei_read_timer_ms();

/**
 * @brief Read the microsecond timer
 *
 * This function should return the number of milliseconds that have passed since the
 * start of the program. If you do not need to determine the run times for DSP and
 * inference blocks, you can simply return 0 from this function. Your impulse will still
 * work correctly without timing information.
 *
 * @return The number of microseconds that have passed since the start of the program
 */
uint64_t ei_read_timer_us();

/**
 * @brief Send a single character to the serial port
 *
 * @param[in]  c The chararater to send
 */
void ei_putchar(char c);

/**
 * @brief Read a single character from the serial port
 *
 * @return The character read from the serial port
 */
char ei_getchar(void);

/**
 * @brief Print wrapper around printf()
 *
 * `ei_printf()` is declared internally to the Edge Impulse SDK library so that debugging
 * information (e.g. during inference) can be printed out. However, the function must be
 * defined by the user, as printing methods can change depending on the platform and use
 * case. For example, you may want to print debugging information to stdout in Linux or
 * over a UART serial port on a microcontroller.
 *
 * @param[in] format Pointer to a character array or string that should be printed
 * @param[in] ... Other optional arguments may be passed as necessary (e.g. handle to a
 *  UART object). Note that any calls to `ei_printf()` from within the
 *  *edge-impulse-sdk* library do not pass anything other than the `format` argument.
 */
__attribute__ ((format (printf, 1, 2)))
void ei_printf(const char *format, ...);

/**
 * @brief Used to print floating point numbers
 *
 * Some platforms cannot handle directly printing floating point numbers (e.g. to a
 * console or over a serial port). If your platform cannot directly print floats,
 * provide an implementation of this function to print them as needed (for example,
 * construct a string containing scientific notation with integers and call
 * `ei_printf()`).
 *
 * If your platform can print floating point values, the easiest implementation of this
 * function is as follows:
 *
 * ```
 * __attribute__((weak)) void ei_printf_float(float f) {
 *     printf("%f", f);
 * }
 * ```
 *
 * @param[in] f The floating point number to print
 */
void ei_printf_float(float f);

/**
 * @brief Wrapper around malloc
 *
 * This function should allocate `size` bytes and return a pointer to the allocated
 * memory. In bare-metal implementations, it can simply be a wrapper for `malloc()`. For
 * example:
 *
 * ```
 * __attribute__((weak)) void *ei_malloc(size_t size) {
 *     return malloc(size);
 * }
 * ```
 *
 * If you intend to run your impulse in a multi-threaded environment, you will need to
 * ensure that your implementation of `ei_malloc()` is thread-safe. For example, if you
 * are using FreeRTOS, here is one possible implementation:
 *
 * ```
 * __attribute__((weak)) void *ei_malloc(size_t size) {
 *     return pvPortMalloc(size);
 * }
 * ```
 *
 * @param[in] size The number of bytes to allocate
 */
void *ei_malloc(size_t size);

/**
 * @brief Wrapper around calloc
 *
 * This function should allocate `nitems * size` bytes and initialize all bytes in this
 * allocated memory to 0. It should return a pointer to the allocated memory. In
 * bare-metal implementations, it can simply be a wrapper for `calloc()`. For example:
 *
 * ```
 * __attribute__((weak)) void *ei_calloc(size_t nitems, size_t size) {
 *     return calloc(nitems, size);
 * }
 * ```
 *
 * If you intend to run your impulse in a multi-threaded environment, you will need to
 * ensure that your implementation of `ei_calloc()` is thread-safe. For example, if you
 * are using FreeRTOS, here is one possible implementation:
 *
 * ```
 * __attribute__((weak)) void *ei_calloc(size_t nitems, size_t size) {
 *     void *ptr = NULL;
 *     if (size > 0) {
 *         ptr = pvPortMalloc(nitems * size);
 *         if(ptr)
 *            memset(ptr, 0, (nitems * size));
 *     }
 *     return ptr;
 * }
 * ```
 *
 * @param[in] nitems Number of blocks to allocate and clear
 * @param[in] size Size (in bytes) of each block
 */
void *ei_calloc(size_t nitems, size_t size);

/**
 * @brief Wrapper around free
 *
 * This function should free the memory space pointed to by `ptr`. If `ptr` is `NULL`,
 * no operation should be performed. In bare-metal implementations, it can simply be a
 * wrapper for `free()`. For example:
 *
 * ```
 * __attribute__((weak)) void ei_free(void *ptr) {
 *     free(ptr);
 * }
 * ```
 *
 * If you intend to run your impulse in a multi-threaded environment, you will need to
 * ensure that your implementation of `ei_free()` is thread-safe. For example, if you
 * are using FreeRTOS, here is one possible implementation:
 *
 * ```
 * __attribute__((weak)) void ei_free(void *ptr) {
 *     pvPortFree(ptr);
 * }
 * ```
 *
 * @param[in] ptr Pointer to the memory to free
 */
void ei_free(void *ptr);

/** @} */

#if defined(__cplusplus) && EI_C_LINKAGE == 1
}
#endif // defined(__cplusplus) && EI_C_LINKAGE == 1

// Load porting layer depending on target

// First check if any of the general frameworks or operating systems are supported/enabled
#ifndef EI_PORTING_ZEPHYR
#if defined(__ZEPHYR__)
#define EI_PORTING_ZEPHYR      1
#else
#define EI_PORTING_ZEPHYR      0
#endif
#endif

#ifndef EI_PORTING_ARDUINO
#ifdef ARDUINO
#define EI_PORTING_ARDUINO      1
#else
#define EI_PORTING_ARDUINO      0
#endif
#endif

#ifndef EI_PORTING_ANDROID
#ifdef __ANDROID__
#define EI_PORTING_ANDROID      1
#else
#define EI_PORTING_ANDROID      0
#endif
#endif

#ifndef EI_PORTING_MBED
#ifdef __MBED__
#define EI_PORTING_MBED      1
#else
#define EI_PORTING_MBED      0
#endif
#endif

// Then check for target spcific build systems

#ifndef EI_PORTING_ESPRESSIF
#if ((defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4)) && EI_PORTING_ZEPHYR == 0)
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#define portTICK_RATE_MS portTICK_PERIOD_MS
#endif
#define EI_PORTING_ESPRESSIF      1
#define EI_PORTING_ARDUINO        0
#else
#define EI_PORTING_ESPRESSIF     0
#endif
#endif

#ifndef EI_PORTING_POSIX
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#define EI_PORTING_POSIX      1
#else
#define EI_PORTING_POSIX      0
#endif
#endif

#ifndef EI_PORTING_SILABS
#if defined(EFR32MG12P332F1024GL125)
#define EI_PORTING_SILABS      1
#else
#define EI_PORTING_SILABS      0
#endif
#endif

#ifndef EI_PORTING_RASPBERRY
#ifdef PICO_BOARD
#define EI_PORTING_RASPBERRY      1
#else
#define EI_PORTING_RASPBERRY      0
#endif
#endif


#ifndef EI_PORTING_STM32_CUBEAI
#if defined(USE_HAL_DRIVER) && !defined(__MBED__) && EI_PORTING_ZEPHYR == 0
#define EI_PORTING_STM32_CUBEAI      1
#else
#define EI_PORTING_STM32_CUBEAI      0
#endif
#endif

#ifndef EI_PORTING_HIMAX
#ifdef CPU_ARC
#define EI_PORTING_HIMAX        1
#else
#define EI_PORTING_HIMAX        0
#endif
#endif

#ifndef EI_PORTING_MINGW32
#ifdef __MINGW32__
#define EI_PORTING_MINGW32      1
#else
#define EI_PORTING_MINGW32      0
#endif
#endif
// End load porting layer depending on target

// Additional configuration for specific architecture
#if defined(__CORTEX_M)

#if (__CORTEX_M == 55U)
#define EI_MAX_OVERFLOW_BUFFER_COUNT	15
#endif

#if (__CORTEX_M == 85U)
#define EI_MAX_OVERFLOW_BUFFER_COUNT	50
#endif

#endif

#if defined(CONFIG_IDF_TARGET_ESP32S3)
#define EI_MAX_OVERFLOW_BUFFER_COUNT	30
#endif

// End additional configuration

#endif // _EI_CLASSIFIER_PORTING_H_
