// STM32F103xx: only high density
#if ! ( defined(STM32F103xB) || defined(STM32F103x6) || defined (STM32G0) )
//-----------------------------------------------------------------------------
///
/// \brief  DAC
///
///         Blah blah blub
///
/// \date   20150117
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


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


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

#include <biwak/biwak.h>         // exception
#include <biwak/dac.hpp>
#include <biwak/stm32/dac.hpp>
#include <biwak/dma.hpp>
#include <biwak/gpio.hpp>
#include <biwak/rcc.h>           // rccDacClockEnable
#include <biwak/handler.h>           // __TIM6_DAC_IRQHandler()
#include <HALWrapper/stm32_dma.h>       // dac headers needs it
#include <HALWrapper/stm32_dac.h>
#include <biwak/nvic.h>          // ePeripheral
#include <string.h>              // memset()
#include <biwak/exception.h>
#include <lepto/log.h>
#include <biwak/interruptObjects.hpp>


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


enum ETimer
{
   // STM32L0 has an LPTIM1 which is not compatible with regular timer
   #if !defined (STM32L0)
         eTimer1,
#endif
      eTimer2,
      eTimer3,
      eTimer4,
      eTimer6,
      eTimer7,
      eTimer14,
      eTimerMax,
};

#if 0
struct{
   //DAC_HandleTypeDef handler;
   DAC_TypeDef *dac;
   IRQn_Type irqChannel1;
   IRQn_Type irqChannel2;
   /*
   EGpioPeripheral _eGpioPeripheral;
   EGpioPin _eGpioPin;
   EAltFunc _eAltFunc;
   */

} const static dacStaticData[]=
{
   #ifdef STM32L100xC
   [eDac1]={ DAC, DAC_IRQn, DAC_IRQn},
   #elif defined STM32F072xB
   [eDac1]={ DAC, TIM6_DAC_IRQn /*, eGpioPeripheralA, eGpioPin4, eAltFuncDac1*/ },
   #elif defined STM32F4
   [eDac1]={ DAC, TIM6_DAC_IRQn /*, eGpioPeripheralA, eGpioPin4, eAltFuncDac1*/ },
   #elif defined STM32F3
   [eDac1]={ DAC, TIM6_DAC_IRQn, TIM7_IRQn },
   #endif
};
#endif

#if 0

static const uint32_t dacTimerTriggerData[eTimerMax]=
{
   #ifdef DAC_TRIGGER_T1_TRGO
      [eTimer1]=DAC_TRIGGER_T1_TRGO,
   #else
      [eTimer1]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T2_TRGO
      [eTimer2]=DAC_TRIGGER_T2_TRGO,
   #else
      [eTimer2]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T3_TRGO
      [eTimer3]=DAC_TRIGGER_T3_TRGO,
   #else
      [eTimer3]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T4_TRGO
      [eTimer4]=DAC_TRIGGER_T4_TRGO,
   #elif defined (TIM4)
      [eTimer4]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T5_TRGO
      [eTimer5]=DAC_TRIGGER_T5_TRGO,
   #elif defined (TIM5)
      [eTimer5]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T6_TRGO
      [eTimer6]=DAC_TRIGGER_T6_TRGO,
   #else
      [eTimer6]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T7_TRGO
      [eTimer7]=DAC_TRIGGER_T7_TRGO,
   #else
      [eTimer7]=DAC_TRIGGER_NONE,
   #endif
   #ifdef DAC_TRIGGER_T8_TRGO
      [eTimer8]=DAC_TRIGGER_T8_TRGO,
   #elif defined (TIM8)
      [eTimer8]=DAC_TRIGGER_NONE,
   #endif
};

#endif


static const uint32_t dacChannelTable[]=
{
   [eDacChannel1]=DAC_CHANNEL_1,
   [eDacChannel2]=DAC_CHANNEL_2,
   [eDacChannelMax]=DAC_CHANNEL_1,
};

INIT_IOBJECTS(const CDac *, dac);
const CDac *interruptDac1 IOBJECT(dac, 1);
const CDac *interruptDac2 IOBJECT(dac, 2);

CDac::CDac( const SDacConfig &dacConfig )
{
   m_pHandle=new DAC_HandleTypeDef;
   memset( m_pHandle, 0, sizeof(*m_pHandle) );
   
   m_pHandle->Instance=dacConfig.instance;

   rccDacClockEnable(m_pHandle->Instance);
   dacConfig.interrupt=this;
   
   setNVInterrupt( dacConfig.irqn, true, NVI_DEFAULT_PRIORITY);
   
   init();
}


void CDac::init()
{
   HAL_StatusTypeDef status;
   // Wen need high priority because timer might be used in ISRs.
   //setNVInterrupt(dacStaticData[eDac].irqChannel1, true, NVI_DEFAULT_PRIORITY);
   //setNVInterrupt(dacStaticData[eDac].irqChannel2, true, NVI_DEFAULT_PRIORITY);

   //HAL_DAC_DeInit(pHandler);
   status=HAL_DAC_Init(m_pHandle);
   bDebugAssert( status == HAL_OK, status );
   (void)status;
}


void CDac::start( EDacChannel eDacChannel, void *buffer, int bufferSize )
{
   HAL_StatusTypeDef status __attribute__ ((unused));
   status=HAL_DAC_Start_DMA( m_pHandle
               , eDacChannel==eDacChannel1 ? DAC_CHANNEL_1:DAC_CHANNEL_2
               , (uint32_t *)buffer, bufferSize
              , DAC_ALIGN_12B_R);
   bDebugAssert( status == HAL_OK, status );
}


void CDac::stop()
{
   HAL_DAC_Stop(m_pHandle, DAC_CHANNEL_1);
}

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

void __TIM6_DAC_IRQHandler(void)
{
   //if( dacDynamicData[eDac1].pHandler )
   //   HAL_DAC_IRQHandler( dacDynamicData[eDac1].pHandler );
   exception( );
}

#if defined(STM32F3)

IRQ_Funcion_imp(TIM6_DAC1_IRQHandler_dac)
{
   HAL_DAC_IRQHandler( interruptDac1->getHandle() );

}

#endif

// STM32F0
IRQ_Funcion_imp(TIM6_DAC_IRQHandler_dac)
{
   //if( dacDynamicData[eDac1].pHandler )
   //   HAL_DAC_IRQHandler( dacDynamicData[eDac1].pHandler );
   //else
   // TBD: call interrupt
   exception( );
}

#ifdef STM32F3

IRQ_Funcion_imp( TIM7_DAC2_IRQHandler_dac )
{
   //if( dacDynamicData[eDac2].pHandler )
   //   HAL_DAC_IRQHandler( dacDynamicData[eDac2].pHandler );
   // IRQ
      exception( );
}

#endif

#pragma GCC diagnostic pop

extern "C"
{
   void HAL_DAC_ErrorCallbackCh1(DAC_HandleTypeDef *hdac);
   void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef* hdac);
   void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* hdac);
   void HAL_DAC_DMAUnderrunCallbackCh1(DAC_HandleTypeDef *hdac);
}


void HAL_DAC_ErrorCallbackCh1(DAC_HandleTypeDef *hdac)
{
   exception( );
};


void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef* pHandle)
{
   /*
   //exception("HAL_DAC_ConvHalfCpltCallbackCh1", 1);
   for ( const CDac **iter=&iobject_dac; iter < &iobjects_end_dac; ++iter)
   {
      const CDac *dac=*iter;
      if( dac && ( dac->getHandle() == pHandle ) )
      {
         dac->signalConvCompleted.emitSignal();
         return;
      }
   }
   */
}


void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* pHandle)
{
   for ( const CDac **iter=&iobject_dac; iter < &iobjects_end_dac; ++iter)
   {
      const CDac *dac=*iter;
      if( dac && ( dac->getHandle() == pHandle ) )
      {
         dac->signalConvCompleted.emitSignal();
         return;
      }
   }
}


#define SAMPLE_TYPE uint8_t
extern SAMPLE_TYPE *data;
extern uint32_t    dataSamples;

void HAL_DAC_DMAUnderrunCallbackCh1(DAC_HandleTypeDef *hdac)
{
   HAL_StatusTypeDef status __attribute( (unused) );

   //__HAL_DAC_ENABLE_IT (hdac, DAC_IT_DMAUDR1);
   //__HAL_DAC_ENABLE (hdac, DAC_CHANNEL_1);
   /*
   assert(
         (status=HAL_DAC_Stop_DMA(hdac, DAC_CHANNEL_1)) == HAL_OK );

   //assert(
   //      (status=HAL_DAC_Start_DMA(hdac, DAC_CHANNEL_1, (uint32_t *)data, dataSamples, DAC_ALIGN_8B_R) ) == HAL_OK );
   status=HAL_DAC_Start_DMA(hdac, DAC_CHANNEL_1, (uint32_t *)data, dataSamples, DAC_ALIGN_8B_R);
   */

   //assert( (status=HAL_DAC_Start_DMA(p.dac[0]->getHandler(), DAC_CHANNEL_1, (uint32_t *)data, SAMPLE_COUNT*sizeof(data[0]), DAC_ALIGN_8B_R) ) == HAL_OK );

   // Crashes, infinite loop
   #pragma GCC diagnostic push
   #pragma GCC diagnostic ignored "-Wvolatile"

   hdac->Instance->CR |= DAC_CR_DMAEN1;

   #pragma GCC diagnostic pop
}


CDacChannel::CDacChannel(CDac &pDac
         , EDacChannel eDacChannel, EGpioPeripheral _eGpioPeripheral, EGpioPin _eGpioPin)
   :m_pDac(pDac)
   ,m_eDacChannel(eDacChannel)
   ,m_gpio(_eGpioPeripheral, _eGpioPin, gpioModeAnalog)
{
   init(DAC_TRIGGER_NONE);
}

void CDacChannel::init(int trigger)
{
   HAL_StatusTypeDef status;
   DAC_ChannelConfTypeDef  ChannelConf;
   
   memset(&ChannelConf, 0, sizeof(ChannelConf));

   // ChannelConf.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
   // ChannelConf.DAC_Trigger = DAC_TRIGGER_NONE;
   // ChannelConf.DAC_Trigger = DAC_TRIGGER_T7_TRGO;
   
   //ChannelConf.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
   //ChannelConf.DAC_Trigger = DAC_TRIGGER_SOFTWARE;
   ChannelConf.DAC_Trigger = trigger;
   
   ChannelConf.DAC_OutputBuffer=DAC_OUTPUTBUFFER_ENABLE;
   //ChannelConf.DAC_OutputBuffer=DAC_OUTPUTBUFFER_DISABLE;
   
#if ! defined( STM32F4 ) && ! defined ( STM32L1) && ! defined ( STM32F0) && ! defined ( STM32L4)
   if( m_eDacChannel == eDacChannel2)
   {
      ChannelConf.DAC_OutputSwitch = DAC_OUTPUTSWITCH_ENABLE;
   }
#endif
   
   // May be valid?
   //bDebugAssert( ChannelConf.DAC_Trigger != DAC_TRIGGER_NONE, 0 );
   
   status=HAL_DAC_ConfigChannel(m_pDac.m_pHandle, &ChannelConf, dacChannelTable[m_eDacChannel]);
   bDebugAssert( status == HAL_OK, status );
   (void)status;

#if 0
   if( eTimerDacChannel2 != eTimerNone )
   {
      ChannelConf.DAC_Trigger=dacTimerTriggerData[eTimerDacChannel2];
      ChannelConf.DAC_OutputBuffer=DAC_OUTPUTBUFFER_ENABLE;
#if ! defined( STM32F4 ) && ! defined ( STM32L1) && ! defined ( STM32F0)
      ChannelConf.DAC_OutputSwitch = DAC_OUTPUTSWITCH_ENABLE;
#endif
      assert( ChannelConf.DAC_Trigger != DAC_TRIGGER_NONE, 0 );
      status=HAL_DAC_ConfigChannel(m_pHandle, &ChannelConf, DAC_CHANNEL_2);
      assert( status == HAL_OK, status );
   }
#endif
   return;
}


void CDacChannel::setValue(int value)
{
   HAL_StatusTypeDef sta __attribute__ ((unused));

   // Dont Stop! It results in big shaker
   //sta=HAL_DAC_Stop(m_pHandle, DAC_CHANNEL_1);
   //assert(sta==HAL_OK, sta);
   
   if(!m_state)
   {
      sta=HAL_DAC_Start(m_pDac.m_pHandle, dacChannelTable[m_eDacChannel]);
      bDebugAssert(sta==HAL_OK, sta);
      m_state=1;
   }

   sta=HAL_DAC_SetValue(m_pDac.m_pHandle, dacChannelTable[m_eDacChannel], DAC_ALIGN_12B_R, value);
   bDebugAssert(sta==HAL_OK, sta);
}


//---fin-----------------------------------------------------------------------
#endif // ? ! defined(STM32F103xB)
