#include "lwip/api.h"
#include "PinNames.h"
#include "sockets.h"
#include "uart_socket.h"
#include "autoconf.h"

#if CONFIG_UART_SOCKET

#define UART_SOCKET_USE_DMA_TX 1
/***********************************************************************
 *                                Macros                               *
 ***********************************************************************/
#define uart_printf printf
#define uart_print_data(x, d, l) \
do{\
	int i;\
	uart_printf("\n%s: Len=%d\n", (x), (l));\
	for(i = 0; i < (l); i++)\
		uart_printf("%02x ", (d)[i]);\
	uart_printf("\n");\
}while(0);

/************************************************************************
 *                        extern funtions                               *
 ************************************************************************/
extern void lwip_selectevindicate(int fd);
extern void lwip_setsockrcvevent(int fd, int rcvevent);
extern int lwip_allocsocketsd();

/*************************************************************************
*                               uart releated  fuantions                          *
*************************************************************************/
static void uart_irq(uint32_t id, SerialIrq event)
{
	uart_socket_t *u = (uart_socket_t *)id;

	if(event == RxIrq) {
		if( u->rx_start == 0 ){
			RtlUpSemaFromISR(&u->action_sema);	//up action semaphore 
			u->rx_start = 1; // set this flag in uart_irq to indicate data recved
		}
		u->recv_buf[u->prxwrite++] = serial_getc(&u->sobj);
		if(u->prxwrite > (UART_RECV_BUFFER_LEN -1)){	//restart from  head if  reach tail
			u->prxwrite = 0;
			u->rxoverlap = 1;							//set overlap indicated that overlaped 
		}
		if(u->rxoverlap && (u->prxwrite + 1) > u->prxread ){
			u->prxread = u->prxwrite;		//if pwrite overhead pread ,pread is always flow rwrite 
		}
		u->last_update =  xTaskGetTickCountFromISR();	// update tick everytime recved data
	}

	if(event == TxIrq){
	}
}

static void uart_send_stream_done(uint32_t id)
{
	uart_socket_t *u = (uart_socket_t *)id;
	
	//u->tx_start = 0;
	memset(u->send_buf,0, UART_SEND_BUFFER_LEN);	//zero set uart_send_buf
	RtlUpSemaFromISR(&u->tx_sema);
	RtlUpSemaFromISR(&u->dma_tx_sema);
}

static int uart_send_stream(uart_socket_t *u, char* pbuf, int len)
{
	int ret;
	
	if(!len || (!pbuf) || !u){
		uart_printf("input error,size should not be null\r\n");
		return -1;
	}

#if UART_SOCKET_USE_DMA_TX
	while(RtlDownSema(&u->dma_tx_sema) == pdTRUE){			
	    	ret = serial_send_stream_dma(&u->sobj, pbuf, len);
	    	if(ret != HAL_OK){
			RtlUpSema(&u->dma_tx_sema);
			return -1;
		}else{
			return 0;
	    	}
	}
#else
	while (len){
		serial_putc(&u->sobj, *pbuf);
       	len--;
		pbuf++;
	}
#endif	

	return 0;
}

static s32 uart_wait_rx_complete(uart_socket_t *u)
{
	s32 tick_current = xTaskGetTickCount();

	while((tick_current -u->last_update) < UART_MAX_DELAY_TIME ){
		vTaskDelay(5);
		tick_current = xTaskGetTickCount();
	}	
	return 0;
}

static void uart_action_handler(void* param)
{
	uart_socket_t *u = (uart_socket_t*)param;
	if(!u)
		goto Exit;
	
	while(RtlDownSema(&u->action_sema) == pdTRUE) {
		if(u->fd == -1)
			goto Exit;
		if(u->rx_start){
			/* Blocked here to wait uart rx data completed */
			uart_wait_rx_complete(u);

			/* As we did not register netconn callback function.,so call lwip_selectevindicate unblocking select */
			lwip_setsockrcvevent(u->fd, 1);
			lwip_selectevindicate(u->fd);	//unblocking select()
			u->rx_start = 0;
		}
		if(u->tx_start){
#if 1			
			if (u->tx_bytes < 128) {
				uart_print_data("TX:", u->send_buf, u->tx_bytes);
			} else {
				uart_printf("\nTX:: Len=%d\n", u->tx_bytes);
			}
#endif
			//if(serial_send_stream_dma(&u->sobj, (char*)u->send_buf, u->tx_bytes) == -1){
			if(uart_send_stream(u, (char*)u->send_buf, u->tx_bytes) == -1){
				uart_printf("uart send data error!");
			} else {
				u->tx_start = 0;
#if (UART_SOCKET_USE_DMA_TX == 0)
				memset(u->send_buf,0, UART_SEND_BUFFER_LEN);	//zero set uart_send_buf
				RtlUpSema(&u->tx_sema);
#endif				
			}
		}
	}
Exit:	
	vTaskDelete(NULL);
}


uart_socket_t* uart_open(uart_set_str *puartpara)
{
	PinName uart_tx = PA_7;//PA_4; //PA_7
	PinName uart_rx = PA_6;//PA_0; //PA_6
	uart_socket_t *u;

	u = (uart_socket_t *)RtlZmalloc(sizeof(uart_socket_t));
	if(!u){
		uart_printf("%s(): Alloc memory for uart_socket failed!\n", __func__);
		return NULL;
	}
	
	/*initial uart */
	serial_init(&u->sobj, uart_tx,uart_rx);
	serial_baud(&u->sobj,puartpara->BaudRate);
	serial_format(&u->sobj, puartpara->number, (SerialParity)puartpara->parity, puartpara->StopBits);

	/*uart irq handle*/
	serial_irq_handler(&u->sobj, uart_irq, (int)u);
	serial_irq_set(&u->sobj, RxIrq, 1);
	serial_irq_set(&u->sobj, TxIrq, 1);

#if UART_SOCKET_USE_DMA_TX
   	serial_send_comp_handler(&u->sobj, (void*)uart_send_stream_done, (uint32_t)u);
#endif

	/*alloc a socket*/
 	u->fd = lwip_allocsocketsd();
	if(u->fd == -1){
		uart_printf("Failed to alloc uart socket!\n");
		goto Exit2;
	}
	/*init uart related semaphore*/
	RtlInitSema(&u->action_sema, 0);
	RtlInitSema(&u->tx_sema, 1);
	RtlInitSema(&u->dma_tx_sema, 1);
	
	/*create uart_thread to handle send&recv data*/
	{
#define	UART_ACTION_STACKSIZE 256 //USE_MIN_STACK_SIZE modify from 512 to 256
#define	UART_ACTION_PRIORITY	1
		if(xTaskCreate(uart_action_handler, ((const char*)"uart_action"), UART_ACTION_STACKSIZE, u, UART_ACTION_PRIORITY, NULL) != pdPASS){
			uart_printf("%s xTaskCreate(uart_action) failed", __FUNCTION__);
			goto Exit1;
		}
	}
	return u;
Exit1:
	/* Free uart related semaphore */
	RtlFreeSema(&u->action_sema);
	RtlFreeSema(&u->tx_sema);	
	RtlFreeSema(&u->dma_tx_sema);		
Exit2:
	RtlMfree((u8*)u, sizeof(uart_socket_t));
	return NULL;
}

int uart_close(uart_socket_t *u)
{
	if(!u){
		uart_printf("uart_close(): u is NULL!\r\n");
		return -1;
	}
	/* Close uart socket */
	if(lwip_close(u->fd) == -1){
		uart_printf("%s(): close uart failed!", __func__);
	}
	/* Delete uart_action task */
	u->fd = -1;
	RtlUpSema(&u->action_sema);
	RtlMsleepOS(20);

	/* Free uart related semaphore */
	RtlFreeSema(&u->action_sema);
	RtlFreeSema(&u->tx_sema);
	RtlFreeSema(&u->dma_tx_sema);
	
	/* Free serial */
	serial_free(&u->sobj);
	
	RtlMfree((u8 *)u, sizeof(uart_socket_t));

	return 0;
}

int uart_read(uart_socket_t *u, void *read_buf, size_t size)
{
	/*the same as socket*/
	int read_bytes = 0;
	int pread_local,pwrite_local;
	char *ptr = (char *)read_buf;

	uart_printf("==>uart_read()\n");
	if(!size || !read_buf || !u){
		uart_printf("uart_read(): input error,size should not be null\r\n");
		return -1;
	}
	
	pread_local = u->prxread;
	pwrite_local = u->prxwrite;
	/*calculate how much data not read */
	if(!u->rxoverlap){
		read_bytes = pwrite_local - pread_local;
	} else {
		read_bytes = (UART_RECV_BUFFER_LEN - pread_local) + pwrite_local;
	}
	/*decide how much data shoule copy to application*/
	if(size < read_bytes)
		read_bytes = size;
	
	if(!u->rxoverlap){
		memcpy(ptr, (u->recv_buf+ pread_local), read_bytes );
	} else {
		uart_printf("uart recv buf is write through!\n");
		if((pread_local + read_bytes) > UART_RECV_BUFFER_LEN){
			memcpy(ptr,(u->recv_buf+ pread_local), (UART_RECV_BUFFER_LEN-pread_local));
			memcpy(ptr+(UART_RECV_BUFFER_LEN-pread_local), u->recv_buf, read_bytes-(UART_RECV_BUFFER_LEN- pread_local));
		} else
			memcpy(ptr,(u->recv_buf+ pread_local), read_bytes);
	}
	lwip_setsockrcvevent(u->fd, 0);
	
	if((pread_local + read_bytes) >= UART_RECV_BUFFER_LEN){		//update pread
		u->prxread = (pread_local + read_bytes) - UART_RECV_BUFFER_LEN;
		u->rxoverlap = 0;		//clean overlap flags
	} else
		u->prxread = pread_local + read_bytes;
	
	return read_bytes;

}


int uart_write(uart_socket_t *u, void *pbuf, size_t size)
{
	if(!size || !pbuf || !u){
		uart_printf("input error,please check!");
		return -1;
	}
	if(RtlDownSema(&u->tx_sema)){
		//uart_printf("[%d]:uart_write %d!\n", xTaskGetTickCount(), size);
		memcpy(u->send_buf, pbuf, size);
		u->tx_bytes = size;
		u->tx_start = 1;	//set uart tx start 
		RtlUpSema(&u->action_sema);	// let uart_handle_run through
	} else {
		uart_printf("uart write buf error!");
		return -1;
	}

	return size;
}

void uart_socket_example(void *param)
{
	char tx_data[] = {0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
	uart_set_str uartset;
	struct timeval tv;
	fd_set readfds;
	int read_len = 0, count = 0;
	int ret = 0;
	char rxbuf[512];
	int uart_fd;
	uart_socket_t *uart_socket = NULL;

	uartset.BaudRate = 9600;
	uartset.number = 8;
	uartset.StopBits = 0;
	uartset.FlowControl = 0;
	uartset.parity = 0;
	strcpy(uartset.UartName, "uart0");

	uart_socket = uart_open(&uartset);
	if(uart_socket == NULL){
		uart_printf("Init uart socket failed!\n");
		goto Exit;
	}
	uart_fd = uart_socket->fd;
	uart_printf("\nOpen uart socket: %d\n", uart_fd);
	while(1)
	{
		FD_ZERO(&readfds);
		FD_SET(uart_fd, &readfds);
		tv.tv_sec = 0;
		tv.tv_usec = 20000;
		if(count++ == 50){
			uart_write(uart_socket, tx_data, sizeof(tx_data));
			//uart_print_data("TX:", tx_data, sizeof(tx_data));
			count = 0;
		}
		ret = select(uart_fd + 1, &readfds, NULL, NULL, &tv);
		//uart_printf("[%d] select ret = %x count=%d\n", xTaskGetTickCount(), ret, count);	
		if(ret > 0)
		{
			if(FD_ISSET(uart_fd, &readfds))
			{
				read_len = uart_read(uart_socket, rxbuf, sizeof(rxbuf));
				if(read_len > 0)
				{
					uart_print_data("RX:", rxbuf, read_len);
					if(rtl_strncmp(rxbuf, "close", 5) == 0)
						break;
				}
			}
			//else for other sockets
		}
	}
	uart_printf("Exit uart socket example!\n");
	uart_close(uart_socket);
Exit:
	vTaskDelete(NULL);
}

void uart_socket()
{
#define 	UART_SOCKET_STACK_SIZE		512
#define 	UART_SOCKET_PRIORITY		1
	if(xTaskCreate(uart_socket_example, "uart_socket", UART_SOCKET_STACK_SIZE, NULL, UART_SOCKET_PRIORITY, NULL) != pdPASS)
		uart_printf("%s xTaskCreate failed", __FUNCTION__);
}

#endif // #if CONFIG_UART_SOCKET