/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include "../generic/iGPIO.h"

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

namespace sdi_uToolBox::stm32
{
//--------------------------------------------------------------
class GPIO : public sdi_uToolBox::generic::IGPIO
{
  public:
  struct GpioDefs
  {
    GPIO_TypeDef *port = nullptr;
    uint16_t      pin  = 0;
    PinType       type = PinType::Invalid;
  };

  public:
  void                        configure(const GpioDefs &gpioDefs);     // GPIO configuration
  [[nodiscard]] GPIO_TypeDef *getPort() const;                         // Returns IO port
  [[nodiscard]] uint16_t      getPin() const;                          // Returns IO pin
  [[nodiscard]] PinType       getType() const;                         // Returns IO type

  void               set(bool enable = true) const override;     // Sets/Resets IO state
  [[nodiscard]] bool get() const override;                       // Returns IO state
  void               toggle() const override;                    // Flips IO state

  void        enableEvent(bool enable = true);     // Adds / remove GPIO handle in the indexed pin event handle list
  static void on_GpioIT(uint16_t pin);             // Checks GPIO IT

  protected:
  GpioDefs m_gpioDefs;

  static inline std::array<GPIO *, 16> m_ITListHdl{};     // List of indexed pin IT handles
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* GPIO configuration */
inline void GPIO::configure(const GpioDefs &gpioDefs)
{
  m_gpioDefs = gpioDefs;
}
//--------------------------------------------------------------
/* Return IO port */
inline GPIO_TypeDef *GPIO::getPort() const
{
  return m_gpioDefs.port;
}
//--------------------------------------------------------------
/* Return IO pin */
inline uint16_t GPIO::getPin() const
{
  return m_gpioDefs.pin;
}
//--------------------------------------------------------------
/* Return IO type */
inline GPIO::PinType GPIO::getType() const
{
  return m_gpioDefs.type;
}
//--------------------------------------------------------------
/* Set/Reset logical IO state */
inline void GPIO::set(bool enable) const
{
  if (m_gpioDefs.type == PinType::ReversedOutput)
    enable = !enable;

  HAL_GPIO_WritePin(m_gpioDefs.port,
                    m_gpioDefs.pin,
                    (enable) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
//--------------------------------------------------------------
/* Return logical IO state */
inline bool GPIO::get() const
{
  const auto state = (GPIO_PIN_SET == HAL_GPIO_ReadPin(m_gpioDefs.port, m_gpioDefs.pin));

  if (m_gpioDefs.type == PinType::ReversedOutput || m_gpioDefs.type == PinType::ReversedInput)
    return !state;

  return state;
}
//--------------------------------------------------------------
/* Flip IO state */
inline void GPIO::toggle() const
{
  HAL_GPIO_TogglePin(m_gpioDefs.port, m_gpioDefs.pin);
}
//--------------------------------------------------------------
/* Add / remove GPIO handle in the indexed pin event handle list */
inline void GPIO::enableEvent(bool enable)
{
  const size_t pinIndex = __builtin_clz(m_gpioDefs.pin);
  if (pinIndex >= m_ITListHdl.size())
    return;

  if (enable)
    m_ITListHdl[pinIndex] = this;
  else
    m_ITListHdl[pinIndex] = nullptr;
}
//--------------------------------------------------------------
/* Check GPIO IT
 */
inline void GPIO::on_GpioIT(uint16_t pin)
{
  const size_t pinIndex = __builtin_clz(pin);
  if (pinIndex < m_ITListHdl.size())
  {
    if (m_ITListHdl[pinIndex] && m_ITListHdl[pinIndex]->getPin() == pin)
      m_ITListHdl[pinIndex]->m_itCallback();
  }
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::stm32
