#ifndef _EPICARDIUM_H
#define _EPICARDIUM_H

#include <stdint.h>
#include <errno.h>

#ifndef __SPHINX_DOC
/* Some headers are not recognized by hawkmoth for some odd reason */
#include <stddef.h>
#include <stdbool.h>
#else
typedef unsigned int size_t;
typedef _Bool bool;
#endif /* __SPHINX_DOC */

/*
 * These definitions are required for the code-generator.  Please don't touch!
 */
#ifndef API
#define API(id, def) def
#endif
#ifndef API_ISR
#define API_ISR(id, isr) void isr(void);
#endif

/*
 * IDs for all defined API calls.  These IDs should not be needed in application
 * code on any side.
 */

/* clang-format off */
#define API_SYSTEM_EXIT             0x1
#define API_SYSTEM_EXEC             0x2
#define API_SYSTEM_RESET            0x3
#define API_BATTERY_VOLTAGE         0x4

#define API_INTERRUPT_ENABLE        0xA
#define API_INTERRUPT_DISABLE       0xB

#define API_UART_WRITE_STR         0x10
#define API_UART_READ_CHAR         0x11
#define API_UART_READ_STR          0x12

#define API_STREAM_READ            0x1F

#define API_DISP_OPEN              0x20
#define API_DISP_CLOSE             0x21
#define API_DISP_PRINT             0x22
#define API_DISP_CLEAR             0x23
#define API_DISP_UPDATE            0x24
#define API_DISP_LINE              0x25
#define API_DISP_RECT              0x26
#define API_DISP_CIRC              0x27
#define API_DISP_PIXEL             0x28
#define API_DISP_FRAMEBUFFER       0x29
#define API_DISP_BACKLIGHT         0x2a
#define API_DISP_PRINT_ADV         0x2b

/* API_BATTERY_VOLTAGE              0x30 */
#define API_BATTERY_CURRENT        0x31
#define API_CHARGEIN_VOLTAGE       0x32
#define API_CHARGEIN_CURRENT       0x33
#define API_SYSTEM_VOLTAGE         0x34
#define API_THERMISTOR_VOLTAGE     0x35

#define API_FILE_OPEN              0x40
#define API_FILE_CLOSE             0x41
#define API_FILE_READ              0x42
#define API_FILE_WRITE             0x44
#define API_FILE_FLUSH             0x45
#define API_FILE_SEEK              0x46
#define API_FILE_TELL              0x47
#define API_FILE_STAT              0x48
#define API_FILE_OPENDIR           0x49
#define API_FILE_READDIR           0x4a
#define API_FILE_UNLINK            0x4b
#define API_FILE_RENAME            0x4c
#define API_FILE_MKDIR             0x4d

#define API_RTC_GET_SECONDS        0x50
#define API_RTC_SCHEDULE_ALARM     0x51
#define API_RTC_SET_MILLISECONDS   0x52
#define API_RTC_GET_MILLISECONDS   0x53
#define API_RTC_GET_MONOTONIC_SECONDS      0x54
#define API_RTC_GET_MONOTONIC_MILLISECONDS 0x55

#define API_LEDS_SET               0x60
#define API_LEDS_SET_HSV           0x61
#define API_LEDS_PREP              0x62
#define API_LEDS_PREP_HSV          0x63
#define API_LEDS_UPDATE            0x64
#define API_LEDS_SET_POWERSAVE     0x65
#define API_LEDS_SET_ROCKET        0x66
#define API_LEDS_SET_FLASHLIGHT    0x67
#define API_LEDS_DIM_TOP           0x68
#define API_LEDS_DIM_BOTTOM        0x69
#define API_LEDS_SET_ALL           0x6a
#define API_LEDS_SET_ALL_HSV       0x6b
#define API_LEDS_SET_GAMMA_TABLE   0x6c
#define API_LEDS_CLEAR_ALL         0x6d
#define API_LEDS_GET_ROCKET        0x6e
#define API_LEDS_GET               0x6f

#define API_VIBRA_SET              0x70
#define API_VIBRA_VIBRATE          0x71

#define API_LIGHT_SENSOR_RUN       0x80
#define API_LIGHT_SENSOR_GET       0x81
#define API_LIGHT_SENSOR_STOP      0x82
#define API_LIGHT_SENSOR_READ	   0x83

#define API_BUTTONS_READ           0x90

#define API_GPIO_SET_PIN_MODE      0xA0
#define API_GPIO_GET_PIN_MODE      0xA1
#define API_GPIO_WRITE_PIN         0xA2
#define API_GPIO_READ_PIN          0xA3

#define API_TRNG_READ              0xB0

#define API_PERSONAL_STATE_SET     0xc0
#define API_PERSONAL_STATE_GET     0xc1
#define API_PERSONAL_STATE_IS_PERSISTENT 0xc2

#define API_BME680_INIT            0xD0
#define API_BME680_DEINIT          0xD1
#define API_BME680_GET_DATA        0xD2

#define API_BHI160_ENABLE          0xe0
#define API_BHI160_DISABLE         0xe1
#define API_BHI160_DISABLE_ALL     0xe2

#define API_MAX30001_ENABLE        0xf0
#define API_MAX30001_DISABLE       0xf1

#define API_MAX86150_ENABLE        0x0100
#define API_MAX86150_DISABLE       0x0101

#define API_USB_SHUTDOWN           0x110
#define API_USB_STORAGE            0x111
#define API_USB_CDCACM             0x112

#define API_WS2812_WRITE           0x0120

#define API_CONFIG_GET_STRING      0x130
#define API_CONFIG_GET_INTEGER     0x131
#define API_CONFIG_GET_BOOLEAN     0x132
/* clang-format on */

typedef uint32_t api_int_id_t;

/**
 * Interrupts
 * ==========
 * Next to API calls, Epicardium API also has an interrupt mechanism to serve
 * the other direction.  These interrupts can be enabled/disabled
 * (masked/unmasked) using :c:func:`epic_interrupt_enable` and
 * :c:func:`epic_interrupt_disable`.
 *
 * .. warning::
 *
 *    Never attempt to call the API from inside an ISR.  This might trigger an
 *    assertion if a call is already being made from thread context.  We plan to
 *    lift this restriction at some point, but for the time being, this is how
 *    it is.
 */

/**
 * Enable/unmask an API interrupt.
 *
 * :param int_id: The interrupt to be enabled
 */
API(API_INTERRUPT_ENABLE, int epic_interrupt_enable(api_int_id_t int_id));

/**
 * Disable/mask an API interrupt.
 *
 * :param int_id: The interrupt to be disabled
 */
API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));

/**
 * The following interrupts are defined:
 */

/* clang-format off */
/** Reset Handler */
#define EPIC_INT_RESET                  0
/** ``^C`` interrupt. See :c:func:`epic_isr_ctrl_c` for details.  */
#define EPIC_INT_CTRL_C                 1
/** UART Receive interrupt.  See :c:func:`epic_isr_uart_rx`. */
#define EPIC_INT_UART_RX                2
/** RTC Alarm interrupt.  See :c:func:`epic_isr_rtc_alarm`. */
#define EPIC_INT_RTC_ALARM              3
/** BHI160 Accelerometer.  See :c:func:`epic_isr_bhi160_accelerometer`. */
#define EPIC_INT_BHI160_ACCELEROMETER	4
/** BHI160 Orientation Sensor.  See :c:func:`epic_isr_bhi160_orientation`. */
#define EPIC_INT_BHI160_ORIENTATION	5
/** BHI160 Gyroscope.  See :c:func:`epic_isr_bhi160_gyroscope`. */
#define EPIC_INT_BHI160_GYROSCOPE	6
/** MAX30001 ECG.  See :c:func:`epic_isr_max30001_ecg`. */
#define EPIC_INT_MAX30001_ECG		7
/** BHI160 Magnetometer.  See :c:func:`epic_isr_bhi160_magnetometer`. */
#define EPIC_INT_BHI160_MAGNETOMETER    8
/** MAX86150 ECG and PPG sensor.  See :c:func:`epic_isr_max86150`. */
#define EPIC_INT_MAX86150		9

/* Number of defined interrupts. */
#define EPIC_INT_NUM                    10
/* clang-format on */

/*
 * "Reset Handler*.  This isr is implemented by the API caller and is used to
 * reset the core for loading a new payload.
 *
 * Just listed here for completeness.  You don't need to implement this yourself.
 */
API_ISR(EPIC_INT_RESET, __epic_isr_reset);

/**
 * Core API
 * ========
 * The following functions control execution of code on core 1.
 */

/**
 * Stop execution of the current payload and return to the menu.
 *
 * :param int ret:  Return code.
 * :return: :c:func:`epic_exit` will never return.
 */
void epic_exit(int ret) __attribute__((noreturn));

/*
 * The actual epic_exit() function is not an API call because it needs special
 * behavior.  The underlying call is __epic_exit() which returns.  After calling
 * this API function, epic_exit() will enter the reset handler.
 */
API(API_SYSTEM_EXIT, void __epic_exit(int ret));

/**
 * Stop execution of the current payload and immediately start another payload.
 *
 * :param char* name: Name (path) of the new payload to start.  This can either
 *    be:
 *
 *    - A path to an ``.elf`` file (l0dable).
 *    - A path to a ``.py`` file (will be loaded using Pycardium).
 *    - A path to a directory (assumed to be a Python module, execution starts
 *      with ``__init__.py`` in this folder).
 *
 * :return: :c:func:`epic_exec` will only return in case loading went wrong.
 *    The following error codes can be returned:
 *
 *    - ``-ENOENT``: File not found.
 *    - ``-ENOEXEC``: File not a loadable format.
 */
int epic_exec(char *name);

/*
 * Underlying API call for epic_exec().  The function is not an API call itself
 * because it needs special behavior when loading a new payload.
 */
API(API_SYSTEM_EXEC, int __epic_exec(char *name));

/**
 * Reset/Restart card10
 */
API(API_SYSTEM_RESET, void epic_system_reset(void));

/**
 * PMIC API
 * ===============
 */


/**
 * Read the current battery voltage.
 */
API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result));

/**
 * Read the current battery current.
 */
API(API_BATTERY_CURRENT, int epic_read_battery_current(float *result));

/**
 * Read the current charge voltage.
 */
API(API_CHARGEIN_VOLTAGE, int epic_read_chargein_voltage(float *result));

/**
 * Read the current charge current.
 */
API(API_CHARGEIN_CURRENT, int epic_read_chargein_current(float *result));

/**
 * Read the current system voltage.
 */
API(API_SYSTEM_VOLTAGE, int epic_read_system_voltage(float *result));

/**
 * Read the current thermistor voltage.
 */
API(API_THERMISTOR_VOLTAGE, int epic_read_thermistor_voltage(float *result));


/**
 * UART/Serial Interface
 * =====================
 */

/**
 * Write a string to all connected serial devices.  This includes:
 *
 * - Real UART, whose pins are mapped onto USB-C pins.  Accessible via the HW-debugger.
 * - A CDC-ACM device available via USB.
 * - Maybe, in the future, bluetooth serial?
 *
 * :param str:  String to write.  Does not necessarily have to be NULL-terminated.
 * :param length:  Amount of bytes to print.
 */
API(API_UART_WRITE_STR, void epic_uart_write_str(
	const char *str, size_t length
));

/**
 * Try reading a single character from any connected serial device.
 *
 * If nothing is available, :c:func:`epic_uart_read_char` returns ``(-1)``.
 *
 * :return:  The byte or ``(-1)`` if no byte was available.
 */
API(API_UART_READ_CHAR, int epic_uart_read_char(void));

/**
 * Read as many characters as possible from the UART queue.
 *
 * :c:func:`epic_uart_read_str` will not block if no new data is available.  For
 * an example, see :c:func:`epic_isr_uart_rx`.
 *
 * :param char* buf: Buffer to be filled with incoming data.
 * :param size_t cnt: Size of ``buf``.
 * :returns: Number of bytes read.  Can be ``0`` if no data was available.
 *    Might be a negative value if an error occured.
 */
API(API_UART_READ_STR, int epic_uart_read_str(char *buf, size_t cnt));

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_UART_RX`
 *
 * UART receive interrupt.  This interrupt is triggered whenever a new character
 * becomes available on any connected UART device.  This function is weakly
 * aliased to :c:func:`epic_isr_default` by default.
 *
 * **Example**:
 *
 * .. code-block:: cpp
 *
 *    void epic_isr_uart_rx(void)
 *    {
 *            char buffer[33];
 *            int n = epic_uart_read_str(&buffer, sizeof(buffer) - 1);
 *            buffer[n] = '\0';
 *            printf("Got: %s\n", buffer);
 *    }
 *
 *    int main(void)
 *    {
 *            epic_interrupt_enable(EPIC_INT_UART_RX);
 *
 *            while (1) {
 *                    __WFI();
 *            }
 *    }
 */
API_ISR(EPIC_INT_UART_RX, epic_isr_uart_rx);

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_CTRL_C`
 *
 * A user-defineable ISR which is triggered when a ``^C`` (``0x04``) is received
 * on any serial input device.  This function is weakly aliased to
 * :c:func:`epic_isr_default` by default.
 *
 * To enable this interrupt, you need to enable :c:data:`EPIC_INT_CTRL_C`:
 *
 * .. code-block:: cpp
 *
 *    epic_interrupt_enable(EPIC_INT_CTRL_C);
 */
API_ISR(EPIC_INT_CTRL_C, epic_isr_ctrl_c);

/**
 * Buttons
 * =======
 *
 */

/** Button IDs */
enum epic_button {
	/** ``1``, Bottom left button (bit 0). */
	BUTTON_LEFT_BOTTOM   = 1,
	/** ``2``, Bottom right button (bit 1). */
	BUTTON_RIGHT_BOTTOM  = 2,
	/** ``4``, Top right button (bit 2). */
	BUTTON_RIGHT_TOP     = 4,
	/** ``8``, Top left (power) button (bit 3). */
	BUTTON_LEFT_TOP      = 8,
	/** ``8``, Top left (power) button (bit 3). */
	BUTTON_RESET         = 8,
};

/**
 * Read buttons.
 *
 * :c:func:`epic_buttons_read` will read all buttons specified in ``mask`` and
 * return set bits for each button which was reported as pressed.
 *
 * .. note::
 *
 *    The reset button cannot be unmapped from reset functionality.  So, while
 *    you can read it, it cannot be used for app control.
 *
 * **Example**:
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    uint8_t pressed = epic_buttons_read(BUTTON_LEFT_BOTTOM | BUTTON_RIGHT_BOTTOM);
 *
 *    if (pressed & BUTTON_LEFT_BOTTOM) {
 *            // Bottom left button is pressed
 *    }
 *
 *    if (pressed & BUTTON_RIGHT_BOTTOM) {
 *            // Bottom right button is pressed
 *    }
 *
 * :param uint8_t mask: Mask of buttons to read.  The 4 LSBs correspond to the 4
 *     buttons:
 *
 *     ===== ========= ============ ===========
 *     ``3`` ``2``     ``1``        ``0``
 *     ----- --------- ------------ -----------
 *     Reset Right Top Right Bottom Left Bottom
 *     ===== ========= ============ ===========
 *
 *     Use the values defined in :c:type:`epic_button` for masking, as shown in
 *     the example above.
 * :return: Returns nonzero value if unmasked buttons are pushed.
 */
API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask));

/**
 * Wristband GPIO
 * ==============
 */

/** GPIO pins IDs */
enum gpio_pin {
    /** ``1``, Wristband connector 1 */
    EPIC_GPIO_WRISTBAND_1 = 1,
    /** ``2``, Wristband connector 2 */
    EPIC_GPIO_WRISTBAND_2 = 2,
    /** ``3``, Wristband connector 3 */
    EPIC_GPIO_WRISTBAND_3 = 3,
    /** ``4``, Wristband connector 4 */
    EPIC_GPIO_WRISTBAND_4 = 4,
};

/** GPIO pin modes */
enum gpio_mode {
    /** Configure the pin as input */
    EPIC_GPIO_MODE_IN = (1<<0),
    /** Configure the pin as output */
    EPIC_GPIO_MODE_OUT = (1<<1),
    EPIC_GPIO_MODE_ADC = (1<<2),

    /** Enable the internal pull-up resistor */
    EPIC_GPIO_PULL_UP = (1<<6),
    /** Enable the internal pull-down resistor */
    EPIC_GPIO_PULL_DOWN = (1<<7),
};

/**
 * Set the mode of a card10 GPIO pin.
 *
 * :c:func:`epic_gpio_set_pin_mode` will set the pin specified by ``pin`` to the mode ``mode``.
 * If the specified pin ID is not valid this function will do nothing.
 *
 * **Example:**
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    // Configure wristband pin 1 as output.
 *    if (epic_gpio_set_pin_mode(GPIO_WRISTBAND_1, GPIO_MODE_OUT)) {
 *        // Do your error handling here...
 *    }
 *
 * :param uint8_t pin: ID of the pin to configure. Use on of the IDs defined in :c:type:`gpio_pin`.
 * :param uint8_t mode: Mode to be configured. Use a combination of the :c:type:`gpio_mode` flags.
 * :returns: ``0`` if the mode was set, ``-EINVAL`` if ``pin`` is not valid or the mode could not be set.
 */
API(API_GPIO_SET_PIN_MODE, int epic_gpio_set_pin_mode(
	uint8_t pin, uint8_t mode
));

/**
 * Get the mode of a card10 GPIO pin.
 *
 * :c:func:`epic_gpio_get_pin_mode` will get the current mode of the GPIO pin specified by ``pin``.
 *
 * **Example:**
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    // Get the mode of wristband pin 1.
 *    int mode = epic_gpio_get_pin_mode(GPIO_WRISTBAND_1);
 *    if (mode < 0) {
 *        // Do your error handling here...
 *    } else {
 *        // Do something with the queried mode information
 *    }
 *
 * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
 * :returns: Configuration byte for the specified pin or ``-EINVAL`` if the pin is not valid.
 */
API(API_GPIO_GET_PIN_MODE, int epic_gpio_get_pin_mode(uint8_t pin));

/**
 * Write value to a card10 GPIO pin,
 *
 * :c:func:`epic_gpio_write_pin` will set the value of the GPIO pin described by ``pin`` to either on or off depending on ``on``.
 *
 * **Example:**
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    // Get the mode of wristband pin 1.
 *    int mode = epic_gpio_get_pin_mode(GPIO_WRISTBAND_1);
 *    if (mode < 0) {
 *        // Do your error handling here...
 *    } else {
 *        // Do something with the queried mode information
 *    }
 *
 * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
 * :param bool on: Sets the pin to either true (on/high) or false (off/low).
 * :returns: ``0`` on succcess, ``-EINVAL`` if ``pin`` is not valid or is not configured as an output.
 */
API(API_GPIO_WRITE_PIN, int epic_gpio_write_pin(uint8_t pin, bool on));

/**
 * Read value of a card10 GPIO pin.
 *
 * :c:func:`epic_gpio_read_pin` will get the value of the GPIO pin described by ``pin``.
 *
 * **Example:**
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    // Get the current value of wristband pin 1.
 *    uint32_t value = epic_gpio_read_pin(GPIO_WRISTBAND_1);
 *    if (mode == -EINVAL) {
 *        // Do your error handling here...
 *    } else {
 *        // Do something with the current value
 *    }
 *
 * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
 * :returns: ``-EINVAL`` if ``pin`` is not valid, an integer value otherwise.
 */
API(API_GPIO_READ_PIN, int epic_gpio_read_pin(uint8_t pin));

/**
 * LEDs
 * ====
 */

/**
 * Set one of card10's RGB LEDs to a certain color in RGB format.
 *
 * This function is rather slow when setting multiple LEDs, use
 * :c:func:`leds_set_all` or :c:func:`leds_prep` + :c:func:`leds_update`
 * instead.
 *
 * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14
 *    are the 4 "ambient" LEDs.
 * :param uint8_t r:  Red component of the color.
 * :param uint8_t g:  Green component of the color.
 * :param uint8_t b:  Blue component of the color.
 */
API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b));


/**
 * Get one of card10's RGB LEDs in format of RGB.
 *
 * :c:func:`epic_leds_get_rgb` will get the value of a RGB  LED described by ``led``.
 *
 * :param int led:  Which LED to get.  0-10 are the LEDs on the top and 11-14
 *    are the 4 "ambient" LEDs.
 * :param uint8_t * rgb:  need tree byte array to get the value of red, green and blue.
 * :returns: ``0`` on success or ``-EPERM`` if the LED is blocked by personal-state.
 *
 * .. versionadded:: 1.10
 */
API(API_LEDS_GET, int epic_leds_get_rgb(int led, uint8_t * rgb));

/**
 * Set one of card10's RGB LEDs to a certain color in HSV format.
 *
 * This function is rather slow when setting multiple LEDs, use
 * :c:func:`leds_set_all_hsv` or :c:func:`leds_prep_hsv` + :c:func:`leds_update`
 * instead.
 *
 * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14 are the 4 "ambient" LEDs.
 * :param float h:  Hue component of the color. (0 <= h < 360)
 * :param float s:  Saturation component of the color. (0 <= s <= 1)
 * :param float v:  Value/Brightness component of the color. (0 <= v <= 0)
 */
API(API_LEDS_SET_HSV, void epic_leds_set_hsv(
	int led, float h, float s, float v
));

/**
 * Set multiple of card10's RGB LEDs to a certain color in RGB format.
 *
 * The first ``len`` leds are set, the remaining ones are not modified.
 *
 * :param uint8_t[len][r,g,b] pattern:  Array with RGB Values with 0 <= len <=
 *    15. 0-10 are the LEDs on the top and 11-14 are the 4 "ambient" LEDs.
 * :param uint8_t len: Length of 1st dimension of ``pattern``, see above.
 */
API(API_LEDS_SET_ALL, void epic_leds_set_all(uint8_t *pattern, uint8_t len));

/**
 * Set multiple of card10's RGB LEDs to a certain color in HSV format.
 *
 * The first ``len`` led are set, the remaining ones are not modified.
 *
 * :param uint8_t[len][h,s,v] pattern:  Array of format with HSV Values with 0
 *    <= len <= 15.  0-10 are the LEDs on the top and 11-14 are the 4 "ambient"
 *    LEDs. (0 <= h < 360, 0 <= s <= 1, 0 <= v <= 1)
 * :param uint8_t len: Length of 1st dimension of ``pattern``, see above.
 */
API(API_LEDS_SET_ALL_HSV, void epic_leds_set_all_hsv(
	float *pattern, uint8_t len
));

/**
 * Prepare one of card10's RGB LEDs to be set to a certain color in RGB format.
 *
 * Use :c:func:`leds_update` to apply changes.
 *
 * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14
 *    are the 4 "ambient" LEDs.
 * :param uint8_t r:  Red component of the color.
 * :param uint8_t g:  Green component of the color.
 * :param uint8_t b:  Blue component of the color.
 */
API(API_LEDS_PREP, void epic_leds_prep(
	int led, uint8_t r, uint8_t g, uint8_t b
));

/**
 * Prepare one of card10's RGB LEDs to be set to a certain color in HSV format.
 *
 * Use :c:func:`leds_update` to apply changes.
 *
 * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14
 *    are the 4 "ambient" LEDs.
 * :param uint8_t h:  Hue component of the color. (float, 0 <= h < 360)
 * :param uint8_t s:  Saturation component of the color. (float, 0 <= s <= 1)
 * :param uint8_t v:  Value/Brightness component of the color. (float, 0 <= v <= 0)
 */
API(API_LEDS_PREP_HSV, void epic_leds_prep_hsv(
	int led, float h, float s, float v
));

/**
 * Set global brightness for top RGB LEDs.
 *
 * Aside from PWM, the RGB LEDs' overall brightness can be controlled with a
 * current limiter independently to achieve a higher resolution at low
 * brightness which can be set with this function.
 *
 * :param uint8_t value:  Global brightness of top LEDs. (1 <= value <= 8, default = 1)
 */
API(API_LEDS_DIM_BOTTOM, void epic_leds_dim_bottom(uint8_t value));

/**
 * Set global brightness for bottom RGB LEDs.
 *
 * Aside from PWM, the RGB LEDs' overall brightness can be controlled with a
 * current limiter independently to achieve a higher resolution at low
 * brightness which can be set with this function.
 *
 * :param uint8_t value:  Global brightness of bottom LEDs. (1 <= value <= 8, default = 8)
 */
API(API_LEDS_DIM_TOP, void epic_leds_dim_top(uint8_t value));

/**
 * Enables or disables powersave mode.
 *
 * Even when set to zero, the RGB LEDs still individually consume ~1mA.
 * Powersave intelligently switches the supply power in groups. This introduces
 * delays in the magnitude of ~10µs, so it can be disabled for high speed
 * applications such as POV.
 *
 * :param bool eco:  Activates powersave if true, disables it when false. (default = True)
 */
API(API_LEDS_SET_POWERSAVE, void epic_leds_set_powersave(bool eco));

/**
 * Updates the RGB LEDs with changes that have been set with :c:func:`leds_prep`
 * or :c:func:`leds_prep_hsv`.
 *
 * The LEDs can be only updated in bulk, so using this approach instead of
 * :c:func:`leds_set` or :c:func:`leds_set_hsv` significantly reduces the load
 * on the corresponding hardware bus.
 */
API(API_LEDS_UPDATE, void epic_leds_update(void));

/**
 * Set the brightness of one of the rocket LEDs.
 *
 * :param int led:  Which LED to set.
 *
 *    +-------+--------+----------+
 *    |   ID  | Color  | Location |
 *    +=======+========+==========+
 *    | ``0`` | Blue   | Left     |
 *    +-------+--------+----------+
 *    | ``1`` | Yellow | Top      |
 *    +-------+--------+----------+
 *    | ``2`` | Green  | Right    |
 *    +-------+--------+----------+
 * :param uint8_t value:  Brightness of LED (value between 0 and 31).
 */
API(API_LEDS_SET_ROCKET, void epic_leds_set_rocket(int led, uint8_t value));

/**
 * Get the brightness of one of the rocket LEDs.
 *
 * :param int led:  Which LED to get.
 *
 *    +-------+--------+----------+
 *    |   ID  | Color  | Location |
 *    +=======+========+==========+
 *    | ``0`` | Blue   | Left     |
 *    +-------+--------+----------+
 *    | ``1`` | Yellow | Top      |
 *    +-------+--------+----------+
 *    | ``2`` | Green  | Right    |
 *    +-------+--------+----------+
 * :returns value:  Brightness of LED (value between 0 and 31)  or ``-EINVAL`` if the LED/rocket does not exists.
 *
 * .. versionadded:: 1.10
 */
API(API_LEDS_GET_ROCKET, int epic_leds_get_rocket(int led));

/**
 * Turn on the bright side LED which can serve as a flashlight if worn on the left wrist or as a rad tattoo illuminator if worn on the right wrist.
 *
 *:param bool power:  Side LED on if true.
 */
API(API_LEDS_SET_FLASHLIGHT, void epic_set_flashlight(bool power));

/**
 * Set gamma lookup table for individual rgb channels.
 *
 * Since the RGB LEDs' subcolor LEDs have different peak brightness and the
 * linear scaling introduced by PWM is not desireable for color accurate work,
 * custom lookup tables for each individual color channel can be loaded into the
 * Epicardium's memory with this function.
 *
 * :param uint8_t rgb_channel:  Color whose gamma table is to be updated, 0->Red, 1->Green, 2->Blue.
 * :param uint8_t[256] gamma_table: Gamma lookup table. (default = 4th order power function rounded up)
 */
API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table(
	uint8_t rgb_channel, uint8_t *gamma_table
));

/**
 * Set all LEDs to a certain RGB color.
 *
 * :param uint8_t r: Value for the red color channel.
 * :param uint8_t g: Value for the green color channel.
 * :param uint8_t b: Value for the blue color channel.
 */
API(API_LEDS_CLEAR_ALL, void epic_leds_clear_all(
	uint8_t r, uint8_t g, uint8_t b
));

/**
 * BME680
 * ======
 *
 * .. versionadded:: 1.4
 */

/**
 * BME680 Sensor Data
 */
struct bme680_sensor_data {
	/** Temperature in degree celsius */
	float temperature;
	/** Humidity in % relative humidity */
	float humidity;
	/** Pressure in hPa */
	float pressure;
	/** Gas resistance in Ohms */
	float gas_resistance;
};

/**
 * Initialize the BM680 sensor.
 *
 * .. versionadded:: 1.4
 *
 * :return: 0 on success or ``-Exxx`` on error.  The following
 *     errors might occur:
 *
 *     - ``-EFAULT``:  On NULL-pointer.
 *     - ``-EINVAL``:  Invalid configuration.
 *     - ``-EIO``:  Communication with the device failed.
 *     - ``-ENODEV``:  Device was not found.
 */
API(API_BME680_INIT, int epic_bme680_init());

/**
 * De-Initialize the BM680 sensor.
 *
 * .. versionadded:: 1.4
 *
 * :return: 0 on success or ``-Exxx`` on error.  The following
 *     errors might occur:
 *
 *     - ``-EFAULT``:  On NULL-pointer.
 *     - ``-EINVAL``:  Invalid configuration.
 *     - ``-EIO``:  Communication with the device failed.
 *     - ``-ENODEV``:  Device was not found.
 */
API(API_BME680_DEINIT, int epic_bme680_deinit());

/**
 * Get the current BME680 data.
 *
 * .. versionadded:: 1.4
 *
 * :param data: Where to store the environmental data.
 * :return: 0 on success or ``-Exxx`` on error.  The following
 *     errors might occur:
 *
 *     - ``-EFAULT``:  On NULL-pointer.
 *     - ``-EINVAL``:  Sensor not initialized.
 *     - ``-EIO``:  Communication with the device failed.
 *     - ``-ENODEV``:  Device was not found.
 */
API(API_BME680_GET_DATA, int epic_bme680_read_sensors(
	struct bme680_sensor_data *data
));

/**
 * MAX86150
 * ======
 */

/**
 * Configuration for a MAX86150 sensor.
 *
 * This struct is used when enabling a sensor using
 * :c:func:`epic_max86150_enable_sensor`.
 */
struct max86150_sensor_config {
    /**
     * Number of samples Epicardium should keep for this sensor.  Do not set
     * this number too high as the sample buffer will eat RAM.
     */
    size_t sample_buffer_len;
    /**
     * Sample rate for PPG from the sensor in Hz.  Maximum data rate is limited
     * to 200 Hz for all sensors though some might be limited at a lower
     * rate.
     *
     * Possible values are 10, 20, 50, 84, 100, 200.
     */
    uint16_t ppg_sample_rate;
};

/**
 * MAX86150 Sensor Data
 */
struct max86150_sensor_data {
	/** Red LED data */
	uint32_t red;
	/** IR LED data */
	uint32_t ir;
	/** ECG data */
	int32_t ecg;
};

/**
 * Enable a MAX86150 PPG and ECG sensor.
 * 
 * Calling this function will instruct the MAX86150 to collect a 
 * data from the sensor.  You can then retrieve the samples using 
 * :c:func:`epic_stream_read`.
 *
 * :param max86150_sensor_config* config: Configuration for this sensor.
 * :param size_t config_size: Size of ``config``.
 * :returns: A sensor descriptor which can be used with
 *    :c:func:`epic_stream_read` or a negative error value:
 *
 *    - ``-ENOMEM``:  The MAX86150 driver failed to create a stream queue.
 *    - ``-ENODEV``:  The MAX86150 driver failed due to physical connectivity problem
 *      (broken wire, unpowered, etc).
 *    - ``-EINVAL``:  config->ppg_sample_rate is not one of 10, 20, 50, 84, 100, 200
 *      or config_size is not size of config.
 *
 * .. versionadded:: 1.13
 */
API(API_MAX86150_ENABLE, int epic_max86150_enable_sensor(struct max86150_sensor_config *config, size_t config_size));

/**
 * Disable the MAX86150 sensor.
 *
 * :returns: 0 in case of success or forward negative error value from stream_deregister.
 *
 * .. versionadded:: 1.13
 */
API(API_MAX86150_DISABLE, int epic_max86150_disable_sensor());

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_MAX86150`
 *
 * :c:func:`epic_isr_max86150` is called whenever the MAX86150
 * PPG sensor has new data available.
 */
API_ISR(EPIC_INT_MAX86150, epic_isr_max86150);

/**
 * Personal State
 * ==============
 * Card10 can display your personal state.
 *
 * If a personal state is set the top-left LED on the bottom side of the
 * harmonics board is directly controlled by epicardium and it can't be
 * controlled by pycardium.
 *
 * To re-enable pycardium control the personal state has to be cleared. To do
 * that simply set it to ``STATE_NONE``.
 *
 * The personal state can be set to be persistent which means it won't get reset
 * on pycardium application change/restart.
 */

/** Possible personal states. */
enum personal_state {
    /** ``0``, No personal state - LED is under regular application control. */
    STATE_NONE = 0,
    /** ``1``, "no contact, please!" - I am overloaded. Please leave me be - red led, continuously on. */
    STATE_NO_CONTACT = 1,
    /** ``2``, "chaos" - Adventure time - blue led, short blink, long blink. */
    STATE_CHAOS = 2,
    /** ``3``, "communication" - want to learn something or have a nice conversation - yellow led, long blinks. */
    STATE_COMMUNICATION = 3,
    /** ``4``, "camp" - I am focussed on self-, camp-, or community maintenance - green led, fade on and off. */
    STATE_CAMP = 4,
    /** STATE_MAX gives latest value and count of possible STATEs**/
    STATE_MAX = 5,
};

/**
 * Set the users personal state.
 *
 * Using :c:func:`epic_personal_state_set` an application can set the users personal state.
 *
 * :param uint8_t state: The users personal state. Must be one of :c:type:`personal_state`.
 * :param bool persistent: Indicates whether the configured personal state will remain set and active on pycardium application restart/change.
 * :returns: ``0`` on success, ``-EINVAL`` if an invalid state was requested.
 */
API(API_PERSONAL_STATE_SET, int epic_personal_state_set(
	uint8_t state, bool persistent
));

/**
 * Get the users personal state.
 *
 * Using :c:func:`epic_personal_state_get` an application can get the currently set personal state of the user.
 *
 * :returns: A value with exactly one value of :c:type:`personal_state` set.
 */
API(API_PERSONAL_STATE_GET, int epic_personal_state_get());

/**
 * Get whether the users personal state is persistent.
 *
 * Using :c:func:`epic_personal_state_is_persistent` an app can find out whether the users personal state is persistent or transient.
 *
 * :returns: ``1`` if the state is persistent, ``0`` otherwise.
 */
API(API_PERSONAL_STATE_IS_PERSISTENT, int epic_personal_state_is_persistent());

/**
 * Sensor Data Streams
 * ===================
 * A few of card10's sensors can do continuous measurements.  To allow
 * performant access to their data, the following function is made for generic
 * access to streams.
 */

/**
 * Read sensor data into a buffer.  ``epic_stream_read()`` will read as many
 * sensor samples into the provided buffer as possible and return the number of
 * samples written.  If no samples are available, ``epic_stream_read()`` will
 * return ``0`` immediately.
 *
 * ``epic_stream_read()`` expects the provided buffer to have a size which is a
 * multiple of the sample size for the given stream.  For the sample-format and
 * size, please consult the sensors documentation.
 *
 * Before reading the internal sensor sample queue, ``epic_stream_read()`` will
 * call a sensor specific *poll* function to allow the sensor driver to fetch
 * new samples from its hardware.  This should, however, never take a long
 * amount of time.
 *
 * :param int sd: Sensor Descriptor.  You get sensor descriptors as return
 *    values when activating the respective sensors.
 * :param void* buf: Buffer where sensor data should be read into.
 * :param size_t count: How many bytes to read at max.  Note that fewer bytes
 *    might be read.  In most cases, this should be ``sizeof(buf)``.
 * :return: Number of data packets read (**not** number of bytes) or a negative
 *    error value.  Possible errors:
 *
 *    - ``-ENODEV``: Sensor is not currently available.
 *    - ``-EBADF``: The given sensor descriptor is unknown.
 *    - ``-EINVAL``:  ``count`` is not a multiple of the sensor's sample size.
 *    - ``-EBUSY``: The descriptor table lock could not be acquired.
 *
 * **Example**:
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    struct foo_measurement sensor_data[16];
 *    int foo_sd, n;
 *
 *    foo_sd = epic_foo_sensor_enable(9001);
 *
 *    while (1) {
 *            n = epic_stream_read(
 *                    foo_sd,
 *                    &sensor_data,
 *                    sizeof(sensor_data)
 *            );
 *
 *            // Print out the measured sensor samples
 *            for (int i = 0; i < n; i++) {
 *                    printf("Measured: %?\n", sensor_data[i]);
 *            }
 *    }
 */
API(API_STREAM_READ, int epic_stream_read(int sd, void *buf, size_t count));

/**
 * BHI160 Sensor Fusion
 * ====================
 * card10 has a BHI160 onboard which is used as an IMU.  BHI160 exposes a few
 * different sensors which can be accessed using Epicardium API.
 *
 * .. versionadded:: 1.4
 *
 * **Example**:
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    // Configure a sensor & enable it
 *    struct bhi160_sensor_config cfg = {0};
 *    cfg.sample_buffer_len = 40;
 *    cfg.sample_rate = 4;   // Hz
 *    cfg.dynamic_range = 2; // g
 *
 *    int sd = epic_bhi160_enable_sensor(BHI160_ACCELEROMETER, &cfg);
 *
 *    // Read sensor data
 *    while (1) {
 *            struct bhi160_data_vector buf[10];
 *
 *            int n = epic_stream_read(sd, buf, sizeof(buf));
 *
 *            for (int i = 0; i < n; i++) {
 *                    printf("X: %6d Y: %6d Z: %6d\n",
 *                           buf[i].x,
 *                           buf[i].y,
 *                           buf[i].z);
 *            }
 *    }
 *
 *    // Disable the sensor
 *    epic_bhi160_disable_sensor(BHI160_ACCELEROMETER);
 */

/**
 * BHI160 Sensor Types
 * -------------------
 */

/**
 * BHI160 virtual sensor type.
 */
enum bhi160_sensor_type {
	/**
	 * Accelerometer
	 *
	 * - Data type: :c:type:`bhi160_data_vector`
	 * - Dynamic range: g's (1x Earth Gravity, ~9.81m*s^-2)
	 */
	BHI160_ACCELEROMETER               = 0,
	/**
	 * Magnetometer
	 *
	 * - Data type: :c:type:`bhi160_data_vector`
	 * - Dynamic range: -1000 to 1000 microtesla
	 */
	BHI160_MAGNETOMETER                = 1,
	/** Orientation */
	BHI160_ORIENTATION                 = 2,
	/** Gyroscope */
	BHI160_GYROSCOPE                   = 3,
	/** Gravity (**Unimplemented**) */
	BHI160_GRAVITY                     = 4,
	/** Linear acceleration (**Unimplemented**) */
	BHI160_LINEAR_ACCELERATION         = 5,
	/** Rotation vector (**Unimplemented**) */
	BHI160_ROTATION_VECTOR             = 6,
	/** Uncalibrated magnetometer (**Unimplemented**) */
	BHI160_UNCALIBRATED_MAGNETOMETER   = 7,
	/** Game rotation vector (whatever that is supposed to be) */
	BHI160_GAME_ROTATION_VECTOR        = 8,
	/** Uncalibrated gyroscrope (**Unimplemented**) */
	BHI160_UNCALIBRATED_GYROSCOPE      = 9,
	/** Geomagnetic rotation vector (**Unimplemented**) */
	BHI160_GEOMAGNETIC_ROTATION_VECTOR = 10,
};

enum bhi160_data_type {
	BHI160_DATA_TYPE_VECTOR
};

/**
 * BHI160 Sensor Data Types
 * ------------------------
 */

/**
 * Vector Data.  The scaling of these values is dependent on the chosen dynamic
 * range.  See the individual sensor's documentation for details.
 */
struct bhi160_data_vector {
	enum bhi160_data_type data_type;

	/** X */
	int16_t x;
	/** Y */
	int16_t y;
	/** Z */
	int16_t z;
	/** Status */
	uint8_t status;
};

/**
 * BHI160 API
 * ----------
 */

/**
 * Configuration for a BHI160 sensor.
 *
 * This struct is used when enabling a sensor using
 * :c:func:`epic_bhi160_enable_sensor`.
 */
struct bhi160_sensor_config {
	/**
	 * Number of samples Epicardium should keep for this sensor.  Do not set
	 * this number too high as the sample buffer will eat RAM.
	 */
	size_t sample_buffer_len;
	/**
	 * Sample rate for the sensor in Hz.  Maximum data rate is limited
	 * to 200 Hz for all sensors though some might be limited at a lower
	 * rate.
	 */
	uint16_t sample_rate;
	/**
	 * Dynamic range.  Interpretation of this value depends on
	 * the sensor type.  Please refer to the specific sensor in
	 * :c:type:`bhi160_sensor_type` for details.
	 */
	uint16_t dynamic_range;
	/** Always zero. Reserved for future parameters. */
	uint8_t _padding[8];
};

/**
 * Enable a BHI160 virtual sensor.  Calling this function will instruct the
 * BHI160 to collect data for this specific virtual sensor.  You can then
 * retrieve the samples using :c:func:`epic_stream_read`.
 *
 * :param bhi160_sensor_type sensor_type: Which sensor to enable.
 * :param bhi160_sensor_config* config: Configuration for this sensor.
 * :returns: A sensor descriptor which can be used with
 *    :c:func:`epic_stream_read` or a negative error value:
 *
 *    - ``-EBUSY``:  The BHI160 driver is currently busy with other tasks and
 *      could not be acquired for enabling a sensor.
 *
 * .. versionadded:: 1.4
 */
API(API_BHI160_ENABLE, int epic_bhi160_enable_sensor(
	enum bhi160_sensor_type sensor_type,
	struct bhi160_sensor_config *config
));

/**
 * Disable a BHI160 sensor.
 *
 * :param bhi160_sensor_type sensor_type: Which sensor to disable.
 *
 * .. versionadded:: 1.4
 */
API(API_BHI160_DISABLE, int epic_bhi160_disable_sensor(
	enum bhi160_sensor_type sensor_type
));

/**
 * Disable all BHI160 sensors.
 *
 * .. versionadded:: 1.4
 */
API(API_BHI160_DISABLE_ALL, void epic_bhi160_disable_all_sensors());

/**
 * BHI160 Interrupt Handlers
 * -------------------------
 */

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_ACCELEROMETER`
 *
 * :c:func:`epic_isr_bhi160_accelerometer` is called whenever the BHI160
 * accelerometer has new data available.
 */
API_ISR(EPIC_INT_BHI160_ACCELEROMETER, epic_isr_bhi160_accelerometer);

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_MAGNETOMETER`
 *
 * :c:func:`epic_isr_bhi160_magnetometer` is called whenever the BHI160
 * magnetometer has new data available.
 */
API_ISR(EPIC_INT_BHI160_MAGNETOMETER, epic_isr_bhi160_magnetometer);

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_ORIENTATION`
 *
 * :c:func:`epic_isr_bhi160_orientation` is called whenever the BHI160
 * orientation sensor has new data available.
 */
API_ISR(EPIC_INT_BHI160_ORIENTATION, epic_isr_bhi160_orientation);

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_GYROSCOPE`
 *
 * :c:func:`epic_isr_bhi160_orientation` is called whenever the BHI160
 * gyroscrope has new data available.
 */
API_ISR(EPIC_INT_BHI160_GYROSCOPE, epic_isr_bhi160_gyroscope);


/**
 * Vibration Motor
 * ===============
 */

/**
 * Turn vibration motor on or off
 *
 * :param status: 1 to turn on, 0 to turn off.
 */
API(API_VIBRA_SET, void epic_vibra_set(int status));

/**
 * Turn vibration motor on for a given time
 *
 * :param millis: number of milliseconds to run the vibration motor.
 */
API(API_VIBRA_VIBRATE, void epic_vibra_vibrate(int millis));

/**
 * Display
 * =======
 * The card10 has an LCD screen that can be accessed from user code.
 *
 * There are two ways to access the display:
 *
 *  - *immediate mode*, where you ask Epicardium to draw shapes and text for
 *    you.  Most functions in this subsection are related to *immediate mode*.
 *  - *framebuffer mode*, where you provide Epicardium with a memory range where
 *    you already drew graphics whichever way you wanted and Epicardium will
 *    copy them to the display.  To use *framebuffer mode*, use the
 *    :c:func:`epic_disp_framebuffer` function.
 */

/** Line-Style */
enum disp_linestyle {
  /** */
  LINESTYLE_FULL = 0,
  /** */
  LINESTYLE_DOTTED = 1
};

/** Fill-Style */
enum disp_fillstyle {
  /** */
  FILLSTYLE_EMPTY = 0,
  /** */
  FILLSTYLE_FILLED = 1
};

/** Width of display in pixels */
#define DISP_WIDTH 160

/** Height of display in pixels */
#define DISP_HEIGHT 80

/**
 * Framebuffer
 *
 * The frambuffer stores pixels as RGB565, but byte swapped.  That is, for every ``(x, y)`` coordinate, there are two ``uint8_t``\ s storing 16 bits of pixel data.
 *
 * .. todo::
 *
 *    Document (x, y) in relation to chirality.
 *
 * **Example**: Fill framebuffer with red
 *
 * .. code-block:: cpp
 *
 * 	union disp_framebuffer fb;
 * 	uint16_t red = 0b1111100000000000;
 * 	for (int y = 0; y < DISP_HEIGHT; y++) {
 * 		for (int x = 0; x < DISP_WIDTH; x++) {
 * 			fb.fb[y][x][0] = red >> 8;
 * 			fb.fb[y][x][1] = red & 0xFF;
 * 		}
 * 	}
 * 	epic_disp_framebuffer(&fb);
 */
union disp_framebuffer {
  /** Coordinate based access (as shown in the example above). */
  uint8_t fb[DISP_HEIGHT][DISP_WIDTH][2];
  /** Raw byte-indexed access. */
  uint8_t raw[DISP_HEIGHT*DISP_WIDTH*2];
};

/**
 * Locks the display.
 *
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_OPEN, int epic_disp_open());

/**
 * Unlocks the display again.
 *
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_CLOSE, int epic_disp_close());

/**
 * Causes the changes that have been written to the framebuffer
 * to be shown on the display
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_UPDATE, int epic_disp_update());

/**
 * Prints a string into the display framebuffer
 *
 * :param posx: x position to print to.
 * :param posy: y position to print to.
 * :param pString: string to print
 * :param fg: foreground color in rgb565
 * :param bg: background color in rgb565
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_PRINT,
    int epic_disp_print(
	    int16_t posx,
	    int16_t posy,
	    const char *pString,
	    uint16_t fg,
	    uint16_t bg)
    );

/*
 * Font Selection
 */
enum disp_font_name {
	DISP_FONT8  = 0,
	DISP_FONT12 = 1,
	DISP_FONT16 = 2,
	DISP_FONT20 = 3,
	DISP_FONT24 = 4,
};

/**
 * Prints a string into the display framebuffer with font type selectable
 *
 * :param fontName: number of font, use FontName enum
 * :param posx: x position to print to.
 * :param posy: y position to print to.
 * :param pString: string to print
 * :param fg: foreground color in rgb565
 * :param bg: background color in rgb565, no background is drawn if bg==fg
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_PRINT_ADV, int epic_disp_print_adv(
	uint8_t font,
	int16_t posx,
	int16_t posy,
	const char *pString,
	uint16_t fg,
	uint16_t bg
));

/**
 * Fills the whole screen with one color
 *
 * :param color: fill color in rgb565
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_CLEAR, int epic_disp_clear(uint16_t color));

/**
 * Draws a pixel on the display
 *
 * :param x: x position;
 * :param y: y position;
 * :param color: pixel color in rgb565
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_PIXEL, int epic_disp_pixel(
	int16_t x, int16_t y, uint16_t color
));

/**
 * Draws a line on the display
 *
 * :param xstart: x starting position
 * :param ystart: y starting position
 * :param xend: x ending position
 * :param yend: y ending position
 * :param color: line color in rgb565
 * :param linestyle: 0 for solid, 1 for dottet (almost no visual difference)
 * :param pixelsize: thickness of the line; 1 <= pixelsize <= 8
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_LINE, int epic_disp_line(
	int16_t xstart,
	int16_t ystart,
	int16_t xend,
	int16_t yend,
	uint16_t color,
	enum disp_linestyle linestyle,
	uint16_t pixelsize
));

/**
 * Draws a rectangle on the display
 *
 * :param xstart: x coordinate of top left corner
 * :param ystart: y coordinate of top left corner
 * :param xend: x coordinate of bottom right corner
 * :param yend: y coordinate of bottom right corner
 * :param color: line color in rgb565
 * :param fillstyle: 0 for empty, 1 for filled
 * :param pixelsize: thickness of the rectangle outline; 1 <= pixelsize <= 8
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_RECT, int epic_disp_rect(
	int16_t xstart,
	int16_t ystart,
	int16_t xend,
	int16_t yend,
	uint16_t color,
	enum disp_fillstyle fillstyle,
	uint16_t pixelsize
));

/**
 * Draws a circle on the display
 *
 * :param x: x coordinate of the center; 0 <= x <= 160
 * :param y: y coordinate of the center; 0 <= y <= 80
 * :param rad: radius of the circle
 * :param color: fill and outline color of the circle (rgb565)
 * :param fillstyle: 0 for empty, 1 for filled
 * :param pixelsize: thickness of the circle outline; 1 <= pixelsize <= 8
 * :return: ``0`` on success or a negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_CIRC, int epic_disp_circ(
	int16_t x,
	int16_t y,
	uint16_t rad,
	uint16_t color,
	enum disp_fillstyle fillstyle,
	uint16_t pixelsize
));

/**
 * Immediately send the contents of a framebuffer to the display. This overrides
 * anything drawn by immediate mode graphics and displayed using ``epic_disp_update``.
 *
 * :param fb: framebuffer to display
 * :return: ``0`` on success or negative value in case of an error:
 *
 *    - ``-EBUSY``: Display was already locked from another task.
 */
API(API_DISP_FRAMEBUFFER, int epic_disp_framebuffer(
	union disp_framebuffer *fb
));


/**
 * Set the backlight brightness.
 *
 * Note that this function does not require acquiring the display.
 *
 * :param brightness: brightness from 0 - 100
 * :return: ``0`` on success or negative value in case of an error
 */
API(API_DISP_BACKLIGHT, int epic_disp_backlight(uint16_t brightness));


/**
 * Start continuous readout of the light sensor. Will read light level
 * at preconfigured interval and make it available via `epic_light_sensor_get()`.
 *
 * If the continuous readout was already running, this function will silently pass.
 *
 *
 * :return: `0` if the start was successful or a negative error value
 *      if an error occured. Possible errors:
 *
 *      - ``-EBUSY``: The timer could not be scheduled.
 */
API(API_LIGHT_SENSOR_RUN, int epic_light_sensor_run());

/**
 * Get the last light level measured by the continuous readout.
 *
 * :param uint16_t* value: where the last light level should be written.
 * :return: `0` if the readout was successful or a negative error
 *      value. Possible errors:
 *
 *      - ``-ENODATA``: Continuous readout not currently running.
 */
API(API_LIGHT_SENSOR_GET, int epic_light_sensor_get(uint16_t* value));


/**
 * Stop continuous readout of the light sensor.
 *
 * If the continuous readout wasn't running, this function will silently pass.
 *
 * :return: `0` if the stop was sucessful or a negative error value
 *      if an error occured. Possible errors:
 *
 *      - ``-EBUSY``: The timer stop could not be scheduled.
 */
API(API_LIGHT_SENSOR_STOP, int epic_light_sensor_stop());

/**
 * Get the light level directly.
 *
 * Each call has an intrinsic delay of about 240us, I recommend another
 * 100-300us delay  between calls. Whether or not the IR LED is fast enough is
 * another issue.
 *
 * :return: Light level
 *
 * .. versionadded:: 1.8
 */
API(API_LIGHT_SENSOR_READ, uint16_t epic_light_sensor_read(void));


/**
 * File
 * ====
 * Except for :c:func:`epic_file_open`, which models C stdio's ``fopen``
 * function, ``close``, ``read`` and ``write`` model `close(2)`_, `read(2)`_ and
 * `write(2)`_.  All file-related functions return >= ``0`` on success and
 * ``-Exyz`` on failure, with error codes from errno.h (``EIO``, ``EINVAL``
 * etc.)
 *
 * .. _close(2): http://man7.org/linux/man-pages/man2/close.2.html
 * .. _read(2): http://man7.org/linux/man-pages/man2/read.2.html
 * .. _write(2): http://man7.org/linux/man-pages/man2/write.2.html
 */

/** */
API(API_FILE_OPEN, int epic_file_open(
	const char* filename, const char* modeString
));

/** */
API(API_FILE_CLOSE, int epic_file_close(int fd));

/** */
API(API_FILE_READ, int epic_file_read(int fd, void* buf, size_t nbytes));

/**
 * Write bytes to a file.
 *
 * :param int fd: Descriptor returned by :c:func:`epic_file_open`.
 * :param void* buf: Data to write.
 * :param size_t nbytes: Number of bytes to write.
 *
 * :return: ``< 0`` on error, ``nbytes`` on success. (Partial writes don't occur on success!)
 *
*/
API(API_FILE_WRITE, int epic_file_write(
	int fd, const void* buf, size_t nbytes
));

/** */
API(API_FILE_FLUSH, int epic_file_flush(int fd));

/** */
API(API_FILE_SEEK, int epic_file_seek(int fd, long offset, int whence));

/** */
API(API_FILE_TELL, int epic_file_tell(int fd));

/** */
enum epic_stat_type {
	/**
	 * Basically ``ENOENT``. Although :c:func:`epic_file_stat` returns an
	 * error for 'none', the type will still be set to none additionally.
	 *
	 * This is also used internally to track open FS objects, where we use
	 * ``EPICSTAT_NONE`` to mark free objects.
	 */
	EPICSTAT_NONE,
	/** normal file */
	EPICSTAT_FILE,
	/** directory */
	EPICSTAT_DIR,
};

/**
 * Maximum length of a path string (=255).
 */
#define EPICSTAT_MAX_PATH        255
/* conveniently the same as FF_MAX_LFN */

/** */
struct epic_stat {
	/** Entity Type: file, directory or none */
	enum epic_stat_type type;

	/*
	 * Note about padding & placement of uint32_t size:
	 *
	 *   To accomodate for future expansion, we want padding at the end of
	 *   this struct. Since sizeof(enum epic_stat_type) can not be assumed
	 *   to be have a certain size, we're placing uint32_t size here so we
	 *   can be sure it will be at offset 4, and therefore the layout of the
	 *   other fields is predictable.
	 */

	/** Size in bytes. */
	uint32_t size;

	/** File Name. */
	char name[EPICSTAT_MAX_PATH + 1];
	uint8_t _reserved[12];
};

/**
 * stat path
 *
 * :param char* filename: path to stat
 * :param epic_stat* stat: pointer to result
 *
 * :return: ``0`` on success, negative on error
 */
API(API_FILE_STAT, int epic_file_stat(
	const char* path, struct epic_stat* stat
));

/**
 * Open a directory, for enumerating its contents.
 *
 * Use :c:func:`epic_file_readdir` to iterate over the directories entries.
 *
 * **Example**:
 *
 * .. code-block:: cpp
 *
 *    #include "epicardium.h"
 *
 *    int fd = epic_file_opendir("/path/to/dir");
 *
 *    struct epic_stat entry;
 *    for (;;) {
 *            epic_file_readdir(fd, &entry);
 *
 *            if (entry.type == EPICSTAT_NONE) {
 *                    // End
 *                    break;
 *            }
 *
 *            printf("%s\n", entry.name);
 *    }
 *
 *    epic_file_close(fd);
 *
 * :param char* path: Directory to open.
 *
 * :return: ``> 0`` on success, negative on error
 */
API(API_FILE_OPENDIR, int epic_file_opendir(const char* path));

/**
 * Read one entry from a directory.
 *
 * Call :c:func:`epic_file_readdir` multiple times to iterate over all entries
 * of a directory.  The end of the entry list is marked by returning
 * :c:data:`EPICSTAT_NONE` as the :c:member:`epic_stat.type`.
 *
 * :param int fd: Descriptor returned by :c:func:`epic_file_opendir`.
 * :param epic_stat* stat: Pointer where to store the result.  Pass NULL to
 *    reset iteration offset of ``fd`` back to the beginning.
 *
 * :return: ``0`` on success, negative on error
 */
API(API_FILE_READDIR, int epic_file_readdir(int fd, struct epic_stat* stat));

/**
 * Unlink (remove) a file.
 *
 * :param char* path: file to delete
 *
 * :return: ``0`` on success, negative on error
 */
API(API_FILE_UNLINK, int epic_file_unlink(const char* path));

/**
 * Rename a file or directory.
 *
 * :param char* oldp: old name
 * :param char* newp: new name
 *
 * :return: ``0`` on success, negative on error
 */
API(API_FILE_RENAME, int epic_file_rename(const char *oldp, const char* newp));

/**
 * Create directory in CWD
 *
 * :param char* dirname: directory name
 *
 * :return: ``0`` on success, negative on error
 */
API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname));

/**
 * RTC
 * ===
 */

/**
 * Get the monotonic time in seconds.
 *
 * :return: monotonic time in seconds
 *
 * .. versionadded:: 1.11
 */
API(API_RTC_GET_MONOTONIC_SECONDS,
	uint32_t epic_rtc_get_monotonic_seconds(void)
);

/**
 * Get the monotonic time in ms.
 *
 * :return: monotonic time in milliseconds
 *
 * .. versionadded:: 1.11
 */
API(API_RTC_GET_MONOTONIC_MILLISECONDS,
	uint64_t epic_rtc_get_monotonic_milliseconds(void)
);

/**
 * Read the current RTC value.
 *
 * :return: Unix time in seconds
 */
API(API_RTC_GET_SECONDS, uint32_t epic_rtc_get_seconds(void));

/**
 * Read the current RTC value in ms.
 *
 * :return: Unix time in milliseconds
 */
API(API_RTC_GET_MILLISECONDS, uint64_t epic_rtc_get_milliseconds(void));

/**
 * Sets the current RTC time in milliseconds
 */
API(API_RTC_SET_MILLISECONDS, void epic_rtc_set_milliseconds(
	uint64_t milliseconds
));

/**
 * Schedule the RTC alarm for the given timestamp.
 *
 * :param uint32_t timestamp: When to schedule the IRQ
 * :return: `0` on success or a negative value if an error occured. Possible
 *    errors:
 *
 *    - ``-EINVAL``: RTC is in a bad state
 */
API(API_RTC_SCHEDULE_ALARM, int epic_rtc_schedule_alarm(uint32_t timestamp));

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_RTC_ALARM`
 *
 * ``epic_isr_rtc_alarm()`` is called when the RTC alarm triggers.  The RTC alarm
 * can be scheduled using :c:func:`epic_rtc_schedule_alarm`.
 */
API_ISR(EPIC_INT_RTC_ALARM, epic_isr_rtc_alarm);

/**
 * TRNG
 * ====
 */

/**
 * Read random bytes from the TRNG.
 *
 * :param uint8_t * dest: Destination buffer
 * :param size: Number of bytes to read.
 * :return: `0` on success or a negative value if an error occured. Possible
 *    errors:
 *
 *    - ``-EFAULT``: Invalid destination address.
 */
API(API_TRNG_READ, int epic_trng_read(uint8_t *dest, size_t size));

/**
 * MAX30001
 * ========
 */

/**
 * Configuration for a MAX30001 sensor.
 *
 * This struct is used when enabling the sensor using
 * :c:func:`epic_max30001_enable_sensor`.
 */
struct max30001_sensor_config {
	/**
	 * Number of samples Epicardium should keep for this sensor.  Do not set
	 * this number too high as the sample buffer will eat RAM.
	 */
	size_t sample_buffer_len;
	/**
	 * Sample rate for the sensor in Hz.
	 */
	uint16_t sample_rate;

	/**
	 * Set to true if the second lead comes from USB-C
	 */
	bool usb;

	/**
	 * Set to true if the interal lead bias of the MAX30001 is to be used.
	 */
	bool bias;

	/** Always zero. Reserved for future parameters. */
	uint8_t _padding[8];
};

/**
 * Enable a MAX30001 ECG sensor.
 *
 * Calling this function will instruct the MAX30001 to collect data for this
 * sensor.  You can then retrieve the samples using :c:func:`epic_stream_read`.
 *
 * :param max30001_sensor_config* config: Configuration for this sensor.
 * :returns: A sensor descriptor which can be used with
 *    :c:func:`epic_stream_read` or a negative error value:
 *
 *    - ``-EBUSY``:  The MAX30001 driver is currently busy with other tasks and
 *      could not be acquired for enabling a sensor.
 *
 * .. versionadded:: 1.6
 */
API(API_MAX30001_ENABLE, int epic_max30001_enable_sensor(
	struct max30001_sensor_config *config
));

/**
 * Disable MAX30001
 *
 * .. versionadded:: 1.6
 */
API(API_MAX30001_DISABLE, int epic_max30001_disable_sensor());

/**
 * **Interrupt Service Routine** for :c:data:`EPIC_INT_MAX30001_ECG`
 *
 * This interrupt handler is called whenever the MAX30001 ECG has new data
 * available.
 */
API_ISR(EPIC_INT_MAX30001_ECG, epic_isr_max30001_ecg);

/**
 * USB
 * ===
 */

/**
 * De-initialize the currently configured USB device (if any)
 *
 */
API(API_USB_SHUTDOWN, int epic_usb_shutdown(void));

/**
 * Configure the USB peripheral to export the internal FLASH
 * as a Mass Storage device.
 */
API(API_USB_STORAGE, int epic_usb_storage(void));

/**
 * Configure the USB peripheral to provide card10's stdin/stdout
 * on a USB CDC-ACM device.
 */
API(API_USB_CDCACM, int epic_usb_cdcacm(void));

/**
 * WS2812
 * ======
 */

/**
 * Takes a gpio pin specified with the gpio module and transmits
 * the led data. The format `GG:RR:BB` is expected.
 *
 * :param uint8_t pin: The gpio pin to be used for data.
 * :param uint8_t * pixels: The buffer, in which the pixel data is stored.
 * :param uint32_t n_bytes: The size of the buffer.
 *
 * .. versionadded:: 1.10
 */
API(API_WS2812_WRITE, void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes));


/**
 * Configuration
 * =============
 */

/**
 * Read an integer from the configuration file
 *
 * :param char* key: Name of the option to read
 * :param int* value: Place to read the value into
 * :return: `0` on success or a negative value if an error occured. Possible
 *    errors:
 *
 *    - ``-ENOENT``: Value can not be read
 *
 * .. versionadded:: 1.13
 */
API(API_CONFIG_GET_INTEGER, int epic_config_get_integer(const char *key, int *value));

/**
 * Read a boolean from the configuration file
 *
 * :param char* key: Name of the option to read
 * :param bool* value: Place to read the value into
 * :return: `0` on success or a negative value if an error occured. Possible
 *    errors:
 *
 *    - ``-ENOENT``: Value can not be read
 *
 * .. versionadded:: 1.13
 */
API(API_CONFIG_GET_BOOLEAN, int epic_config_get_boolean(const char *key, bool *value));

/**
 * Read a string from the configuration file.
 *
 * If the buffer supplied is too small for the config option,
 * no error is reported and the first `buf_len - 1` characters
 * are returned (0 terminated).
 *
 * :param char* key: Name of the option to read
 * :param char* buf: Place to read the string into
 * :param size_t buf_len: Size of the provided buffer
 * :return: `0` on success or a negative value if an error occured. Possible
 *    errors:
 *
 *    - ``-ENOENT``: Value can not be read
 *
 * .. versionadded:: 1.13
 */
API(API_CONFIG_GET_STRING, int epic_config_get_string(const char *key, char *buf, size_t buf_len));


#endif /* _EPICARDIUM_H */