//-----------------------------------------------------------------------------
///
/// \brief  Just a simple exti
///
///         THere is a virtual function that can be overloaded as ISR.
///
/// \date   20121107
/// \author Maximilian Seesslen <development@seesslen.net>
///
//-----------------------------------------------------------------------------


//---Documentation-------------------------------------------------------------


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


//---Own------------------------------

#include <biwak/exti.h>
#include <biwak/handler.h>
#include <biwak/lltools.h>          // setExti()
#include <lepto/log.h>
#include <biwak/handler.h>
#include <biwak/nvic.h>
#include <biwak/interruptObjects.hpp>

#include <HALWrapper/stm32.h>
#include <HALWrapper/stm32_gpio.h>


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

#if defined(SANITY_CHECKS)
EGpioPeripheral extiPeripheral[16];
#endif


CExti::CExti(const SExtiConfig &configData)
   :m_configData(configData)
{

#if defined(SANITY_CHECKS)
   if(extiLine >= sizeof(extiPeripheral)/sizeof(extiPeripheral[0]))
      exception("extiLine peripheral to big",extiLine);

   if(_eGpioPin >= sizeof(extiData)/sizeof(extiData[0]))
      exception("invalid pin for exti",_eGpioPin);

   if( extiPeripheral[extiLine] )
      exception("exti line already used", extiLine);

   extiPeripheral[extiLine]=_eGpioPeripheral;
#endif
   
   if ( configData.interrupt )
   {
      lFatal( LDS( "IAU", "Interrupt already used" ) );
   }
   configData.interrupt=this;

   // Priority: External periphery, nothing serious; dont mess with HRTimers ...
   setNVInterrupt( m_configData.irqn, true, NVI_DEFAULT_PRIORITY );

}

CExti::~CExti()
{
   setNVInterrupt( m_configData.irqn, false, NVI_LOW_PRIORITY );
}

void CExti::enableInterrupt(bool enable /*=true*/, bool highPriority /*=false*/)
{
   setNVInterrupt( m_configData.irqn, enable,
            highPriority ? NVI_HIGHER_PRIORITY : NVI_DEFAULT_PRIORITY );
}

void CExti::interrupt() /* virtual */
{

}

INIT_IOBJECTS(CExti *, exti);

CExti *interruptExti0 IOBJECT(exti, 0);
CExti *interruptExti1 IOBJECT(exti, 1);
CExti *interruptExti2 IOBJECT(exti, 2);
CExti *interruptExti3 IOBJECT(exti, 3);
CExti *interruptExti4 IOBJECT(exti, 4);
CExti *interruptExti5 IOBJECT(exti, 5);
CExti *interruptExti6 IOBJECT(exti, 6);
CExti *interruptExti7 IOBJECT(exti, 7);
CExti *interruptExti8 IOBJECT(exti, 8);
CExti *interruptExti9 IOBJECT(exti, 9);
CExti *interruptExti10 IOBJECT(exti, 10);
CExti *interruptExti11 IOBJECT(exti, 11);
CExti *interruptExti12 IOBJECT(exti, 12);
CExti *interruptExti13 IOBJECT(exti, 13);
CExti *interruptExti14 IOBJECT(exti, 14);
CExti *interruptExti15 IOBJECT(exti, 15);

extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   for ( CExti **iter=&iobject_exti; iter < &iobjects_end_exti; ++iter)
   {
      CExti *exti=*iter;
      if( exti && ( exti->getPin() == GPIO_Pin ) )
      {
         exti->interrupt();
      }
   }
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"

#if defined STM32F1 || defined STM32F3 || defined STM32F4 || defined STM32L1 || defined STM32L4 || defined STM32G4

   // STM32G441;
   IRQ_Funcion_imp( EXTI0_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
   }

#endif


#if defined ( STM32F0 ) || defined ( STM32L0 ) || defined ( STM32G0 )

   IRQ_Funcion_imp( EXTI0_1_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
   }

#endif


/// \brief  Reads input signals
///
///         It should read all slopes till an timeout appears. Otherwise
///         decoding is very asynchron and not guaranteed to work.

#if defined STM32F1 || defined STM32F3 || defined STM32F4 || defined STM32L1

   IRQ_Funcion_imp( EXTI1_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
   }

#endif


#if defined STM32F1 || defined STM32F4 || defined STM32L1 || defined STM32L4

   IRQ_Funcion_imp( EXTI2_IRQHandler )
   {
      //callInterrupt(interruptExti2);
      //INVOKE_HAL_GPIO_EXTI(GPIO_PIN_2);
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
   }

#endif


#if defined STM32F3

   IRQ_Funcion_imp( EXTI2_TSC_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
   }

#endif


#if defined STM32F1 || defined STM32F0 || defined STM32L0
   IRQ_Funcion_decl ( EXTI2_3_IRQHandler );
   IRQ_Funcion_imp( EXTI2_3_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
      //printf("EXTI 2...3\n\r");
      /*
      if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_2) != RESET)
      {
         //callInterrupt(interruptExti2);
         //INVOKE_HAL_GPIO_EXTI(GPIO_PIN_2);
         HAL_GPIO_EXTI_IRQHandler( GPIO_PIN_1 );
      }

      if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_3) != RESET)
      {
         callInterrupt(interruptExti3);
         INVOKE_HAL_GPIO_EXTI(GPIO_PIN_3);
      }
      */
   }

#endif


#if defined ( STM32F1 ) || defined ( STM32F3 ) || defined ( STM32F4 ) || \
    defined ( STM32L1 ) || defined ( STM32L4 ) || defined ( STM32G4 )

   IRQ_Funcion_imp( EXTI3_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler( GPIO_PIN_3 );
   }

   IRQ_Funcion_imp( EXTI4_IRQHandler )
   {
      HAL_GPIO_EXTI_IRQHandler( GPIO_PIN_4 );
   }

#endif
   

#define pinToMakro( pin ) \
   ( 1 << pin )
   
static_assert( pinToMakro( 0 ) == GPIO_PIN_0, "pinToMakro failed" );
static_assert( pinToMakro( 1 ) == GPIO_PIN_1, "pinToMakro failed" );
static_assert( pinToMakro( 2 ) == GPIO_PIN_2, "pinToMakro failed" );
static_assert( pinToMakro( 3 ) == GPIO_PIN_3, "pinToMakro failed" );
static_assert( pinToMakro( 4 ) == GPIO_PIN_4, "pinToMakro failed" );
static_assert( pinToMakro( 5 ) == GPIO_PIN_5, "pinToMakro failed" );
static_assert( pinToMakro( 6 ) == GPIO_PIN_6, "pinToMakro failed" );
static_assert( pinToMakro( 7 ) == GPIO_PIN_7, "pinToMakro failed" );
static_assert( pinToMakro( 8 ) == GPIO_PIN_8, "pinToMakro failed" );
static_assert( pinToMakro( 9 ) == GPIO_PIN_9, "pinToMakro failed" );
static_assert( pinToMakro( 10 ) == GPIO_PIN_10, "pinToMakro failed" );
static_assert( pinToMakro( 11 ) == GPIO_PIN_11, "pinToMakro failed" );
static_assert( pinToMakro( 12 ) == GPIO_PIN_12, "pinToMakro failed" );
static_assert( pinToMakro( 13 ) == GPIO_PIN_13, "pinToMakro failed" ); 
   
#if defined ( STM32F0 ) || defined ( STM32L0 ) || defined ( STM32G0 )

// inline is really necessary; couldnt capture 48Khz signal in debug builds
/*
int inline pinIndexToDefine(int pin)
{
   return(1 << (int)pin);
}
*/

IRQ_Funcion_imp( EXTI4_15_IRQHandler )
{
   for(uint32_t pin=GPIO_PIN_4; pin<=GPIO_PIN_15;  )
   {
         HAL_GPIO_EXTI_IRQHandler( pin );
         pin = pin << 1;
   }
}
#endif

#if defined ( STM32F1 ) || defined( STM32F3 ) || defined ( STM32F4 ) \
 || defined ( STM32L1 ) || defined ( STM32L4 ) \
 || defined ( STM32G4 )

   IRQ_Funcion_imp( EXTI9_5_IRQHandler )
   {
      for(uint32_t pin=GPIO_PIN_5; pin<=GPIO_PIN_9; pin <<= 1 )
      {
         HAL_GPIO_EXTI_IRQHandler( pin );
      }
   }
#endif
   
#if defined ( STM32F1 ) || defined ( STM32F3 ) || defined ( STM32F4 ) \
 || defined ( STM32L1 ) || defined ( STM32L4 )

   IRQ_Funcion_imp( EXTI15_10_IRQHandler )
   {
      for(uint32_t pin=GPIO_PIN_10; pin<=GPIO_PIN_15; pin <<= 1 )
      {
         HAL_GPIO_EXTI_IRQHandler( pin );
      }
   }

#endif

#pragma GCC diagnostic pop


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