//-----------------------------------------------------------------------------
///
/// \brief  Class representing an GPIO
///
/// \date   20121107
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


//---Includes------------------------------------------------------------------


#include <biwak/gpio.hpp>
#include <biwak/rcc.h>        // rccGpioClockEnable()
#include <biwak/exception.h>
#include <HALWrapper/stm32_rcc.h>
#include <HALWrapper/stm32_gpio.h>
#include <biwak/biwak.h>      // exception
#include <biwak/timer.hpp>      // usleep


//---Implementation------------------------------------------------------------


#if defined CONFIG_DIRECT_GPIO_INSTANCE
   #define peripheralEnumToPeripheral(periphery)      periphery
#endif

static_assert ( GPIO_PIN_SET == 1, "" );
static_assert ( GPIO_PIN_RESET == 0, "" );

#define pinStateEnumToPinSTate( _value ) ((GPIO_PinState)_value)

struct SGpioMode
{
   const uint32_t mode;
   // its not packed; increasing to int does not help
   const uint8_t speed;
   const uint8_t pp;
   //const uint8_t af;
}; // __attribute__ ( ( packed ) ); // packed increases binary size


#if ! defined ( GPIO_SPEED_FREQ_VERY_HIGH )
   #define GPIO_SPEED_FREQ_VERY_HIGH   GPIO_SPEED_FREQ_HIGH
#endif


#if defined ( STM32F1 )
/// \brief  Configures an GPIO
void remapF1( uint32_t GPIO_AF )
{
   // STM32F1 does not support the AF mechanism (yet? HAL?).
   // AF stuff has to be remapped manually.
   // We configure AF for every pin. Its just a Register configuration so its
   // not a very big problem.
   switch(GPIO_AF)
   {
      case(0):
         break;
      case(GPIO_AFx_SPI1_REMAP):
         // AF1_SPI1: Pins PA15, PB3, PB4, PB5
         // PA15: JTDI, PB3: JTDO/TRACESWO, PB4: NJTRST
         //while(1){};
         __HAL_AFIO_REMAP_SWJ_NOJTAG();
         __HAL_AFIO_REMAP_SWJ_NONJTRST();
         // No good idea!
         //__HAL_AFIO_REMAP_SWJ_DISABLE();
         //__HAL_AFIO_REMAP_TIM3_DISABLE();
         __HAL_AFIO_REMAP_SPI1_ENABLE();
         break;
      case(GPIO_AFx_SPI1_NOREMAP):
         // AF1_SPI1: Pins PA15, PB3, PB4, PB5
         // PA15: JTDI, PB3: JTDO/TRACESWO, PB4: NJTRST
         //while(1){};
         //__HAL_AFIO_REMAP_SWJ_NOJTAG();
         //__HAL_AFIO_REMAP_SWJ_NONJTRST();
         // No good idea!
         //__HAL_AFIO_REMAP_SWJ_DISABLE();
         //__HAL_AFIO_REMAP_TIM3_DISABLE();
         //__HAL_AFIO_REMAP_SPI1_DISABLE();
         break;
      case(GPIO_AFx_USART2_DEFAULT):
         __HAL_AFIO_REMAP_USART2_DISABLE();
         __HAL_AFIO_REMAP_TIM2_ENABLE();
         break;
      case(GPIO_AFx_I2C1_DEFAULT):
         #if defined(AFIO_MAPR_TIM4_REMAP)
         __HAL_AFIO_REMAP_TIM4_DISABLE();
         #endif
         __HAL_AFIO_REMAP_I2C1_DISABLE();
         //exception("Foo", 0);
         break;
      case(GPIO_AFx_SPI2_DEFAULT):
         break;
      case(GPIO_AFx_CAN1_DEFAULT):
         // CASE 1: CAN_RX mapped to PA11, CAN_TX mapped to PA12
         __HAL_AFIO_REMAP_CAN1_1();
         //__HAL_AFIO_REMAP_TIM1_DISABLE();
         //__HAL_AFIO_REMAP_USB1_DISABLE();
         break;
      case(GPIO_AFx_CAN1_2):
         // CASE 2: CAN_RX mapped to PB8,  CAN_TX mapped to PB9 (not available on 36-pin package)
         __HAL_AFIO_REMAP_CAN1_2();
         //__HAL_AFIO_REMAP_TIM1_DISABLE();
         break;
      #if defined(GPIO_AF10_TIM)
      case(GPIO_AF10_TIM):
         __HAL_AFIO_REMAP_CAN1_2();
         //__HAL_AFIO_REMAP_TIM1_DISABLE();
         break;
      #endif
      default:
         // Do nothing, settings already ok by default
         // Unknown AF
         exception();
         break;
   }

   return;
};
#endif // ? STM32F1


/// \brief  Le contructeur
///
CGpio::CGpio(EGpioPeripheral eGpioPeripheral, EGpioPin eGpioPin
             , const SGpioMode &gpioMode /*=gpioTypeInput*/, int altFunc  )
   :m_eGpioPeripheral( eGpioPeripheral )
   ,m_eGpioPin( eGpioPin )
{
   init( eGpioPeripheral, eGpioPin, gpioMode, altFunc );
}

int CGpio::init(EGpioPeripheral eGpioPeripheral, EGpioPin eGpioPin
             , const SGpioMode &gpioMode /*=gpioTypeInput*/, int altFunc  ) /* static */
{
   #if defined (BIWAK_AUTOMATIC_RCC_ENABLE)
      rccGpioClockEnable(GPIOx);
   #endif

   #if defined ( STM32F1 )
      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wvolatile"
      __HAL_RCC_AFIO_CLK_ENABLE();
      #pragma GCC diagnostic pop

      remapF1( altFunc );
   #endif

   if(altFunc>=0x0)
   {
      #if 0
      GPIO_InitTypeDef init{ .Pin = pinEnumToDefine( _eGpioPin )
         , .Mode=gpioMode.mode
         , .Pull=gpioMode.pp
         , .Speed= gpioMode.speed
         #if ! defined ( STM32F1 )
            , .Alternate = (uint32_t)altFunc
         #endif
      };

      HAL_GPIO_Init( peripheralEnumToPeripheral(_eGpioPeripheral),
                &init );
      #else
      setMode( eGpioPeripheral, eGpioPin, gpioMode, altFunc);
      #endif
   }
   else
   {
      // Skip initialization; e.g. for USB
   }

   return(0);
}


// Init the mode of an GPIO.
void CGpio::setMode( EGpioPeripheral eGpioPeripheral, EGpioPin eGpioPin,
               const SGpioMode& gpioMode, int altFunc )
{
   #if defined ( STM32F1 )
   (void)altFunc;
   #endif

   GPIO_InitTypeDef gpioInit {
      .Pin{ pinEnumToDefine( eGpioPin ) },
      .Mode { gpioMode.mode },
      .Pull { gpioMode.pp },
      .Speed { gpioMode.speed },
   #if ! defined ( STM32F1 )
      .Alternate { (uint32_t)altFunc},
   #endif
   };

   HAL_GPIO_Init( peripheralEnumToPeripheral( eGpioPeripheral ),
             &gpioInit );
};


// Change the mode of an GPIO. This makes only sense for
// non-alternate-functions. E.g. an input when you want to change the slopes
// you want to get interrupted to
void CGpio::setMode( const SGpioMode& gpioMode )
{
   setMode( m_eGpioPeripheral, m_eGpioPin,
                  gpioMode, 0 );

};


CGpio::~CGpio()
{
   HAL_GPIO_DeInit( peripheralEnumToPeripheral( m_eGpioPeripheral )
                   , pinEnumToDefine( m_eGpioPin ) );
}

void CGpio::setValue(bool _value) const
{
   HAL_GPIO_WritePin(
         peripheralEnumToPeripheral( m_eGpioPeripheral )
         , pinEnumToDefine( m_eGpioPin ) , pinStateEnumToPinSTate( _value ) );
   return;
}

bool CGpio::getValue() const
{
   return( HAL_GPIO_ReadPin( peripheralEnumToPeripheral( m_eGpioPeripheral ),
                             pinEnumToDefine( m_eGpioPin ) ) );
}


//---fin-----------------------------------------------------------------------
