#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 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(can_tx_message_type) * TX_NUM + 1];
static uint8_t _g_rx_buffer[sizeof(can_rx_message_type) * RX_NUM + 1];

static int shark_send_can0_data(can_tx_message_type *P_message);
static uint8_t can_get_mailbox(uint32_t can_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){
	can_rx_message_type 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.extended_id;
	handle_can_frame(can_id, message.data, message.dlc); 
	return ;
}

void can_tx_poll(void){
	can_tx_message_type can_tr_m;
	if (CAN1->ests_bit.bof){
		shark_can0_reset();
	}
	while (can_get_mailbox((u32)CAN1) != CAN_TX_STATUS_NO_EMPTY) {
		if (circle_get_data(&g_tx_circle, (uint8_t * )&can_tr_m, sizeof(can_tr_m)) != sizeof(can_tr_m)) {
			break;
		}
		can_message_transmit(CAN1,&can_tr_m);
	}
}

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

static __inline__ void can_fifo_recv(can_rx_fifo_num_type fifo){
	can_rx_message_type Rxmessage;

	can_message_receive(CAN1, fifo, &Rxmessage);

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

}

void USBFS_L_CAN1_RX0_IRQHandler(void)
{
	can_fifo_recv(CAN_RX_FIFO0);
}

void CAN0_RX1_IRQHandler(void)
{
	can_fifo_recv(CAN_RX_FIFO1);
}

static void shark_can0_txrx_pin_config(void){
	gpio_init_type gpio_init_struct;
    /* enable can clock */
    crm_periph_clock_enable(CAN_PIN_RCU, TRUE);

#ifdef CAN_REMAP
	gpio_pin_remap_config(CAN_REMAP,TRUE);
#endif
	
	gpio_default_para_init(&gpio_init_struct);
	
	/* can tx pin */
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
	gpio_init_struct.gpio_pins = CAN_TX_PIN;
	gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
	gpio_init(CAN_TX_GROUP, &gpio_init_struct);
	
	/* can rx pin */
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
	gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
	gpio_init_struct.gpio_pins = CAN_RX_PIN;
	gpio_init_struct.gpio_pull = GPIO_PULL_UP;
	gpio_init(CAN_RX_GROUP, &gpio_init_struct);
}


static void shark_can0_config(void)
{
	can_base_type can_base_struct;
	can_baudrate_type can_baudrate_struct;
	can_filter_init_type can_filter_init_struct;
	
	crm_periph_clock_enable(CRM_CAN1_PERIPH_CLOCK, TRUE);
	/* can base init */
	can_default_para_init(&can_base_struct);
	can_base_struct.mode_selection = CAN_MODE_COMMUNICATE;
	can_base_struct.ttc_enable = FALSE;
	can_base_struct.aebo_enable = TRUE;
	can_base_struct.aed_enable = TRUE;
	can_base_struct.prsf_enable = FALSE;
	can_base_struct.mdrsel_selection = CAN_DISCARDING_FIRST_RECEIVED;
	can_base_struct.mmssr_selection = CAN_SENDING_BY_REQUEST;
	can_base_init(CAN1, &can_base_struct);

	//CAN0_SPEED==250 //250k bps
	/* can baudrate, set baudrate = pclk/(baudrate_div *(1 + bts1_size + bts2_size)) */
	can_baudrate_struct.baudrate_div = 40;
	can_baudrate_struct.rsaw_size = CAN_RSAW_1TQ;
	can_baudrate_struct.bts1_size = CAN_BTS1_5TQ;
	can_baudrate_struct.bts2_size = CAN_BTS2_4TQ;
	can_baudrate_set(CAN1, &can_baudrate_struct);

	/* can filter 0 config */
	can_filter_init_struct.filter_activate_enable = TRUE;
	can_filter_init_struct.filter_mode = CAN_FILTER_MODE_ID_MASK;
	can_filter_init_struct.filter_fifo = CAN_FILTER_FIFO0;
	can_filter_init_struct.filter_number = 0;
	can_filter_init_struct.filter_bit = CAN_FILTER_32BIT;
	can_filter_init_struct.filter_id_high = (((CAN_MY_ADDRESS << 3) >> 16) & 0xFFFF);	/* extended identifier is 29 bit */
	can_filter_init_struct.filter_id_low = ((CAN_MY_ADDRESS << 3) & 0xFFFF) | 0x04;
	can_filter_init_struct.filter_mask_high = ((CAN_FILTER_DEST_MASK << 3) >> 16) & 0xFFFF; /* extended identifier is 29 bit */
	can_filter_init_struct.filter_mask_low = ((CAN_FILTER_DEST_MASK << 3) & 0xFFFF) | 0x04;
	can_filter_init(CAN1, &can_filter_init_struct);

//  	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);  
  	nvic_irq_enable(USBFS_L_CAN1_RX0_IRQn,CAN_IRQ_PRIORITY,0);	
    /* enable can receive FIFO0 not empty interrupt */
    can_interrupt_enable(CAN1, CAN_RF0MIEN_INT, TRUE);
}


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


static uint8_t can_get_mailbox(uint32_t can_periph)
{
    uint8_t transmit_mailbox = CAN_TX_MAILBOX0;
    can_type* can_x = CAN1;
	
	/* select one empty transmit mailbox */
	if(can_x->tsts_bit.tm0ef)
	{
	  transmit_mailbox = CAN_TX_MAILBOX0;
	}
	else if(can_x->tsts_bit.tm1ef)
	{
	  transmit_mailbox = CAN_TX_MAILBOX1;
	}
	else if(can_x->tsts_bit.tm2ef)
	{
	  transmit_mailbox = CAN_TX_MAILBOX2;
	}
	else
	{
	  transmit_mailbox = CAN_TX_STATUS_NO_EMPTY;
	}

    /* return no mailbox empty */
    if(CAN_TX_STATUS_NO_EMPTY == transmit_mailbox){
        return CAN_TX_STATUS_NO_EMPTY;
    }
	return transmit_mailbox;
}


int shark_can0_send_message(uint32_t can_id, const void*buff, int len){
	can_tx_message_type 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.standard_id	= 0;
		trasnmit_msg.extended_id	= can_frame_id.id;				
		trasnmit_msg.frame_type	= CAN_TFT_DATA;
		trasnmit_msg.id_type	= CAN_ID_EXTENDED;
		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){
	can_tx_message_type trasnmit_msg;
	
	trasnmit_msg.standard_id	= 0;
	trasnmit_msg.extended_id	= can_id;				
	trasnmit_msg.frame_type	= CAN_TFT_DATA;
	trasnmit_msg.id_type	= CAN_ID_EXTENDED;
	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_reset(CAN1);
	crm_periph_clock_enable(CRM_CAN1_PERIPH_CLOCK, FALSE);
}

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();
}

