#include <zephyr.h>
#include <sys/printk.h>
#include <string.h>
#include <drivers/uart.h>

static const char stop[] =  "foqs";

#if defined(CONFIG_BOARD_NRF9160DK_NRF9160NS)
#define UART_DEV "UART_2"
#elif defined(CONFIG_BOARD_NRF9160DK_NRF52840) || (CONFIG_BOARD_NRF52840DK_NRF52840) 
#define UART_DEV "UART_1"
#endif

#define UART_HANDLER_STACK_SIZE 500

K_THREAD_STACK_DEFINE(uart_handler_stack, UART_HANDLER_STACK_SIZE);
static struct k_thread uart_handler_thread;

#define UART_INPUT_BUF_SIZE 8192
#define UART_OUTPUT_BUF_SIZE 40

struct input_buffer_type{
    u8_t buffer[UART_INPUT_BUF_SIZE];
    int offs; 
};

static struct input_buffer_type input_buffer;

struct output_buffer_type{
    u8_t buffer[UART_OUTPUT_BUF_SIZE];
    int offs;
};

static struct output_buffer_type output_buffer;

static struct device *uart_dev;

K_SEM_DEFINE(uart_sem, 0, 1);



static void uart_input_handler(void *dummy1, void *dummy2, void *dummy3)
{
    while (1) {

		k_sem_take(&uart_sem, K_FOREVER);
        printk("Received %d bytes: ", input_buffer.offs);
        for(int i=0; i<input_buffer.offs - sizeof(stop) + 1; i++){
            printk("%c", input_buffer.buffer[i]);
        }
        printk("\n\n");
	}
}

int uart_start(void){
    k_thread_create(&uart_handler_thread, uart_handler_stack,
        K_THREAD_STACK_SIZEOF(uart_handler_stack),
        uart_input_handler, NULL, NULL, NULL,
        K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);
        k_thread_name_set(&uart_handler_thread, "uart handler");
    return 0;
}

static void append_to_buffer(void* append_data, int len){
    memcpy(output_buffer.buffer + output_buffer.offs, append_data, len);
    output_buffer.offs+=len;
}

static int send_data()
{
	if (output_buffer.offs == 0) {
		return 0;
	}
    printk("Send %d bytes:", output_buffer.offs);
	for(int i = 0; i < output_buffer.offs; i++){
        printk("%c", output_buffer.buffer[i]);
		uart_poll_out(uart_dev, output_buffer.buffer[i]);
	}
    printk("\n");

	return 0;
}

static void uart_cb(struct device *x)
{
    static int bytes_recv = 0;
	u8_t byte;
	int rx;

	uart_irq_update(x);

	if (!uart_irq_rx_ready(x)) {
		return;
	}
	while (true) {
		rx = uart_fifo_read(x, &byte, 1);

		if (bytes_recv < sizeof(input_buffer.buffer)) {
			input_buffer.buffer[bytes_recv] = byte;
		}
		if (strncmp(&input_buffer.buffer[bytes_recv-3], stop, 4) == 0) {
            input_buffer.offs = bytes_recv;
            bytes_recv = 0;
			//printk("Stop received, current transmission is complete\n");
			k_sem_give(&uart_sem);
			break;
		}
		if (rx != 1) {
			break;
		}
		
		bytes_recv++;
	}
    //printk("\n");
}

int init_uart(void)
{
    uart_dev = device_get_binding(UART_DEV);
	uart_irq_callback_set(uart_dev, uart_cb);
	uart_irq_rx_enable(uart_dev); 
	printk("UART initialized\n"); 

    output_buffer.offs = 0; 
    return 0; 

}

void main(void)
{
	printk("UART sample started, with board %s\n", CONFIG_BOARD);
	init_uart();
    uart_start();

    char hello[] = CONFIG_BOARD;

    while(true){
       append_to_buffer(&hello, sizeof(hello)-1);
       append_to_buffer((void*)stop, sizeof(stop)-1);
       send_data();
       output_buffer.offs = 0;
       k_sleep(K_SECONDS(5));   
    }
}
