/***************************************************************************//**
 * @file
 * @brief IO Stream.
 *******************************************************************************
 * # License
 * <b>Copyright 2019 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * SPDX-License-Identifier: Zlib
 *
 * The licensor of this software is Silicon Laboratories Inc.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 ******************************************************************************/

#ifndef SL_IOSTREAM_H
#define SL_IOSTREAM_H

#if defined(SL_COMPONENT_CATALOG_PRESENT)
#include "sl_component_catalog.h"
#endif
#include "sl_enum.h"
#include "sl_status.h"

#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************//**
 * @addtogroup iostream I/O Stream
 * @brief I/O Stream can be used to read/write different formats of data to various streams.
 *        The source files for I/O Stream platform software module are present under platform/services/iostream.
 * @details
 * ## Overview
 *
 *   I/O Stream is a platform module software that provides Input/Output functionalities
 *   by creating streams. Streams are abstractions allowing a uniform way to read/write
 *   data regardless of the physical communication interface.
 *
 *   I/O Stream offers many interfaces, see submodules for a list of all types available
 *   and their specificities.You can load multiple streams in the project and you can
 *   select the interface that must be used at runtime.Some interface type can also
 *   be instantiated, meaning that you can have multiple instances of an interface
 *   type which will be normally bound to a hardware peripheral.
 *
 * ## Initialization
 *
 *   The I/O Stream core doesn't require any initialization. Instead each stream type has
 *   their own initialization and their own configuration. See I/O Stream specific type
 *   to know more about how to initialize a stream.
 *
 *   Note that most stream will set itself as the default stream during their initialization.
 *   Thus the initial default stream will be the last stream initialized.
 *
 * ## Default system-wide stream
 *
 *   Multiple streams can be initialized in your application and you can configure a default
 *   stream that must be used when no stream is specified. Also note that the default stream
 *   will be used when calling printf and you can change the default stream at runtime.
 *   The following defines should be used for the default stream:
 *
 *      SL_IOSTREAM_STDIN
 *      SL_IOSTREAM_STDOUT
 *      SL_IOSTREAM_STDERR
 *
 * ## RTOS - Task's default stream
 *
 *   In the case of an RTOS environment, each task can set its own stream. By default, the task
 *   stream will be set to the system_wide default stream. From your task, you can change the
 *   default stream assigned to your task without affecting the other tasks' stream.
 *
 * ## Printf
 *
 *   I/O Stream provides third-party printf integrations. It can work with toolchain implementation
 *   or with the tiny printf implementation for embedded system. The printf API doesn't have an
 *   argument for specifying the stream to be used, so I/O Stream provides a printf API that takes
 *   a stream as an argument and calls the configured third-party implementation of printf.
 *
 * @{
 ******************************************************************************/

/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN
#define SL_IOSTREAM_STDIN 0  ///< Default input stream
#define SL_IOSTREAM_STDOUT 0 ///< Default output stream
#define SL_IOSTREAM_STDERR 0 ///< Default error stream
/// @endcond

// -----------------------------------------------------------------------------
// Data Types

/// @brief Struct representing iostream operations.
typedef struct {
  void *context;                                                                                ///< context
  sl_status_t (*write)(void *context, const void *buffer, size_t buffer_length);                ///< write
  sl_status_t (*read)(void *context, void *buffer, size_t buffer_length, size_t *bytes_read);   ///< read
} sl_iostream_t;

/// @brief Enumeration representing the possible types of iostream instances.
SL_ENUM(sl_iostream_type_t){
  SL_IOSTREAM_TYPE_SWO = 0,              ///< SWO Instance
  SL_IOSTREAM_TYPE_RTT = 1,              ///< RTT Instance
  SL_IOSTREAM_TYPE_UART = 2,             ///< USART Instance
  SL_IOSTREAM_TYPE_VUART = 3,            ///< Vuart
  SL_IOSTREAM_TYPE_DEBUG_OUTPUT = 4,     ///< Backchannel output Instance Type
  SL_IOSTREAM_TYPE_LOOPBACK = 5,         ///< Loopback Instance
  SL_IOSTREAM_TYPE_UNDEFINED = 6,        ///< Undefined Instance Type
};

/// @brief Struct representing an I/O Stream instance.
typedef struct {
  sl_iostream_t *handle;        ///< iostream instance handle.
  char *name;                   ///< iostream instance name.
  sl_iostream_type_t type;      ///< iostream instance type.
  uint8_t periph_id;            ///< iostream peripheral id.
  sl_status_t (*init)(void);    ///< iostream instance init function.
} sl_iostream_instance_info_t;

/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN
// Special stream to be used when you want to avoid printing anything
extern sl_iostream_t sl_iostream_null;
/// @endcond

// -----------------------------------------------------------------------------
// Prototypes

/***************************************************************************//**
 * Set the stream as default I/O Stream.
 *
 * @param[in] stream   I/O Stream to set as default.
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_set_default(sl_iostream_t *stream);

/***************************************************************************//**
 * Get the default I/O Stream configured.
 *
 * @return  Status result
 ******************************************************************************/
sl_iostream_t *sl_iostream_get_default(void);

/***************************************************************************//**
 * Configure the systemwide default stream.
 *
 * @param[in] stream  I/O Stream to be used.
 *
 * @return  Status result
 ******************************************************************************/
#if defined(SL_CATALOG_KERNEL_PRESENT) && !defined(SL_IOSTREAM_FORCE_BAREMETAL)
sl_status_t sl_iostream_set_system_default(sl_iostream_t *stream);
#else
#define sl_iostream_set_system_default sl_iostream_set_default
#endif

/***************************************************************************//**
 * Output data on a stream.
 *
 * @param[in] stream          I/O Stream to be used.
 *                              SL_IOSTREAM_STDOUT;           Default output stream will be used.
 *                              Pointer to specific stream;   Specific stream will be used.
 *
 * @param[in] buffer          Buffer that contains the data to output.
 *
 * @param[in] buffer_length   Data length contained in the buffer.
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_write(sl_iostream_t *stream,
                              const void *buffer,
                              size_t buffer_length);

/***************************************************************************//**
 * Get data from a stream.
 *
 * @param[in]  stream          I/O Stream to be used.
 *                               SL_IOSTREAM_STDOUT;           Default output stream will be used.
 *                               Pointer to specific stream;   Specific stream will be used.
 *
 * @param[out] buffer          Buffer that contains the data to output.
 *
 * @param[in]  buffer_length   Data length contained in the buffer.
 *
 * @param[out] bytes_read      Data length copied to the buffer.
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_read(sl_iostream_t *stream,
                             void *buffer,
                             size_t buffer_length,
                             size_t *bytes_read);

/***************************************************************************//**
 * Print a character on stream.
 *
 * @param[in] stream  I/O Stream to be used:
 *                      SL_IOSTREAM_STDOUT;           Default output stream will be used.
 *                      SL_IOSTREAM_STDERR;           Default error output stream will be used.
 *                      Pointer to specific stream;   Specific stream will be used.
 *
 * @param[in] c       Character to print
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_putchar(sl_iostream_t *stream,
                                char c);

/***************************************************************************//**
 * Print a character on stream.
 *
 * @param[in] stream  I/O Stream to be used.
 *                      SL_IOSTREAM_STDIN;            Default input stream will be used.
 *                      Pointer to specific stream;   Specific stream will be used.
 *
 * @param[out] c      Pointer to variable that will receive the character.
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_getchar(sl_iostream_t *stream,
                                char *c);

/***************************************************************************//**
 * Print a formated string on stream.
 *
 * @param[in] stream  I/O Stream to be used:
 *                      SL_IOSTREAM_STDOUT;           Default output stream will be used.
 *                      SL_IOSTREAM_STDERR;           Default error output stream will be used.
 *                      Pointer to specific stream;   Specific stream will be used.
 *
 * @param[in] format  String that contains the text to be written.
 *
 * @param[in] argp    A value identifying a variable arguments list.
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_vprintf(sl_iostream_t *stream,
                                const char *format,
                                va_list argp);

#if defined(__GNUC__)
__attribute__((format(printf, 2, 3)))
#endif

/***************************************************************************//**
 * Print a formated string on stream.
 *
 * @param[in] stream  I/O Stream to be used:
 *                      SL_IOSTREAM_STDOUT;           Default output stream will be used.
 *                      SL_IOSTREAM_STDERR;           Default error output stream will be used.
 *                      Pointer to specific stream;   Specific stream will be used.
 *
 * @param[in] format  String that contains the text to be written.
 *
 * @param[in] ...     Additional arguments.
 *
 * @return  Status result
 ******************************************************************************/
sl_status_t sl_iostream_printf(sl_iostream_t *stream,
                               const char *format,
                               ...);

/** @} (end addtogroup iostream) */

#ifdef __cplusplus
}
#endif

#endif // SL_IOSTREAM_H
