#include "uart.h"
#include "os/os_task.h"
#include "libs/crc16.h"
#include "libs/logger.h"
#include "libs/utils.h"

#define SHARK_UART_BAUDRATE				230400

#ifdef DEBUG_PORT_UART1
#define SHARK_UART0_com					USART1
#define SHARK_UART0_tx_port				GPIOA
#define SHARK_UART0_tx_pin				GPIO_PIN_2
#define SHARK_UART0_rx_port				GPIOA
#define SHARK_UART0_rx_pin				GPIO_PIN_3
#define SHARK_UART0_irq					USART1_IRQn
#define SHARK_UART0_clk					RCU_USART1
#define SHARK_UART0_tx_gpio_clk			RCU_GPIOA
#define SHARK_UART0_rx_gpio_clk			RCU_GPIOA
#define SHARK_UART0_tx_dma				DMA0
#define SHARK_UART0_tx_dma_ch			DMA_CH6
#define SHARK_UART0_tx_dma_clk			RCU_DMA0
#define SHARK_UART0_rx_dma				DMA0
#define SHARK_UART0_rx_dma_ch			DMA_CH5
#define SHARK_UART0_rx_dma_clk			RCU_DMA0
#define SHARK_UART0_DMA_TX_IRQ          DMA0_Channel6_IRQn
#define UART_DMA_IRQHandler             DMA0_Channel6_IRQHandler
#else
#define SHARK_UART0_com					USART3
#define SHARK_UART0_tx_port				GPIOB
#define SHARK_UART0_tx_pin				GPIO_PINS_10
#define SHARK_UART0_rx_port				GPIOB
#define SHARK_UART0_rx_pin				GPIO_PINS_11
#define SHARK_UART0_irq					USART3_IRQn
#define SHARK_UART0_clk					CRM_USART3_PERIPH_CLOCK
#define SHARK_UART0_tx_gpio_clk			CRM_GPIOB_PERIPH_CLOCK
#define SHARK_UART0_rx_gpio_clk			CRM_GPIOB_PERIPH_CLOCK
#define SHARK_UART0_tx_dma				DMA1
#define SHARK_UART0_tx_dma_ch			DMA1_CHANNEL2
#define SHARK_UART0_tx_dma_clk			CRM_DMA1_PERIPH_CLOCK
#define SHARK_UART0_rx_dma				DMA1
#define SHARK_UART0_rx_dma_ch			DMA1_CHANNEL3
#define SHARK_UART0_rx_dma_clk			CRM_DMA1_PERIPH_CLOCK
#define SHARK_UART0_DMA_TX_IRQ          DMA0_Channel3_IRQn
#define UART_DMA_IRQHandler             DMA1_Channel3_IRQHandler
#endif

// ================================================================================
#define ENABLE_RX_DMA 1
static u8 shark_uart0_tx_cache[SHARK_UART_TX_MEM_SIZE];
static u8 shark_uart0_rx_cache[SHARK_UART_RX_MEM_SIZE];

static shark_uart_t _shark_uart[1];
///static bool uart_no_data = false;
#if ENABLE_RX_DMA==1
#define update_dma_w_pos(uart) circle_update_write_position(&uart->rx_queue, SHARK_UART_RX_MEM_SIZE - SHARK_UART0_rx_dma_ch->dtcnt)
#else
#define update_dma_w_pos(uart){}
#endif

// ================================================================================
static usart_type *_uart_index(usart_type* com){
	return SHARK_UART0;
}
static bool shark_uart_on_rx_frame(shark_uart_t *uart)
{
	u16 crc0 = decode_u16(uart->rx_frame + uart->rx_length);
	u16 crc1 = crc16_get(uart->rx_frame, uart->rx_length);

	if (crc0 != crc1) {
		return false;
	}
	//protocol_recv_frame(_uart_index(uart->uart_com), (char *)uart->rx_frame, uart->rx_length);
	return true;
}

static void shark_uart_rx(shark_uart_t *uart){
	while(1) {
		u8 data = 0;
		update_dma_w_pos(uart);
		if (circle_get_one_data(&uart->rx_queue, &data) != 1) {
			return;
		}
		switch(data){
			case CH_START:
				uart->rx_length = 0;
				uart->escape = false;
				uart->start = true;
				break;
			case CH_END:
				if (uart->rx_length > 2 && uart->rx_length != 0xFFFF){
					uart->rx_length -= 2; //skip crc
					shark_uart_on_rx_frame(uart);
				}
				uart->rx_length = 0xFFFF;
				uart->start = false;
				break;
			case CH_ESC:
				uart->escape = true;
				break;
			default:
				if (uart->escape) {
					uart->escape = false;
					switch (data) {
						case CH_ESC_START:
							data = CH_START;
							break;

						case CH_ESC_END:
							data = CH_END;
							break;

						case CH_ESC_ESC:
							data = CH_ESC;
							break;

						default:
							data = 0xFF;
					}
				}

				if (uart->rx_length < sizeof(uart->rx_frame)) {
					uart->rx_frame[uart->rx_length] = data;
					uart->rx_length++;
				} else {
					uart->rx_length = 0xFFFF;
				}			
		}
	}
}

#define DMA_CHCTL(dma, dma_ch) ((dma_channel_type *)dma_ch)->ctrl
#define DMA_CHMADDR(dma, dma_ch) ((dma_channel_type *)dma_ch)->maddr

#define DMA_CHXCTL_CHEN        BIT(0)                  /*!< channel enable */

static void shark_uart_dma_tx(shark_uart_t *uart)
{
	u32 value = DMA_CHCTL(SHARK_UART0_tx_dma, uart->tx_dma_ch);

	if (value & DMA_CHXCTL_CHEN) {
		if (SET != dma_flag_get(DMA1_FDT1_FLAG)) {
			return;
		}
		dma_flag_clear(DMA1_FDT1_FLAG);
		byte_queue_skip(&uart->tx_queue, uart->tx_length);
		DMA_CHCTL(SHARK_UART0_tx_dma, uart->tx_dma_ch) = value & (~DMA_CHXCTL_CHEN);
	}

	uart->tx_length = byte_queue_peek(&uart->tx_queue);
	if (uart->tx_length > 0) {
		dma_data_number_set(uart->tx_dma_ch,  uart->tx_length);
		DMA_CHMADDR(SHARK_UART0_tx_dma, uart->tx_dma_ch) = (u32) byte_queue_head(&uart->tx_queue);
		DMA_CHCTL(SHARK_UART0_tx_dma, uart->tx_dma_ch) = value | DMA_CHXCTL_CHEN;
	}
}



static void shark_uart_write(shark_uart_t *uart, const u8 *buff, u16 size)
{
	while (size > 0) {
		u16 length = byte_queue_write(&uart->tx_queue, buff, size);

		if (length == size) {
			shark_uart_dma_tx(uart);
			break;
		}

		shark_uart_dma_tx(uart);
		buff += length;
		size -= length;
	}
}

static void shark_uart_write_byte(shark_uart_t *uart, u8 value)
{
	byte_queue_write(&uart->tx_queue, &value, 1);
}


void shark_uart_write_log(char *buffer){
	int len = strlen(buffer);
	shark_uart_t *uart = (_shark_uart+SHARK_UART0);
	if (len > byte_queue_get_free(&uart->tx_queue)){
		return;
	}
	byte_queue_write(&uart->tx_queue, (const u8 *)buffer, len);
	shark_uart_dma_tx(uart);
}

static void shark_uart_tx_dma_init(shark_uart_t *uart){
	dma_init_type dma_init_struct;
	crm_periph_clock_enable(SHARK_UART0_tx_dma_clk, TRUE);
	
	dma_reset(uart->tx_dma_ch);
	dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
	dma_init_struct.memory_inc_enable = TRUE;
	dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
	dma_init_struct.peripheral_base_addr = (u32) &(((usart_type *)uart->uart_com)->dt);
	dma_init_struct.peripheral_inc_enable = FALSE;
	dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
	dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
	dma_init_struct.loop_mode_enable = FALSE;
	dma_init(uart->tx_dma_ch, &dma_init_struct);
	/* config flexible dma for usart2 tx */
	//dma_flexible_config(DMA1, FLEX_CHANNEL1, DMA_FLEXIBLE_UART2_TX);

	usart_dma_transmitter_enable(uart->uart_com, TRUE);
}

#if ENABLE_RX_DMA==1
static void shark_uart_rx_dma_init(shark_uart_t *uart){
	dma_init_type dma_init_struct;
	crm_periph_clock_enable(SHARK_UART0_rx_dma_clk, TRUE);
	/* dma1 channel2 for usart2 rx configuration */
	dma_reset(uart->rx_dma_ch);
	dma_default_para_init(&dma_init_struct);
	dma_init_struct.buffer_size = SHARK_UART_RX_MEM_SIZE;
	dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
	dma_init_struct.memory_base_addr = (uint32_t)shark_uart0_rx_cache;
	dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
	dma_init_struct.memory_inc_enable = TRUE;
	dma_init_struct.peripheral_base_addr = (u32) &(((usart_type *)uart->uart_com)->dt);
	dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
	dma_init_struct.peripheral_inc_enable = FALSE;
	dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
	dma_init_struct.loop_mode_enable = FALSE;
	dma_init(uart->rx_dma_ch, &dma_init_struct);	
	/* config flexible dma for usart2 rx */
	//dma_flexible_config(DMA1, FLEX_CHANNEL2, DMA_FLEXIBLE_UART2_RX);

	usart_dma_receiver_enable(uart->uart_com, TRUE);
}
#endif
static void shark_uart_pin_init(shark_uart_t *uart){
	crm_periph_clock_enable(SHARK_UART0_rx_gpio_clk, TRUE);
	crm_periph_clock_enable(SHARK_UART0_tx_gpio_clk, TRUE);

	gpio_init_type gpio_init_struct;
	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 = SHARK_UART0_tx_pin;
	gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
	gpio_init(SHARK_UART0_tx_port, &gpio_init_struct);
	
	/* configure the usart2 rx pin */
	gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
	gpio_init_struct.gpio_pins = SHARK_UART0_rx_pin;
	gpio_init_struct.gpio_pull = GPIO_PULL_UP;
	gpio_init(SHARK_UART0_rx_port, &gpio_init_struct);
}

static void shark_uart_pin_deinit(shark_uart_t *uart){
	if (_uart_index(uart->uart_com) == SHARK_UART0) {
		gpio_init_type gpio_init_struct;
		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_INPUT;
		gpio_init_struct.gpio_pins = SHARK_UART0_tx_pin;
		gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
		gpio_init(SHARK_UART0_tx_port, &gpio_init_struct);
	
		gpio_init_struct.gpio_pins = SHARK_UART0_rx_pin;
		gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
		gpio_init(SHARK_UART0_rx_port, &gpio_init_struct);
	}
}


static void shark_uart_device_init(shark_uart_t *uart){
	crm_periph_clock_enable(SHARK_UART0_clk, TRUE);
	usart_reset(uart->uart_com);
	/* configure usart2 param */
	usart_init(uart->uart_com, SHARK_UART_BAUDRATE, USART_DATA_8BITS, USART_STOP_1_BIT);
	usart_transmitter_enable(uart->uart_com, TRUE);
	usart_receiver_enable(uart->uart_com, TRUE);
	usart_enable(uart->uart_com, TRUE);
}

static u32 shark_uart_task(void *args)
{
	shark_uart_t *uart = (shark_uart_t *)args;
	if(uart->uart_com != 0) {
		shark_uart_rx(uart);
		shark_uart_dma_tx(uart);
	}
	return 0;
}


void shark_uart_flush(void){
	shark_uart_t *uart = _shark_uart + SHARK_UART0;
	if (uart->uart_com != 0) {
		while(!byte_queue_empty(&uart->tx_queue)) {
			shark_uart_dma_tx(uart);
		}
	}	
}


#if 0
void DMA_Channel1_2_IRQHandler(void){
	shark_uart_t *uart = _shark_uart + SHARK_UART0;
	if (dma_interrupt_flag_get(uart->tx_dma_ch, DMA_INT_FLAG_FTF) != RESET){
		shark_uart_dma_tx(uart);
		dma_interrupt_flag_clear(uart->tx_dma_ch, DMA_INT_FLAG_FTF);
	}
}

void DMA_Channel3_4_IRQHandler(void){
	shark_uart_t *uart = _shark_uart + SHARK_UART1;
	if (dma_interrupt_flag_get(uart->tx_dma_ch, DMA_INT_FLAG_FTF) != RESET){
		shark_uart_dma_tx(uart);
		dma_interrupt_flag_clear(uart->tx_dma_ch, DMA_INT_FLAG_FTF);
	}
}
#endif

static u8 *tx_cache_addr(uart_enum_t uart_no){
	return shark_uart0_tx_cache;
}

static u8 *rx_cache_addr(uart_enum_t uart_no){
	return shark_uart0_rx_cache;
}


void shark_uart_deinit(uart_enum_t uart_no){
	shark_uart_t *uart = _shark_uart + uart_no;
	if (uart->uart_com != 0) {
		usart_enable(uart->uart_com, FALSE);
		usart_reset(uart->uart_com);
		crm_periph_clock_enable(SHARK_UART0_clk, FALSE);
		dma_channel_enable(uart->rx_dma_ch, FALSE);
		dma_channel_enable(uart->tx_dma_ch, FALSE);
		crm_periph_clock_enable(SHARK_UART0_tx_dma_clk, FALSE);
		crm_periph_clock_enable(SHARK_UART0_rx_dma_clk, FALSE);
		shark_uart_pin_deinit(uart);
	}
#if ENABLE_RX_DMA==0
	nvic_irq_disable(SHARK_UART0_irq);	
#endif
}


bool shark_uart_timeout(void){
#if UART_NUM==2	
	return (_shark_uart[0].uart_no_data && _shark_uart[1].uart_no_data)?TRUE:FALSE;
#else
	return (_shark_uart[0].uart_no_data)?TRUE:FALSE;
#endif
}
void shark_uart_init(uart_enum_t uart_no)
{
	shark_uart_t *uart = _shark_uart + uart_no;
	uart->escape = false;
	uart->rx_length = 0;
	uart->tx_length = 0;
	uart->uart_com = SHARK_UART0_com;

	circle_buffer_init(&uart->rx_queue, rx_cache_addr(uart_no), SHARK_UART_RX_MEM_SIZE);
	byte_queue_init(&uart->tx_queue,tx_cache_addr(uart_no), SHARK_UART_TX_MEM_SIZE);

	uart->rx_dma_ch = SHARK_UART0_rx_dma_ch;
	uart->tx_dma_ch = SHARK_UART0_tx_dma_ch;

	shark_uart_pin_init(uart);
	shark_uart_device_init(uart);
#if ENABLE_RX_DMA==1
	shark_uart_rx_dma_init(uart);
#endif
	shark_uart_tx_dma_init(uart);
	usart_enable(uart->uart_com, TRUE);

	shark_task_create(shark_uart_task, uart);
#if ENABLE_RX_DMA==0
	nvic_irq_enable(SHARK_UART0_irq, UART_IRQ_PRIORITY, 0);
#endif
	uart->uart_no_data = false;
}

#if ENABLE_RX_DMA==0
void USART3_IRQHandler(void){
	if(usart_flag_get(USART0, USART_FLAG_RBNE) == SET){
		shark_uart_t *uart = _shark_uart + SHARK_UART0;
		u8 c = usart_data_receive(USART0);
		circle_put_one_data(&uart->rx_queue, c);
	}
}
#endif


void UART_DMA_IRQHandler(void) {
	
}

static void shark_uart_write_byte_esc(shark_uart_t *uart, u8 value)
{
	switch (value) {
	case CH_START:
		shark_uart_write_byte(uart, CH_ESC);
		value = CH_ESC_START;
		break;

	case CH_END:
		shark_uart_write_byte(uart, CH_ESC);
		value = CH_ESC_END;
		break;

	case CH_ESC:
		shark_uart_write_byte(uart, CH_ESC);
		value = CH_ESC_ESC;
		break;
	}

	shark_uart_write_byte(uart, value);
}

static void shark_uart_write_esc(shark_uart_t *uart, const u8 *buff, u16 length)
{
	const u8 *buff_end;

	for (buff_end = buff + length; buff < buff_end; buff++) {
		shark_uart_write_byte_esc(uart, *buff);
	}
}

static void shark_uart_tx_start(shark_uart_t *uart)
{
	shark_uart_write_byte(uart, CH_START);
	uart->tx_crc16 = 0;
}

static void shark_uart_tx_continue(shark_uart_t *uart, const void *buff, u16 length)
{
	shark_uart_write_esc(uart, (const u8 *) buff, length);
	uart->tx_crc16 = crc16_update(uart->tx_crc16, (const u8 *) buff, length);
}

static void shark_uart_tx_end(shark_uart_t *uart)
{
	shark_uart_write_esc(uart, (u8 *)&uart->tx_crc16, sizeof(uart->tx_crc16));
	shark_uart_write_byte(uart, CH_END);
}

void shark_uart_write_frame(uart_enum_t uart_no, uint8_t *bytes, int len){
	shark_uart_t *uart = _shark_uart + uart_no;
	shark_uart_tx_start(uart);
	shark_uart_tx_continue(uart, bytes, len);
	shark_uart_tx_end(uart);
	shark_uart_dma_tx(uart);
}

void shark_uart_frame_start(uart_enum_t uart_no, uint8_t *bytes, int len){
	shark_uart_t *uart = _shark_uart + uart_no;
	shark_uart_tx_start(uart);
	shark_uart_tx_continue(uart, bytes, len);
}

void shark_uart_frame_continue(uart_enum_t uart_no, uint8_t *bytes, int len){
	shark_uart_t *uart = _shark_uart + uart_no;
	shark_uart_tx_continue(uart, bytes, len);
}

void shark_uart_frame_end(uart_enum_t uart_no){
	shark_uart_tx_end(_shark_uart + uart_no);
}

void shark_uart_write_bytes(uart_enum_t uart_no, u8 *buff, u16 size){
	shark_uart_write(_shark_uart + uart_no, buff, size);
}

int fputc(int c, FILE *fp){
	shark_uart_write_byte(_shark_uart+SHARK_UART0, (u8)c);
	return 1;
}

