#include <stdio.h>
#include "os/queue.h"
#include "bsp/bsp_driver.h"
#include "libs/utils.h"
#include "libs/circle_buffer.h"

#define CAN_RX_MESSAGE_RX_ID 1
#define CAN_SEND_QUEUE_SIZE 32
#define RX_ID_OFFSET 16
#define CAN_SEND_OK 0
#define CAN_SEND_ERROR -1
#define CAN_SEND_WAIT_TIMEOUT -2

#define CAN_DEV CAN1

#define TX_NUM 64
#define RX_NUM 64
static c_buffer_t g_tx_circle;
static c_buffer_t g_rx_circle;
static uint8_t _g_tx_buffer[sizeof(CanTxMessage) * TX_NUM + 1];
static uint8_t _g_rx_buffer[sizeof(CanRxMessage) * RX_NUM + 1];

static int shark_send_can0_data(CanTxMessage *P_message);
static uint8_t can_get_mailbox(CAN_Module *an_periph);
/* this function can be overide by app, which need recv the can frame */
__weak void handle_can_frame(can_id_t id, uint8_t *data, int len){

}

void can_rx_poll(void){
	CanRxMessage message;
	if (circle_get_data(&g_rx_circle, (uint8_t *)&message, sizeof(message)) != sizeof(message)) {
		return;
	}	
	can_id_t can_id;
	can_id.id = message.ExtId;
	handle_can_frame(can_id, message.Data , message.DLC); 
	return ;
}

void can_tx_poll(void){
	CanTxMessage can_tr_m;
	if (CAN_GetFlagSTS(CAN_DEV, CAN_FLAG_BOFFL)){
		shark_can0_reset();
	}
	while (can_get_mailbox(CAN_DEV) != CAN_TxSTS_NoMailBox) {
		if (circle_get_data(&g_tx_circle, (uint8_t * )&can_tr_m, sizeof(can_tr_m)) != sizeof(can_tr_m)) {
			break;
		}
		CAN_TransmitMessage(CAN_DEV,&can_tr_m);
	}
}

static u32 _can_poll_task(void *args) {
	can_rx_poll();
	can_tx_poll();
	return 0;
}

static __inline__ void can_fifo_recv(int fifo){
	CanRxMessage Rxmessage;

	CAN_ReceiveMessage(CAN_DEV, fifo, &Rxmessage);

	circle_put_data(&g_rx_circle, (uint8_t *)&Rxmessage,  sizeof(Rxmessage));

}

void CAN_RX0_IRQHandler(void)
{
	can_fifo_recv(CAN_FIFO0);
}

void CAN_RX1_IRQHandler(void)
{
	can_fifo_recv(CAN_FIFO1);
}

static void shark_can0_txrx_pin_config(void){
    /* enable can clock */
    rcu_apb1_periph_clock_enable(RCU_CAN0);
    rcu_apb2_periph_clock_enable(CAN_PIN_RCU);
    rcu_apb2_periph_clock_enable(RCU_AF);

#ifdef CAN_REMAP
	gpio_pin_remap_config(CAN_REMAP,ENABLE);
#endif
    gpio_init(CAN_TX_GROUP, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CAN_TX_PIN);
	gpio_init(CAN_RX_GROUP, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, CAN_RX_PIN);
}

static void can_filter_init(u32 id, u32 mask, u8 fifo, u8 filter)
{
    CAN_FilterInitType CAN_FilterInitStructure;
    /* CAN filter init */
    CAN_FilterInitStructure.Filter_Num            = filter;
    CAN_FilterInitStructure.Filter_Mode           = CAN_Filter_IdMaskMode;
    CAN_FilterInitStructure.Filter_Scale          = CAN_Filter_32bitScale;
    CAN_FilterInitStructure.Filter_HighId         = (uint16_t)id >> 13;
    CAN_FilterInitStructure.Filter_LowId          = (0x003F8U & (uint16_t)(id << 3)) | (1U << 2);  
    CAN_FilterInitStructure.FilterMask_HighId     = (uint16_t)mask >> 13;
    CAN_FilterInitStructure.FilterMask_LowId      = (0x003F8U & (uint16_t)(mask << 3)) | (1U << 2);  
    CAN_FilterInitStructure.Filter_FIFOAssignment = fifo;
    CAN_FilterInitStructure.Filter_Act            = ENABLE;
    CAN1_InitFilter(&CAN_FilterInitStructure);
    CAN_INTConfig(CAN_DEV, CAN_INT_FMP0, ENABLE);
}

static void shark_can0_config(void)
{

    CAN_InitType CAN_InitStructure;
    /* CAN register init */
    CAN_DeInit(CAN_DEV);
    /* Struct init*/
    CAN_InitStruct(&CAN_InitStructure);
    /* CAN cell init */
    CAN_InitStructure.TTCM              = DISABLE;
    CAN_InitStructure.ABOM              = DISABLE;
    CAN_InitStructure.AWKUM             = DISABLE;
    CAN_InitStructure.NART              = ENABLE;
    CAN_InitStructure.RFLM              = DISABLE;
    CAN_InitStructure.TXFP              = ENABLE;
    CAN_InitStructure.OperatingMode     = CAN_Normal_Mode;
    CAN_InitStructure.RSJW              = CAN_RSJW_1tq;
    CAN_InitStructure.TBS1              = CAN_TBS1_4tq;
    CAN_InitStructure.TBS2              = CAN_TBS2_3tq;
    CAN_InitStructure.BaudRatePrescaler = 18;
    /*Initializes the CAN */
    CAN_Init(CAN_DEV, &CAN_InitStructure);

    /* recv my can ID, use fifo0 */
    can_filter_init(CAN_MY_ADDRESS, CAN_FILTER_DEST_MASK, CAN_FIFO0, 0);

  	//nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);  

  	nvic_irq_enable(CAN_IRQ0,CAN_IRQ_PRIORITY,0);	
}


static int shark_send_can0_data(CanTxMessage *P_message){
	can_tx_poll();
	if (circle_put_data(&g_tx_circle, (u8 *)P_message, sizeof(CanTxMessage))){
		return CAN_SEND_OK;
	}
	return CAN_SEND_ERROR;
}


static uint8_t can_get_mailbox(CAN_Module *can_periph)
{
    uint8_t transmit_mailbox = 0;
    /* Check the parameters */

    /* Select one empty transmit mailbox */
    if ((CAN_DEV->TSTS & CAN_TSTS_TMEM0) == CAN_TSTS_TMEM0)
    {
        transmit_mailbox = 0;
    }
    else if ((CAN_DEV->TSTS & CAN_TSTS_TMEM1) == CAN_TSTS_TMEM1)
    {
        transmit_mailbox = 1;
    }
    else if ((CAN_DEV->TSTS & CAN_TSTS_TMEM2) == CAN_TSTS_TMEM2)
    {
        transmit_mailbox = 2;
    }
    else
    {
        transmit_mailbox = CAN_TxSTS_NoMailBox;
    }
	return transmit_mailbox;
}


int shark_can0_send_message(uint32_t can_id, const void*buff, int len){
	CanTxMessage trasnmit_msg;
	can_id_t can_frame_id;
	u32 total_frames = ((len + 7) >> 3);
	int send_len = len;
	u32 frame_id = 1;
	can_frame_id.id = can_id;
	can_frame_id.total = total_frames;

	while(send_len > 0){
		can_frame_id.idx = frame_id;

		trasnmit_msg.StdId	= 0;
		trasnmit_msg.ExtId	= can_frame_id.id;				
		trasnmit_msg.RTR	= CAN_RTRQ_DATA;
		trasnmit_msg.IDE    = CAN_ID_EXT;
		trasnmit_msg.DLC = min(CAN_DATA_SIZE,send_len);
		memcpy((char *)trasnmit_msg.Data, (char *)buff + (len - send_len), trasnmit_msg.DLC);
		send_len -= trasnmit_msg.DLC;
		frame_id ++;
		if (shark_send_can0_data(&trasnmit_msg) != CAN_SEND_OK){
			return CAN_SEND_ERROR;
		}
	}

	return CAN_SEND_OK;

}

int shark_can0_send_ext_message(uint32_t can_id, const void*buff, int len){
	CanTxMessage trasnmit_msg;
	
	trasnmit_msg.StdId	= 0;
	trasnmit_msg.ExtId	= can_id;				
	trasnmit_msg.RTR	= CAN_RTRQ_DATA;
	trasnmit_msg.IDE	= CAN_ID_EXT;
	trasnmit_msg.DLC = min(CAN_DLC_LENGTH,len);
	memcpy((char *)trasnmit_msg.Data, (char *)buff, trasnmit_msg.DLC);

	if (shark_send_can0_data(&trasnmit_msg) != CAN_SEND_OK){
		return CAN_SEND_ERROR;
	}
	return CAN_SEND_OK;
}


void shark_can0_reset(void){
	shark_can0_txrx_pin_config();
	shark_can0_config();
}

void shark_can0_deinit(void){
	CAN_DeInit(CAN_DEV);
	rcu_apb1_periph_clock_disable(RCU_CAN0);
}

void shark_can0_init(void){
	circle_buffer_init(&g_tx_circle, _g_tx_buffer, sizeof(_g_tx_buffer));
	circle_buffer_init(&g_rx_circle, _g_rx_buffer, sizeof(_g_rx_buffer));

	shark_task_create(_can_poll_task, NULL);
	shark_can0_txrx_pin_config();
	shark_can0_config();
}

