#include "serial.h"
#include <stdbool.h>
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include "main.h"
#include "ble.h"
//#include <util.h>

LOG_MODULE_REGISTER(serial_c, CONFIG_BT_CUSTOM_CLIENT_LOG_LEVEL);

#define UART_RX_TIMEOUT 50000 /* Wait for RX complete event time in microseconds. */
#define BYTE_SEND_TIME_US 87
#define SLACK 10
#define SEND_DELAY_TIMEOUT K_USEC((UART_BUF_SIZE + SLACK) * BYTE_SEND_TIME_US) 

K_SEM_DEFINE(uart_rx_sem, 0, 1);
K_SEM_DEFINE(uart_tx_sem, 0, 1);
K_SEM_DEFINE(uart_sent_sem, 0, 1);
K_SEM_DEFINE(custom_write_sem, 0, 1);

struct uart_data_in_struct
{
	uint16_t len;
	uint8_t data[UART_BUF_SIZE];
}; 

// RJW _ Need to get rid of this monstrosity
struct uart_data_t 
{
	void *fifo_reserved;
	uint8_t  data[UART_BUF_SIZE];
	uint16_t len;
};

static uint8_t uart_out_buff[UART_BUF_SIZE];

// UART payload buffer element size. Tjhis is the size of a characteristic

static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));

#define NUMBER_OF_UART_RX_ARRAYS 2

volatile static bool uart_data_in_available;
volatile static uint8_t current_uart_array_number;
static struct uart_data_in_struct uart_data_in_array[NUMBER_OF_UART_RX_ARRAYS];
volatile static struct uart_data_in_struct latest_uart_data; 
static uint8_t uart_out_buf[UART_BUF_SIZE];

#define UART_IN_CIRC_BUF_LEN 1024
static uint8_t uart_in_circular_buffer[UART_IN_CIRC_BUF_LEN];
static uint16_t uart_in_circular_buffer_in_index;
static uint16_t uart_in_circular_buffer_out_index;

void set_ble_data_sent(void)
{

	k_sem_give(&custom_write_sem);

}

// serial_thread for incoming data over the UART from ther PC than then goes out to the XLRT
void serial_thread(void)
{
	uint16_t uart_out_index;
	uart_out_index = 0;
	
	while (1)
	{

		k_sem_take(&uart_rx_sem, K_FOREVER);
		if (uart_in_circular_buffer_in_index != uart_in_circular_buffer_out_index) 
		{				
			while ((uart_in_circular_buffer_in_index != uart_in_circular_buffer_out_index) && (uart_out_index < UART_BUF_SIZE))
			{
				uart_out_buf[uart_out_index++] = uart_in_circular_buffer[uart_in_circular_buffer_out_index++];
				if (uart_in_circular_buffer_out_index >= UART_IN_CIRC_BUF_LEN)
					uart_in_circular_buffer_out_index = 0;					
			}
			send_ble_data_to_xlrt(&uart_out_buf[0], uart_out_index);
			uart_out_index = 0;
			k_sem_take(&custom_write_sem, SEND_DELAY_TIMEOUT); // Then wait until it's been sent out over BLE or timed out
		}			
	}
}

/*
* uart_cb is a callback function that is the ISR for the UART
* it is set up in the UART initialisation function  APIs
* It can be called for a number of different types of interrupt, some  of which 
* aren't clear. The main one being data in and message out completed
* UART_TX_DONE is called when the previous message has been sent out over the UART
* when uart_tx() has previously been called
* UART_RX_RDY is called when the UART buffer is full or there was a timeout in 
* reception of data
*/
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
	ARG_UNUSED(dev);

	static size_t aborted_len;
	struct uart_data_t *buf;
	static uint8_t *aborted_buf;	
	uint16_t len;
	uint16_t i;
	uint8_t *data_ptr;

	switch (evt->type) 
	{
	case UART_TX_DONE:	
		LOG_DBG("UART_TX_DONE");
		k_sem_give(&uart_sent_sem);
		break;

	case UART_RX_RDY:
		for (i=0; i<evt->data.rx.len; i++ )
		{
			uart_in_circular_buffer[uart_in_circular_buffer_in_index++] = evt->data.rx.buf[i];
			if (uart_in_circular_buffer_in_index == UART_IN_CIRC_BUF_LEN)
				uart_in_circular_buffer_in_index = 0;
		}
		k_sem_give(&uart_rx_sem);

		break;

	case UART_RX_DISABLED:	
		LOG_DBG("UART_RX_DISABLED");
        data_ptr = &uart_data_in_array[current_uart_array_number].data[0];
        len = sizeof(uart_data_in_array[current_uart_array_number].data);
        uart_rx_enable(uart, data_ptr, len, UART_RX_TIMEOUT);
		break;

	case UART_RX_BUF_REQUEST:
		data_ptr = &uart_data_in_array[current_uart_array_number].data[0];
		len = sizeof(uart_data_in_array[current_uart_array_number].data);
		uart_rx_buf_rsp(uart, data_ptr, len);
		current_uart_array_number ^= 0x01;		
		break;

	case UART_RX_BUF_RELEASED:
	current_uart_array_number ^= 0x01;
		break;

	case UART_TX_ABORTED:
		LOG_DBG("UART_TX_ABORTED");
		if (!aborted_buf) 
        {
			aborted_buf = (uint8_t *)evt->data.tx.buf;
		}
		aborted_len += evt->data.tx.len;
		buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]);

		uart_tx(uart, &buf->data[aborted_len],
			buf->len - aborted_len, SYS_FOREVER_MS);

		break;

	default:
		break;
	}
}

/*
*
* Set up the UART which means using this awful callback system Noric insist on
* The line:
*
* err = uart_callback_set(uart, uart_cb, NULL);
*
* Sets it up
*
* uart_cb is a function that is called whenever any UART interrupt goes off
* be it receive, transmit or errors.
*
* The receive enable is set up with this function: 
*
* uart_rx_enable
* When UART data come in it is put into a buffer specified in uart_rx_enable
* RX_ENABLE is called when, after the first byte is received the buffer is full *or*
* it times out. The timeout is also specified in uart_rx_enable
* When either happens it's necessary to point to a different buffer and restart the uart receive
* so two 240 byte arrays are set up and data is poured into them by the system which is 
* hidden away from the user. 
*/
int uart_init(void)
{
	int err;	
	uart_in_circular_buffer_in_index = 0;
	uart_in_circular_buffer_out_index = 0;
	current_uart_array_number = 0;
	uint8_t *data_ptr;
	uint16_t len;

	if (!device_is_ready(uart)) 
	{
		LOG_ERR("UART device not ready");
		return -ENODEV;
	}
	err = uart_callback_set(uart, uart_cb, NULL);
	if (err) 
	{
		return err;
	}
	data_ptr = &uart_data_in_array[current_uart_array_number].data[0];
	len = sizeof(uart_data_in_array[current_uart_array_number].data);
	err = uart_rx_enable(uart, data_ptr, len, UART_RX_TIMEOUT);

	return NO_ERROR;

}

void send_uart_data(uint8_t *data_ptr, uint8_t len)
{
	uart_tx(uart, data_ptr , len, SYS_FOREVER_MS);
}

#define UART_OUT_CIRC_BUF_LEN 1024
uint16_t uart_out_circ_buf_in_index;
uint16_t uart_out_circ_buf_out_index;
uint8_t uart_out_circular_buffer[UART_OUT_CIRC_BUF_LEN];

void store_received_ble_data(uint8_t *data_ptr, uint16_t len)
{

	uint16_t i;

	if (NULL != data_ptr)
	{
		for (i=0; i<len; i++)
		{
			uart_out_circular_buffer[uart_out_circ_buf_in_index++] = *data_ptr++;
			if (UART_OUT_CIRC_BUF_LEN == uart_out_circ_buf_in_index)			
				uart_out_circ_buf_in_index = 0;			
		}
		k_sem_give(&uart_tx_sem);
	}
	
}



void uart_out_thread(void)
{

	uint16_t index;
	bool data_sent;

	index = 0;
	data_sent = false;

	while (1)
	{
		// If data has previously been sent out, wait until the UART interrupt tells 
		// us it's all gone or it times out
		if (true == data_sent) 
		{
			k_sem_take(&uart_sent_sem, SEND_DELAY_TIMEOUT);
			data_sent = false;
		}
		k_sem_take(&uart_tx_sem, K_FOREVER);		
		while ((uart_out_circ_buf_in_index != uart_out_circ_buf_out_index) && (index < UART_BUF_SIZE))
			{
			uart_out_buff[index++] = uart_out_circular_buffer[uart_out_circ_buf_out_index++];
			if (UART_OUT_CIRC_BUF_LEN == uart_out_circ_buf_out_index)			
				uart_out_circ_buf_out_index = 0;			
			}
		if (index > 0)
		{
			uart_tx(uart, &uart_out_buff[0] , index, SYS_FOREVER_MS);
			data_sent = true;
		}

		index = 0;
		
	}

}