/*
 * commprt.c
 *
 *  Created on: Jan 25, 2022
 *      Author: radioman
 */

#include <string.h>
#include "stm32l1xx_hal.h"
#include "commprt.h"
#include "uartio.h"
#include "usbd_cdc_if.h"

#define COMM_HEADER_BYTE	0x55
#define COMM_BUFFER_SIZE	260

extern UART_HandleTypeDef huart1;

static uint8_t comm_txbuffer[COMM_BUFFER_SIZE];
static uint8_t comm_rxbuffer[COMM_BUFFER_SIZE];

uint8_t COMM_Checksum(const uint8_t *data, uint8_t len)
{
	uint8_t chk = 0;
	while(len--)
		chk ^= data[len];
	return chk;
}

COMM_StatusTypeDef COMM_Receive(uint8_t *buf, COMM_IfaceTypeDef iface, uint32_t timeout)
{
	uint8_t header = 0, psize = 0;
	uint32_t stime = HAL_GetTick();
	int (*read_data)(uint8_t*, uint32_t);
	uint32_t (*data_avail)(void);

	if(iface == COMM_BT)
	{
		read_data = UARTIO_Receive;
		data_avail = UARTIO_DataAvailable;
	}

	if(iface == COMM_USB)
	{
		read_data = CDC_ReadData;
		data_avail = CDC_DataAvailable;
	}

	while((header != COMM_HEADER_BYTE))
	{
		if(read_data(&header, 1) <= 0)
			return COMM_FAIL;
		if((HAL_GetTick() - stime) > timeout)
			return COMM_TIMEOUT;
	}

	while(read_data(&psize, 1) != 1)
	{
		if((HAL_GetTick() - stime) > timeout)
			return COMM_TIMEOUT;
	}

	if(psize == 0)
		return COMM_FAIL;

	while(data_avail() < (psize + 1))
	{
		if((HAL_GetTick() - stime) > timeout)
			return COMM_TIMEOUT;
	}

	read_data(comm_rxbuffer, psize + 1);

	uint8_t checksum = (COMM_HEADER_BYTE ^ psize) ^ COMM_Checksum(comm_rxbuffer, psize);
	if(checksum != comm_rxbuffer[psize])
		return COMM_CHECKSUM_MISTMATCH;

	memcpy(buf, comm_rxbuffer, psize);

	return COMM_OK;
}

COMM_StatusTypeDef COMM_Transmit(uint8_t *data, uint8_t len, COMM_IfaceTypeDef iface)
{
	comm_txbuffer[0] = COMM_HEADER_BYTE;
	comm_txbuffer[1] = len;
	memcpy(&comm_txbuffer[2], data, len);
	comm_txbuffer[len + 2] = COMM_Checksum(comm_txbuffer, len + 2);
	int (*write_data)(uint8_t*, uint32_t);

	if(iface == COMM_BT)
		write_data = UARTIO_Transmit;

	if(iface == COMM_USB)
		write_data = CDC_WriteData;

	if(write_data(data, len) > 0)
		return COMM_OK;
	else
		return COMM_FAIL;
}
