//----------------------------------------------------------------------------
//
//
//----------------------------------------------------------------------------


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


#include <biwak/fdcan.h>
#include <biwak/stm32/fdcan.h>
#include <biwak/gpio.h>
#include <biwak/rcc.h>           // rcCFDCanClockEnable
#include <biwak/nvic.h>          // setNVInterrupt
#include <biwak/handler.h>       // IRQ
#include <lepto/log.h>
#include <biwak/biwak.h>
#include <biwak/exception.h>
#include <stm/stm32_can.h>
#include <string.h>              // memset

#define Error_Handler() exception("error")


//---Declarations-------------------------------------------------------------

#if 0
struct
{
   FDCAN_HandleTypeDef *pHandler;
   CFDCan *pCan;
} static canDynamicData[(int)eCanMax]={0};
#endif

CFDCan *interruptCan1=nullptr;

/// \brief  Le contructeur
///
///         This constructor does not set anything. you have to call init()
///         afterwards
/*
CFDCan::CFDCan(ECan _eCan,
           EGpioPeripheral eGpioPeripheralTx, EGpioPin eGpioPinTx,
           EGpioPeripheral eGpioPeripheralRx, EGpioPin eGpioPinRx,
           const SCanConfig &configData)
*/
CFDCan::CFDCan( const SFDCanConfig &_configData)
//   :eCan(_eCan)
   :m_configData(_configData)
   ,m_status( /*rcCFDCanClockEnable( canData[_eCan].can )*/ 0 )
   //,gpioTx(m_configData.eGpioPeripheralTx, m_configData.eGpioPinTx, gpioModeAfInOut, m_configData.altFuncTx)
   //,gpioRx(m_configData.eGpioPeripheralRx, m_configData.eGpioPinRx, gpioModeAfInOut, m_configData.altFuncRx)

   ,gpioRx(m_configData.eGpioPeripheralRx, m_configData.eGpioPinRx, gpioModeCanRx, m_configData.altFuncRx)
   ,gpioTx(m_configData.eGpioPeripheralTx, m_configData.eGpioPinTx, gpioModeCanTx, m_configData.altFuncTx)
   ,buffer(2)
{
   m_pHandler=new FDCAN_HandleTypeDef;
   m_pHandler->Instance=m_configData.instance;
   init();
}


CFDCan::~CFDCan()
{
   //delete txHeader;
   //delete rxHeader;
   delete m_pHandler;
   m_pHandler=NULL;
}
#define CHECK_RESTRICTED 1
void CFDCan::init()
{
   HAL_StatusTypeDef status;

   #if defined ( CAN_IMPLEMENTATION_1 )
   CAN_FilterTypeDef CAN_FilterConfStructure;
   #else
   //FDCAN_FilterConfTypeDef FDCAN_FilterConfStructure;
   //FDCAN_FilterConfTypeDef FDCAN_FilterConfStructure;
   #endif

   m_enabled=false;

   memset((void *)&m_pHandler->Init, 0, sizeof(m_pHandler->Init) );

   rccFDCanClockEnable( m_configData.instance ) ;

   /* CAN register init */
   HAL_FDCAN_DeInit(m_pHandler);

   interruptCan1=this;
   setNVInterrupt( m_configData.irqnTx, true, NVI_DEFAULT_PRIORITY);
   setNVInterrupt( m_configData.irqnRx0, true, NVI_DEFAULT_PRIORITY);

   /* CAN cell init */
   m_pHandler->Instance = FDCAN1;
   m_pHandler->Init.ClockDivider = FDCAN_CLOCK_DIV1;
   m_pHandler->Init.FrameFormat = FDCAN_FRAME_CLASSIC; // CLASSIC; //FD_BRS;
   m_pHandler->Init.Mode = FDCAN_MODE_NORMAL;
   //m_pHandler->Init.Mode = FDCAN_MODE_INTERNAL_LOOPBACK;
   m_pHandler->Init.AutoRetransmission = ENABLE;
   m_pHandler->Init.TransmitPause = DISABLE;
   m_pHandler->Init.ProtocolException = DISABLE;

   // see: http://www.bittiming.can-wiki.info/
   // Calculation: RATE=CLK/(Prescale*(1+BS1+BS2+SJW))
   // 96000000 / 48 / 1 / (13 + 2 + 1) = 125 Khz
   // 48000000 / 24 / 1 / (13 + 2 + 1) = 125 Khz
   // 64000000 / 32 / 1 / (13 + 2 + 1) = 125 Khz

   m_pHandler->Init.NominalPrescaler = 24;
   m_pHandler->Init.NominalSyncJumpWidth = 1;
   m_pHandler->Init.NominalTimeSeg1 = 13;
   m_pHandler->Init.NominalTimeSeg2 = 2;
   m_pHandler->Init.DataPrescaler = 32;
   m_pHandler->Init.DataSyncJumpWidth = 1;
   m_pHandler->Init.DataTimeSeg1 = 13;
   m_pHandler->Init.DataTimeSeg2 = 2;
   m_pHandler->Init.StdFiltersNbr = 0;
   m_pHandler->Init.ExtFiltersNbr = 0;
   m_pHandler->Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;

   if (HAL_FDCAN_Init(m_pHandler) != HAL_OK)
   {
     exception("Could not init FDCAN");
   }

#if CHECK_RESTRICTED
   if(HAL_FDCAN_IsRestrictedOperationMode( m_pHandler ) != 0)
   {
      exception("Could not startCAN; Restricted");
   }
#endif
   initFilter();
   //initDelays();
   initHeaders();

#if CHECK_RESTRICTED
   if(HAL_FDCAN_IsRestrictedOperationMode( m_pHandler ) != 0)
   {
      exception("Could not startCAN; Restricted");
   }
#endif

   status=HAL_FDCAN_Start(m_pHandler);
   if(status!=HAL_OK)
   {
      exception("Could not startCAN");
   }

   /* Activate Rx FIFO 0 new message notification on both FDCAN instances */
   if (HAL_FDCAN_ActivateNotification(m_pHandler, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
   {
     exception("Could not set CAN activate notification");
   }

   #if defined ( CAN_IMPLEMENTATION_1 )
   status=HAL_CAN_ActivateNotification(m_pHandler, CAN_IT_RX_FIFO0_MSG_PENDING);
   status=HAL_CAN_ActivateNotification(m_pHandler, CAN_IT_RX_FIFO1_MSG_PENDING);
   if(status!=HAL_OK)
      exception("Could not set up notifications", -1);
   #endif

   printf("State: %d\n", HAL_FDCAN_GetState( m_pHandler ) );

   m_enabled=true;
}


void CFDCan::initFilter()
{
   /* Configure reception filter to Rx FIFO 0 on both FDCAN instances */
   sFilterConfig.IdType = FDCAN_STANDARD_ID;
   sFilterConfig.FilterIndex = 0;
   sFilterConfig.FilterType = FDCAN_FILTER_MASK;
   //sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
   sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
   sFilterConfig.FilterID1 = 0x000;    // Filter
   sFilterConfig.FilterID2 = 0x000;    // Mask
   if (HAL_FDCAN_ConfigFilter(m_pHandler, &sFilterConfig) != HAL_OK)
   {
     exception("Could not set CAN filter");
   }

   sFilterConfig.IdType = FDCAN_EXTENDED_ID;
   sFilterConfig.FilterIndex = 1;
   if (HAL_FDCAN_ConfigFilter(m_pHandler, &sFilterConfig) != HAL_OK)
   {
     exception("Could not set CAN filter");
   }

   /* Configure global filter on both FDCAN instances:
      Filter all remote frames with STD and EXT ID
      Reject non matching frames with STD ID and EXT ID */
#if 0
   if (HAL_FDCAN_ConfigGlobalFilter(m_pHandler, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
   //if (HAL_FDCAN_ConfigGlobalFilter(m_pHandler, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE) != HAL_OK)
   {
     exception("Could not set CAN global filter");
   }
#endif

   return;
}


void CFDCan::initHeaders()
{
   /* Prepare Tx message Header */
   TxHeader.Identifier = 0x321;
   TxHeader.IdType = FDCAN_STANDARD_ID;
   TxHeader.TxFrameType = FDCAN_DATA_FRAME;
   TxHeader.DataLength = FDCAN_DLC_BYTES_4;
   TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
   TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
   TxHeader.FDFormat = FDCAN_CLASSIC_CAN;
   TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
   TxHeader.MessageMarker = 0;
   return;
}

void CFDCan::initDelays()
{
   /* Configure and enable Tx Delay Compensation, required for BRS mode.
      TdcOffset default recommended value: DataTimeSeg1 * DataPrescaler
      TdcFilter default recommended value: 0 */
   if (HAL_FDCAN_ConfigTxDelayCompensation(m_pHandler, 5, 0) != HAL_OK)
   {
     exception("Could not config CAN tx delay compensation");
   }
   if (HAL_FDCAN_EnableTxDelayCompensation(m_pHandler) != HAL_OK)
   {
     exception("Could not enable CAN tx delay compensation");
   }

   return;
}

void CFDCan::deinit()
{

   return;
}

int CFDCan::transmit(const CCanMessage &message)
{
   uint8_t status=0;

   TxHeader.Identifier=message.getId();
   TxHeader.DataLength=message.getLen() << 16;
   TxHeader.IdType= (TxHeader.Identifier >= 0x7FF) ? FDCAN_EXTENDED_ID : FDCAN_STANDARD_ID;

   status=HAL_FDCAN_AddMessageToTxFifoQ(m_pHandler, &TxHeader, (uint8_t *)message.getData() );

   if( status)
   {
      exception("STATUS!");
   }
   //status=HAL_FDCAN_GetState(m_pHandler);

   return(status);
}


int CFDCan::getTransmitStatus(int mailBox)
{
   return(HAL_FDCAN_GetState(m_pHandler) );
}


int CFDCan::getLastError(void)
{
   return(HAL_FDCAN_GetError(m_pHandler) );
}

void CFDCan::interrupt(void)
{
   HAL_FDCAN_IRQHandler( m_pHandler );
   return;
}

/**
  * @brief  Rx FIFO 0 callback.
  * @param  hfdcan pointer to an FDCAN_HandleTypeDef structure that contains
  *         the configuration information for the specified FDCAN.
  * @param  RxFifo0ITs indicates which Rx FIFO 0 interrupts are signaled.
  *         This parameter can be any combination of @arg FDCAN_Rx_Fifo0_Interrupts.
  * @retval None
  */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
   FDCAN_RxHeaderTypeDef RxHeader;
   uint8_t RxData[0x10];

   if(!interruptCan1)
      exception("No CAN");

  if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != 0)
  {
    /* Retrieve Rx messages from RX FIFO0 */
    if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0,
                               &RxHeader,
                               RxData ) != HAL_OK)
    {
      Error_Handler();
    }
    CCanMessage msg( RxHeader.Identifier );
    interruptCan1->buffer << msg;

    /* Compare payload to expected data */
    if (0) // (BufferCmp8b(TxData, RxData, 16) != 0)
    {
      Error_Handler();
    }
    else
    {
      if (hfdcan->Instance == FDCAN1)
      {
        /* Turn LED1 on */
        //BSP_LED_On(LED1);
      }
      else /* FDCAN2 */
      {
        /* Turn LED4 on */
        //BSP_LED_On(LED4);
      }
    }
  }
}



void HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef *hcan)
{
   exception("Error Callback");
   //HAL_CAN_DeInit(hcan);
   //HAL_CAN_Init(hcan);
};

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

/**
  * @brief This function handles FDCAN1 interrupt 0.
  */
IRQ_Funcion_imp( FDCAN1_IT0_IRQHandler )
{
   //exception("Exc1");
   if(interruptCan1)
   {
      interruptCan1->interrupt();
   }
   else
   {
      exception("Exc1");
   }
};

IRQ_Funcion_imp( FDCAN1_IT1_IRQHandler )
{
   //exception("Exc2");
   if(interruptCan1)
   {
      interruptCan1->interrupt();
   }
   else
   {
      exception("Exc2");
   }
};

#pragma GCC diagnostic pop

//----------------------------------------------------------------------------
