/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

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

#include <STM32Headers.h>

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

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

  virtual void onReceiveData(std::span<const uint8_t> buf);     // Read data

  size_t write(const std::span<const uint8_t> buffer) override;
  size_t read(const std::span<uint8_t> buffer) override;

  bool isDataAvailable() const override;

  protected:
  USBD_HandleTypeDef                      *m_usbdHdl = nullptr;

  sdi_toolBox::generic::CircularBuffer<uint8_t, QUEUE_SIZE> m_rxBuffer;
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Bus configuration */
template<size_t QUEUE_SIZE>
inline void HIDBus<QUEUE_SIZE>::configure(USBD_HandleTypeDef *usbdHdl)
{
  m_usbdHdl = usbdHdl;
}
//--------------------------------------------------------------
/* Returns bus handle */
template<size_t QUEUE_SIZE>
inline USBD_HandleTypeDef *HIDBus<QUEUE_SIZE>::getBus() const
{
  return m_usbdHdl;
}
//--------------------------------------------------------------
/* Read data from IT and restart the reading process */
template<size_t QUEUE_SIZE>
inline void HIDBus<QUEUE_SIZE>::onReceiveData(const std::span<const uint8_t> buf)
{
  for (const auto &c : buf)
    m_rxBuffer.push(c);
}
//--------------------------------------------------------------
template<size_t QUEUE_SIZE>
size_t HIDBus<QUEUE_SIZE>::write(const std::span<const uint8_t> buffer)
{
  USBD_CUSTOM_HID_SendReport(m_usbdHdl, const_cast<uint8_t*>(buffer.data()), buffer.size());

  return 0;//TODO: correct return value
}

template<size_t QUEUE_SIZE>
size_t HIDBus<QUEUE_SIZE>::read(const std::span<uint8_t> buffer)
{
  CriticalSection criticalSection;
  for (size_t i = 0; i < buffer.size(); i++)
  {
    const auto data = m_rxBuffer.pop();
    if (!data)
      return i;
    buffer[i] = *data;
  }
  return buffer.size();
}

template<size_t QUEUE_SIZE>
bool HIDBus<QUEUE_SIZE>::isDataAvailable() const
{
  CriticalSection criticalSection;
  return !m_rxBuffer.isEmpty();
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::stm32
