#include "usart.h"
#include "common.h"
#include "parameters.h"
#include "portgw.h"


#define DEBUG_RBUF_SIZE 256		// gcc-4.7 cannot handle const variable sizes

#ifdef DEBUG
static uint8_t debug_rx_fifo[DEBUG_RBUF_SIZE];
static uint8_t debug_tx_fifo[DEBUG_RBUF_SIZE];
#endif


#define FIN { 0 }, { 0 }, 0, 0
#define UARTS_LEN   5


static uart_t uarts[UARTS_LEN] = {
	{
        // RS485_1
		true,
		{
			BRD_9600,
			NO_PAR,
			DATABITS_8,
			STOP_1
		},
		UART7,
		NULL,
		NULL,
		0,
		GetUsartSettingsInt,
		{
			NULL,
			NULL,
		},
		SetUsartTransportQueue,
		FIN
	}, 
    {
        // GSM 
		false,
		{
			BRD_9600,
			NO_PAR,
			DATABITS_8,
			STOP_1
		},
		USART2,
		NULL,
		NULL,
		0,
		GetUsartSettingsInt,
		{
			NULL,
			NULL,
		},
		SetUsartTransportQueue,
		FIN
	},    
    {
        // RS485_2 
		true,
		{
			BRD_9600,
			NO_PAR,
			DATABITS_8,
			STOP_1
		},
		USART3,
		NULL,
		NULL,
		0,
		GetUsartSettingsInt,
		{
			NULL,
			NULL,
		},
		SetUsartTransportQueue,
		FIN
	}, 
    {
        // RS232_1
		true,
		{
			BRD_9600,
			NO_PAR,
			DATABITS_8,
			STOP_1
		},
		UART4,
		NULL,
		NULL,
		0,
		GetUsartSettingsInt,
		{
			NULL,
			NULL,
		},
		SetUsartTransportQueue,
		FIN
	}, 
    {
        // RS232_2
		true,
		{
			BRD_9600,
			NO_PAR,
			DATABITS_8,
			STOP_1
		},
		UART5,
		NULL,
		NULL,
		0,
		GetUsartSettingsInt,
		{
			NULL,
			NULL,
		},
		SetUsartTransportQueue,
		FIN
	}
};
#undef FIN


void uart_hw_init(usart_type *uart, uart_settings_t *sett);

void uart_proddefault_init(uint8_t indx_uarts, uint8_t portgw_queue) {
	uarts[indx_uarts].setting.baud = BRD_9600;
	uarts[indx_uarts].setting.databits = DATABITS_8;
	uarts[indx_uarts].setting.parity = NO_PAR;
	uarts[indx_uarts].setting.stopbits = STOP_1;
	uart_hw_init( uarts[indx_uarts].addr, &uarts[indx_uarts].setting);
    
    uarts[indx_uarts].trans_queue.rxQ = &PortGwRxQ[portgw_queue];;
	uarts[indx_uarts].trans_queue.txQ = &PortGwTxQ[portgw_queue];;
}

void uart_hw_init(usart_type *uart, uart_settings_t *sett) {
    gpio_init_type gpio_initstructure;
	uint32_t baud;
	usart_data_bit_num_type wordlen;
	usart_parity_selection_type parity;
	usart_stop_bit_num_type stop;

	switch(sett->baud)
	{
		case BRD_1200:
			baud = 1200;
		break;
		case BRD_2400:
			baud = 2400;
		break;
		case BRD_4800:
			baud = 4800;
		break;
		case BRD_9600:
			baud = 9600;
		break;
		case BRD_19200:
			baud = 19200;
		break;
		case BRD_38400:
			baud = 38400;
		break;
		case BRD_57600:
			baud = 57600;
		break;
		case BRD_115200:
			baud = 115200;
		break;
		case BRD_230400:
			baud = 230400;
		break;
		case BRD_460800:
			baud = 460800;
		break;
		default:
		break;
	}

	switch(sett->databits)
	{
		case DATABITS_7:
			wordlen = USART_DATA_8BITS;
		break;
		case DATABITS_8:
			if ((sett->parity == EVEN_PAR) ||
                (sett->parity == ODD_PAR)) {
                wordlen = USART_DATA_9BITS;
            } else {
                wordlen = USART_DATA_8BITS;
            }
		break;
		default:
		break;
	}	

	switch(sett->parity)
	{
		case NO_PAR:
            parity = USART_PARITY_NONE;
		break;
		case ODD_PAR:
			parity = USART_PARITY_ODD;
		break;
		case EVEN_PAR:
			parity = USART_PARITY_EVEN;
		break;
		default:
		break;
	}	

	switch(sett->stopbits)
	{
		case STOP_1:
			stop = USART_STOP_1_BIT;
		break;
		case STOP_2:
			stop = USART_STOP_2_BIT;
		break;
		default:
		break;
	}	
    
    // RS485_1
	if (uart == UART7) {
        crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE);
        crm_periph_clock_enable(CRM_UART7_PERIPH_CLOCK, TRUE);
        
        gpio_default_para_init(&gpio_initstructure);
    
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
        gpio_initstructure.gpio_pins = GPIO_PINS_8;
        gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
        gpio_init(GPIOE, &gpio_initstructure);
        
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
        gpio_initstructure.gpio_pins = GPIO_PINS_7;
        gpio_initstructure.gpio_pull = GPIO_PULL_UP;
        gpio_init(GPIOE, &gpio_initstructure);
        
        nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
        nvic_irq_enable(UART7_IRQn, 6, 0);
	}
    // RS485_2
	if (uart == USART3) {
        crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
		crm_periph_clock_enable(CRM_USART3_PERIPH_CLOCK, TRUE);
        crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
        gpio_pin_remap_config(USART3_GMUX_0011, TRUE);
        
        gpio_default_para_init(&gpio_initstructure);
        
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
        gpio_initstructure.gpio_pins = GPIO_PINS_8;
        gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
        gpio_init(GPIOD, &gpio_initstructure);
        
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
        gpio_initstructure.gpio_pins = GPIO_PINS_9;
        gpio_initstructure.gpio_pull = GPIO_PULL_UP;
        gpio_init(GPIOD, &gpio_initstructure);
        
        nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
        nvic_irq_enable(USART3_IRQn, 6, 0);
	}
    // RS232_1
    if (uart == UART4) {
        crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
        crm_periph_clock_enable(CRM_UART4_PERIPH_CLOCK, TRUE);
        
        gpio_default_para_init(&gpio_initstructure);
        
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
        gpio_initstructure.gpio_pins = GPIO_PINS_10;
        gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
        gpio_init(GPIOC, &gpio_initstructure);
        
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
        gpio_initstructure.gpio_pins = GPIO_PINS_11;
        gpio_initstructure.gpio_pull = GPIO_PULL_UP;
        gpio_init(GPIOC, &gpio_initstructure);
        
        nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
        nvic_irq_enable(UART4_IRQn, 6, 0);
    }
    // RS232_2
	if (uart == UART5) {
        crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
        crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
        crm_periph_clock_enable(CRM_UART5_PERIPH_CLOCK, TRUE);
        
        gpio_default_para_init(&gpio_initstructure);

        // configure the uart tx pin
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
        gpio_initstructure.gpio_pins = GPIO_PINS_12;
        gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
        gpio_init(GPIOC, &gpio_initstructure);
        
        gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
        gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
        gpio_initstructure.gpio_pins = GPIO_PINS_2;
        gpio_initstructure.gpio_pull = GPIO_PULL_UP;
        gpio_init(GPIOD, &gpio_initstructure);
        
        nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
        nvic_irq_enable(UART5_IRQn, 6, 0);
	}

    usart_init(uart, baud, wordlen, stop);
    usart_parity_selection_config(uart, parity);
    usart_transmitter_enable(uart, TRUE);
    usart_receiver_enable(uart, TRUE);
    usart_enable(uart, TRUE);
    
    usart_interrupt_enable(uart, USART_RDBF_INT, TRUE);
    usart_interrupt_enable(uart, USART_TDC_INT, FALSE);
    usart_enable(uart, TRUE); 
}


void usart_init_struct(void) {
	for (int i = 0; i < UARTS_LEN; ++i) {
		if (!uarts[i].enabled) continue;
		if(uarts[i].tx_fifo != NULL && uarts[i].rx_fifo != NULL){
			uarts[i].rx_buf_sem = xSemaphoreCreateCounting(uarts[i].buf_size, 0);
			uarts[i].tx_buf_sem = xSemaphoreCreateCounting(uarts[i].buf_size, (uarts[i].buf_size-1));
			rbuf8_init(&uarts[i].tx_buf, uarts[i].tx_fifo, uarts[i].buf_size);
			rbuf8_init(&uarts[i].rx_buf, uarts[i].rx_fifo, uarts[i].buf_size);
		}
		if(uarts[i].set_uart_queue != NULL){
			uarts[i].set_uart_queue(&uarts[i].trans_queue, i);
		}
		uart_hw_init(uarts[i].addr, &uarts[i].setting);
	}

#ifdef PRINTF_CUSTOM    
	init_printf(NULL, putc_);
#endif
}


void usart_reinit(void)
{
	uart_settings_t uart_settings_new;

	for (int i = 0; i < UARTS_LEN; ++i) {
		if (!uarts[i].enabled) continue;
		if(uarts[i].get_uart_settings != NULL){
			if(uarts[i].set_uart_queue != NULL){
				uarts[i].set_uart_queue(&uarts[i].trans_queue, i);
			}
			if(uarts[i].get_uart_settings(&uart_settings_new, i)){
				if(uart_settings_new.baud != uarts[i].setting.baud
				|| uart_settings_new.databits != uarts[i].setting.databits
				|| uart_settings_new.parity != uarts[i].setting.parity
				|| uart_settings_new.stopbits != uarts[i].setting.stopbits){
					uarts[i].setting.baud = uart_settings_new.baud;
					uarts[i].setting.databits = uart_settings_new.databits;
					uarts[i].setting.parity = uart_settings_new.parity;
					uarts[i].setting.stopbits = uart_settings_new.stopbits;
					uart_hw_init(uarts[i].addr, &uarts[i].setting);
				}
			}
		}
	}
}


void transport_enable_tx(uint8_t indx_uarts) {
    usart_interrupt_enable(uarts[indx_uarts].addr, USART_TDBE_INT, TRUE);
}


static inline void transport_irq_handler(uint8_t indx_uarts) {
	uint16_t c;
	static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
	if (uarts[indx_uarts].addr->sts & USART_ROERR_FLAG) {
		c = uarts[indx_uarts].addr->dt;
		//DBG printf("overrunRS485\r\n");
	}
	if (uarts[indx_uarts].addr->sts & USART_PERR_FLAG) {
		c = uarts[indx_uarts].addr->dt;
		//DBG printf("[rs485] parity fail: 0x%X\r\n", c);
	}
    if (usart_flag_get(uarts[indx_uarts].addr, USART_TDBE_FLAG) != RESET) {
      
		if(uarts[indx_uarts].trans_queue.txQ != NULL){
			if (xQueueReceiveFromISR(*(uarts[indx_uarts].trans_queue.txQ), &c, &xHigherPriorityTaskWoken) == pdTRUE) {
				uarts[indx_uarts].addr->dt = c;
				//DBG printf("wr: 0x%x\r\n", c);
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
			} else {
                uarts[indx_uarts].addr->ctrl1_bit.tdbeien = FALSE;
				uarts[indx_uarts].addr->ctrl1_bit.rdbfien = TRUE;
			}
		}
		else{
			uarts[indx_uarts].addr->ctrl1_bit.tdbeien = FALSE;
            uarts[indx_uarts].addr->ctrl1_bit.rdbfien = TRUE;
		}
		//USART_ClearITPendingBit(uarts[indx_uarts].addr, USART_IT_TXE);
	}
	if (usart_flag_get(uarts[indx_uarts].addr, USART_RDBF_FLAG) != RESET) {
		c = uarts[indx_uarts].addr->dt;
        if (uarts[indx_uarts].addr->ctrl1_bit.pen == TRUE) {  
			if (uarts[indx_uarts].addr->ctrl1_bit.dbn == TRUE) {
				/* 8-bit data */
				c &= 0xFF;
			} else {
				/* 7-bit data */
				c &= 0x7F;
			}
		}
		if(uarts[indx_uarts].trans_queue.rxQ != NULL){
			if (xQueueSendFromISR(*(uarts[indx_uarts].trans_queue.rxQ), &c, &xHigherPriorityTaskWoken) == pdTRUE) {
				//DBG printf("wr: 0x%x\r\n", c);
			} else {
				//DBG printf("[uart] rx overflow\r\n");
			}
			portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
		}
		//USART_ClearITPendingBit(uarts[indx_uarts].addr, USART_IT_RXNE);
	}
}


// RS485_1
void UART7_IRQHandler(void)
{
    transport_irq_handler(0);
}
  

// RS485_2
void USART3_IRQHandler(void)
{
    transport_irq_handler(2);
}


// RS232_1
void UART4_IRQHandler(void)
{
    transport_irq_handler(3);
}


// RS232_2
void UART5_IRQHandler(void)
{
    transport_irq_handler(4);
}
