/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include "../generic/iStream.h"

#include <STM32Headers.h>
#include <array>
#include <cstring>

namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
class UARTBus : public sdi_uToolBox::generic::IStream
{
  public:
  UARTBus()                                    = default;     // Constructor
  ~UARTBus() override                          = default;     // Destructor
  UARTBus(const UARTBus &other)                = delete;      // Copy constructor
  UARTBus(UARTBus &&UARTBus) noexcept          = delete;      // Move constructor
  UARTBus &operator=(const UARTBus &other)     = delete;      // Copy assignment
  UARTBus &operator=(UARTBus &&other) noexcept = delete;      // Move assignment

  void                              configure(UART_HandleTypeDef *uartHdl);     // Bus configuration
  [[nodiscard]] UART_HandleTypeDef *getBus() const;                             // Returns bus handle

  void onReceiveData_fromISR(const size_t size);     // Read data from IT and restart the reading process

  protected:
  UART_HandleTypeDef                      *m_uartHdl = nullptr;
  std::array<uint8_t, UART_RX_BUFFER_SIZE> m_uartRXBuffer;
  std::array<uint8_t, UART_TX_BUFFER_SIZE> m_uartTXBuffer;

  private:
  virtual void initDevice()                                                  = 0;     // Device initialization
  virtual bool startReceiving()                                              = 0;     // Start specific receiving method (IT, DMA, ...)
  virtual void receiveData_fromISR(const uint8_t *buffer, const size_t size) = 0;     // Receive data from interuption
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Bus configuration */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
inline void UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::configure(UART_HandleTypeDef *uartHdl)
{
  m_uartHdl = uartHdl;
}
//--------------------------------------------------------------
/* Returns bus handle */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
inline UART_HandleTypeDef *UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::getBus() const
{
  return m_uartHdl;
}
//--------------------------------------------------------------
/* Read data from IT and restart the reading process */
template<size_t UART_RX_BUFFER_SIZE, size_t UART_TX_BUFFER_SIZE>
inline void UARTBus<UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE>::onReceiveData_fromISR(const size_t size)
{
  // Read data
  receiveData_fromISR(m_uartRXBuffer.data(), size);

  // Restart receiving process
  if (!startReceiving())
  {
    // Quick fix in case of failure to start the receiving process
    initDevice();
    startReceiving();
  }
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::stm32
