/**
 * ui_interface.c
 *
 * Packet-based communication interface.
 * 
 * Copyright (c) 2022 Bigscreen, Inc.
 */

#include <asf.h>
#include <string.h>
#include <stdbool.h>
#include "ui_interface.h"
#include "vxr_interface.h"

Data_Packet_T response_packet;

uint32_t ui_user_signature_buffer[512/sizeof(uint32_t)];
uint8_t ui_user_signature_block_num;
bool ui_signature_needs_updating = true;

/*
 * Checks a buffer for a valid packet. Only processes the first packet found.
 * Packet data is populated into the Data_Packet_T pointer passed to this function.
 * Arguments:
 *		buf: the data buffer which may contain a packet
 *		buf_length: the length of valid data the buffer
 *		received_packet: a pointer to a Data_Packet_T struct which will
 *						 be filled with data from the received packet.
 * Return:
 *		uint8_t*:
 *				If valid start byte found: pointer to the location of the first packet start byte,
 *									   e.g. the beginning of the packet
 *				NOTE: If there is no valid packet found, the return value will still contain
 *					  the location of the first start byte, if it exists.
 *
 * If no packet is found, the received_packet "Type" will be "UI_NO_PACKET". This should
 * be used as the final check to determine whether or not a valid packet is in the buffer.
 */

uint8_t* UI_check_packet(uint8_t* buf, uint32_t buf_length, Data_Packet_T* received_packet) {
	
	uint8_t* start_byte;
	uint8_t packet_type;
	uint16_t packet_length;
	
	// 1. Check for the start bytes
	start_byte = memchr(buf, USART_START_BYTE0, buf_length);
	uint32_t start_place = ((uint32_t)start_byte) - ((uint32_t)buf);
	// Make sure the start byte was found and that enough bytes are in the buffer to complete
	// at least a zero data packet
	if((start_byte == NULL) || ((buf_length - start_place) < USART_DATA_OFFSET)) {
		received_packet->ErrorCode = UI_ERR_NO_PACKET;
	} else {
		// 1b. check for second start byte
		if(USART_START_BYTE1 != start_byte[USART_START_BYTE1_OFFSET]) {
			received_packet->ErrorCode = UI_ERR_BAD_START_SEQ;
		} else {
			// 2. get the packet type. two bytes, second is bit inverted version of first
			if((start_byte[USART_PACKET_TYPE_OFFSET] ^ start_byte[USART_INV_PACKET_TYPE_OFFSET]) != 0xFFu) {
				received_packet->ErrorCode = UI_ERR_BAD_TYPE;
			} else {
				packet_type = start_byte[USART_PACKET_TYPE_OFFSET];
				// 3. get the length
				packet_length = start_byte[USART_DATA_LENGTH_LSB_OFFSET];
				packet_length += ((uint16_t)start_byte[USART_DATA_LENGTH_MSB_OFFSET]) << 8;
				if(packet_length > MAX_PACKET_DATA_LENGTH) {
					received_packet->ErrorCode = UI_ERR_INVALID_LENGTH;
				} else {
					// check that enough bytes have been received for all the declared data
					uint32_t end_place = start_place + USART_DATA_OFFSET + packet_length;
					// end_place is actually 1 after the last data byte. It's the place just after the packet ends.
					if(buf_length >= end_place) {
						// 4. get the data
						memcpy(received_packet->Data, &(start_byte[USART_DATA_OFFSET]), packet_length);
						// populate the other members of the packet
						received_packet->Length = packet_length;
						received_packet->Type = packet_type;
						received_packet->ErrorCode = UI_ERR_SUCCESS;
					} else {
						// Not enough data for this packet, but more could be coming later.
						// Caller shouldn't delete this partial packet from the buffer yet
						received_packet->ErrorCode = UI_ERR_NO_PACKET;
					}
				} 
			} 
		}
	}
	
	return start_byte;
}

uint8_t UI_build_packet(uint8_t* buf, uint32_t buf_length, Data_Packet_T* pkt) {
	if((buf_length < (USART_DATA_OFFSET + (pkt->Length))) || (MAX_PACKET_DATA_LENGTH < (pkt->Length)))  {
		// not enough buffer space to load the packet, or length too long, fail.
		return pdFAIL;
	}
	
	buf[USART_START_BYTE0_OFFSET] = USART_START_BYTE0;
	buf[USART_START_BYTE1_OFFSET] = USART_START_BYTE1;
	buf[USART_PACKET_TYPE_OFFSET] = pkt->Type;
	buf[USART_INV_PACKET_TYPE_OFFSET] = ~(pkt->Type);
	buf[USART_DATA_LENGTH_LSB_OFFSET] = (uint8_t)((pkt->Length) & 0x00FFu);
	buf[USART_DATA_LENGTH_MSB_OFFSET] = (uint8_t)(((pkt->Length) & 0xFF00u) >> 8);
	memcpy(&(buf[USART_DATA_OFFSET]), pkt->Data, pkt->Length);
	
	return pdPASS;
}

void UI_process_command(Data_Packet_T* req_pkt) {
	switch(req_pkt->Type) {
		case HID_CODE_FOR_SW_VER:
			// Reply with the software version string
			response_packet.Type = HID_CODE_FOR_SW_VER_REPLY;
			response_packet.Length = get_sw_ver_len();
			memcpy(response_packet.Data, get_sw_ver(), get_sw_ver_len());
		break;
		
		case HID_CODE_FOR_SERIAL_NUM:
			// Reply with the serial number
			response_packet.Length = sigv2_tag_length(SigTag_Serial);
			if(0 == response_packet.Length) {
				response_packet.Type = UI_NACK; // no serial number entry found in user signature
			} else {
				if(sigv2_find_tag(SigTag_Serial, response_packet.Data)) {
					response_packet.Type = HID_CODE_FOR_SERIAL_NUM_REPLY;
				} else {
					// Really shouldn't fail to here. But if it does, respond with nack
					response_packet.Type = UI_NACK;
				}
			}
		break;
		
		case HID_CODE_FOR_RGB_LED:
			// Next three bytes are R, G, B respectively
			if(req_pkt->Length == 3) {
				RGB_Color_t newcolor;
				newcolor.r = req_pkt->Data[0];
				newcolor.g = req_pkt->Data[1];
				newcolor.b = req_pkt->Data[2];
				rgb_led_set_color(MCU_I2C(), &newcolor);
				response_packet.Length = 0;
				response_packet.Type = UI_ACK;
			} else {
				// respond with NACK since there's something wrong with the command
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_READ_SIG:
			// Next byte is block. Signature is read in 32 bytes blocks (total of 512 bytes will 
			// be read out in 16 blocks)
			if(req_pkt->Length == 1) {
				if((req_pkt->Data[0]) <= 15) {
					const uint8_t* sig = (const uint8_t*) get_signature_raw();
					response_packet.Type = HID_CODE_FOR_SIG_REPLY;
					response_packet.Length = HID_SIG_XFER_LENGTH;
					memcpy(response_packet.Data, &(sig[(req_pkt->Data[0])*HID_SIG_XFER_LENGTH]),HID_SIG_XFER_LENGTH);
				} else {
					response_packet.Length = 0;
					response_packet.Type = UI_NACK;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_WRITE_SIG:
			// Should be taking in 33 bytes - first one is the block (0 to 15), next 32 are the data to be
			// written to that block
			if(req_pkt->Length == (HID_SIG_XFER_LENGTH + 1u)){
				if((req_pkt->Data[0]) <= 15) {
					if(pdPASS == update_signature_raw(&(req_pkt->Data[1]), (req_pkt->Data[0])*HID_SIG_XFER_LENGTH, HID_SIG_XFER_LENGTH)) {
					
						response_packet.Length = 0;
						response_packet.Type = UI_ACK;
					} else {
						response_packet.Length = 0;
						response_packet.Type = UI_NACK;
					}
				} else {
					response_packet.Length = 0;
					response_packet.Type = UI_NACK;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_SAVE_SIG:
			if(req_pkt->Length == 0) {
				if(pdPASS == save_signature_raw()) {
					response_packet.Length = 0;
					response_packet.Type = UI_ACK;
				} else {
					response_packet.Length = 0;
					response_packet.Type = UI_NACK;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_CHECK_VXR_CONNECTION:
			if(req_pkt->Length == 0) {
				if(RC_Success == VXR_Check_Connection()) {
					response_packet.Length = 0;
					response_packet.Type = UI_ACK;
				} else {
					response_packet.Length = 0;
					response_packet.Type = UI_NACK;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_VXR_DELETE:
		// Deletes a block of Flash memory on the VXR7200.
		// There are 8 blocks - so only values 0->7 are allowed
			if(req_pkt->Length == 1) {
				if(RC_Success == VXR_Erase_Firmware_Bank(req_pkt->Data[0])) {
					response_packet.Length = 0;
					response_packet.Type = UI_ACK;
				} else {
					response_packet.Length = 0;
					response_packet.Type = UI_NACK;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_VXR_CHECKSUM:
		// Calculates the checksum of installed firmware on the VXR7200 chip.
		// requires a length and starting address
		// Both are 4 byte values, most significant byte last
		// for example, 0x01020304 is sent as 0x04, 0x03, 0x02, 0x01
		// Data[0:3] = address to start calculating checksum
		// Data[4:7] = length (in bytes) to sum up for the checksum
			if(req_pkt->Length == 8) {
				uint32_t chksum_addr = ((uint32_t)req_pkt->Data[0]);
				chksum_addr += (((uint32_t)req_pkt->Data[1]) << 8);
				chksum_addr += (((uint32_t)req_pkt->Data[2]) << 16);
				chksum_addr += (((uint32_t)req_pkt->Data[3]) << 24);
				uint32_t chksum_len = ((uint32_t)req_pkt->Data[4]);
				chksum_len += (((uint32_t)req_pkt->Data[5]) << 8);
				chksum_len += (((uint32_t)req_pkt->Data[6]) << 16);
				chksum_len += (((uint32_t)req_pkt->Data[7]) << 24);
				uint32_t chksum_result;
				if(RC_Success == VXR_Calc_Checksum(chksum_addr, chksum_len, &chksum_result)) {
					memcpy(response_packet.Data, &(chksum_result), VXR_CHECKSUM_LENGTH);
					response_packet.Type = HID_CODE_FOR_VXR_CHECKSUM_REPLY;
					response_packet.Length = VXR_CHECKSUM_LENGTH;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_VXR_PROGRAM:
			// programs 1-32 bytes in the VXR7200 Flash memory
			// Data[0] = length in bytes (1 to 32)
			// Data[1:4] = starting address where to program
			// Data[5:5+length-1] = bytes to program
			if( (req_pkt->Length >= 6 ) && (req_pkt->Length <= 37) ) {
				// Allows 1 to 32 bytes to be programmed per packet
				// Length of 6: 1 byte for length, 4 byte address, 1 data byte
				// Length of 37: 1 byte for length, 4 byte address, 32 data bytes
				uint32_t prog_len = req_pkt->Data[0];
				uint32_t prog_addr = ((uint32_t)req_pkt->Data[1]);
				prog_addr += (((uint32_t)req_pkt->Data[2]) << 8);
				prog_addr += (((uint32_t)req_pkt->Data[3]) << 16);
				prog_addr += (((uint32_t)req_pkt->Data[4]) << 24);
				if(RC_Success == VXR_Program_Firmware(prog_addr, prog_len, &(req_pkt->Data[5]))) {
					response_packet.Length = 0;
					response_packet.Type = UI_ACK;
				} else {
					response_packet.Length = 0;
					response_packet.Type = UI_NACK;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_VXR_TAGS:
			if(req_pkt->Length == 0) {
				response_packet.Data[0] = 0;
				if(VXR_Tag_Valid(Vxr_Tag_Config_0)) response_packet.Data[0] |= 0x01;
				if(VXR_Tag_Valid(Vxr_Tag_Config_1)) response_packet.Data[0] |= 0x02;
				if(VXR_Tag_Valid(Vxr_Tag_Firmware_0)) response_packet.Data[0] |= 0x04;
				if(VXR_Tag_Valid(Vxr_Tag_Firmware_1)) response_packet.Data[0] |= 0x08;
				
				response_packet.Length = 1;
				response_packet.Type = HID_CODE_FOR_VXR_TAGS_REPLY;
				
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
		
		case HID_CODE_FOR_HW_TEST:
			if(req_pkt->Length == 1) {
				// One byte for the test ID
				switch( (HW_Test_T)(req_pkt->Data[0]) ) {
					case HWTest_USB_Hub:
						response_packet.Data[0] = test_usb_hub();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					case HWTest_RGB_LED:
						response_packet.Data[0] = test_rgb_led();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					case HWTest_USBC_Switch:
						response_packet.Data[0] = test_usbc_switch();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					case HWTest_VXR:
						response_packet.Data[0] = test_vxr();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					case HWTest_Prox:
						response_packet.Data[0] = test_prox();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					case HWTest_OLED:
						response_packet.Data[0] = test_oled_panels();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					case HWTest_Fan:
						response_packet.Data[0] = test_fan();
						response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY;
						response_packet.Length = 1;
					break;
					default:
						response_packet.Type = UI_NACK;
						response_packet.Length = 0;
					break;
				}
			} else {
				response_packet.Length = 0;
				response_packet.Type = UI_NACK;
			}
		break;
			
		default:
			response_packet.Length = 0;
			response_packet.Type = UI_NACK;
		break;
	}
}

Data_Packet_T* UI_get_response(void) {
	return &response_packet;
}
