/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include "../generic/iI2CBus.h"

#include <STM32Headers.h>

namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
class I2CBus : public sdi_uToolBox::generic::II2CBus
{
  public:
  void                             configure(I2C_HandleTypeDef *i2cHdl,     // Bus configuration
                                             const uint32_t     delay = HAL_MAX_DELAY);
  [[nodiscard]] I2C_HandleTypeDef *getBus() const;       // Returns bus handle
  [[nodiscard]] uint32_t           getDelay() const;     // Returns timeout delay

  I2CStatus write(const uint8_t chipAddress, const std::span<const uint8_t> &buffer) override;     // Write data to I2C device
  I2CStatus read(const uint8_t chipAddress, const std::span<uint8_t> &buffer) override;            // Read data from I2C device

  I2CStatus isDeviceReady(const uint8_t chipAddress) override;     // Check device readiness

  protected:
  I2C_HandleTypeDef *m_i2cHandle = nullptr;
  uint32_t           m_delay     = 0;
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Bus configuration */
inline void I2CBus::configure(I2C_HandleTypeDef *i2cHdl, const uint32_t delay)
{
  m_i2cHandle = i2cHdl;
  m_delay     = delay;
}
//--------------------------------------------------------------
/* Returns bus handle */
inline I2C_HandleTypeDef *I2CBus::getBus() const
{
  return m_i2cHandle;
}
//--------------------------------------------------------------
/* Return IO pin */
inline uint32_t I2CBus::getDelay() const
{
  return m_delay;
}
//--------------------------------------------------------------
/* Write data to I2C device */
inline I2CBus::I2CStatus I2CBus::write(const uint8_t chipAddress, const std::span<const uint8_t> &buffer)
{
  const auto ret = HAL_I2C_Master_Transmit(m_i2cHandle,
                                           chipAddress << 1,
                                           const_cast<uint8_t *>(buffer.data()),
                                           buffer.size(),
                                           m_delay);

  if (ret == HAL_OK)
    return I2CStatus::Ok;
  else if (ret == HAL_BUSY)
    return I2CStatus::Busy;
  else if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
    return I2CStatus::Timeout;
  else
    return I2CStatus::Error;
}
//--------------------------------------------------------------
/* Read data from I2C device */
inline I2CBus::I2CStatus I2CBus::read(const uint8_t chipAddress, const std::span<uint8_t> &buffer)
{
  const auto ret = HAL_I2C_Master_Receive(m_i2cHandle,
                                          chipAddress << 1,
                                          buffer.data(),
                                          buffer.size(),
                                          m_delay);

  if (ret == HAL_OK)
    return I2CStatus::Ok;
  else if (ret == HAL_BUSY)
    return I2CStatus::Busy;
  else if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
    return I2CStatus::Timeout;
  else
    return I2CStatus::Error;
}
//--------------------------------------------------------------
/* Check device readiness */
inline I2CBus::I2CStatus I2CBus::isDeviceReady(const uint8_t chipAddress)
{
  const auto ret = HAL_I2C_IsDeviceReady(m_i2cHandle,
                                         chipAddress << 1,
                                         5,
                                         m_delay);

  if (ret == HAL_OK)
    return I2CStatus::Ok;
  else if (ret == HAL_BUSY)
    return I2CStatus::Busy;
  else if (ret == HAL_ERROR && m_i2cHandle->ErrorCode == HAL_I2C_ERROR_TIMEOUT)
    return I2CStatus::Timeout;
  else
    return I2CStatus::Error;
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::stm32
