#include <string.h>
#include <stdio.h>

/* Include files needed to use the UART protocol. */
#include "vn/util.h"
#include "vn/protocol/upack.h"
#include "vn/protocol/upackf.h"
#include "vn/int.h"

/* This include file normally contains higher level abstractions for working
 * with VectorNav sensors. However, we include it for some string functions to
 * display named options for the sensor's register's fields. */
#include "vn/sensors.h"

void packetFoundHandler(void *userData, VnUartPacket *packet, size_t runningIndexOfPacketStart);
void UserUart_initialize(void);
bool UserUart_checkForReceivedData(char* buffer, size_t bufferSize, size_t* numOfBytesReceived);
void UserUart_mockReceivedData(char* buffer, size_t bufferSize);
void UserUart_sendData(char *data, size_t size);

bool gIsCheckingForModelNumberResponse = false;
bool gIsCheckingForAsyncOutputFreqResponse = false;
bool gIsCheckingForVpeBasicControlResponse = false;
uint8_t gEnable, gHeadingMode, gFilteringMode, gTuningMode;

int main(void)
{
	char buffer[256];
	size_t numOfBytes, readModelNumberSize, writeAsyncOutputFreqSize, readVpeBasicControlSize, writeVpeBasicControlSize;
	size_t writeBinaryOutput1Size;
	char mockAsciiAsyncData[] = "$VNYMR,+100.949,-007.330,+000.715,-00.0049,-00.2449,+00.5397,-01.258,-00.100,-09.701,-00.000018,+00.001122,-00.000551*69\r\n";
	char mockReadModelNumberResponse[] = "$VNRRG,01,VN-200T-CR*31\r\n";
	char mockWriteAsyncOutputFrequencyResponse[] = "$VNWRG,07,2*6F\r\n";
	char mockFactoryDefaultReadVpeBasicControlResponse[] = "$VNRRG,35,1,1,1,1*75\r\n";
	char mockWriteVpeBasicControlResponse[] = "$VNWRG,35,1,0,1,1*71\r\n";
	char mockErrorMessage[] = "$VNERR,12*72\r\n";
	uint8_t mockBinaryAsyncData[] = { 0xFA, 0x01, 0x09, 0x00, 0x70, 0x05, 0x00, 0x03, 0x0A, 0x00, 0x00, 0x00, 0x48, 0x0E, 0x2C, 0x42, 0x08, 0x4C, 0x37, 0xC1, 0x10, 0x38, 0x8B, 0xC2, 0xD4, 0xCB };
	char genReadModelNumberBuffer[256];
	char genWriteAsyncOutputFrequencyBuffer[256];
	char genReadVpeBasicControlBuffer[256];
	char genWriteVpeBasicControlBuffer[256];
	char genWriteBinaryOutput1Buffer[256];

    /* This example provides an overview of the UART protocol functionality
     * of the VectorNav C Library.
     *
     * Using the UART Protocol allows communicating with a VectorNav sensor
     * over a UART interface using both ASCII and binary protocols. Usage of
     * this "core" feature requires you to do all of the grunt work of
     * initializing and managing the UART port for your development
     * environment. Once this is setup, you will initialize the UART protocol
     * and then simply pass arrays of data between your UART code and the
     * VectorNav C Library's protocol code. To keep this example generic, we
     * will mock the necessary UART initialization and management functions
     * that would need to be replaced by code specific for your environment
     * to tie into a real VectorNav sensor. For now we just use some fake data
     * to illustrate the process. */

    /* The VnUartProtocol structure encapsulates the data used for buffering
     * and handling incoming data. It is not associated with sending commands
     * to the sensor. This will be illustrated further in the example. */
	VnUartPacketFinder up;

    /* First thing you should do is initialize the data structure. */
	VnUartPacketFinder_initialize(&up);

	/* Register our callback method for when the VnUartPacketFinder finds an
	   ASCII asynchronous packet. */
	VnUartPacketFinder_registerPacketFoundHandler(&up, packetFoundHandler, NULL);

	/* Initialize the UART port (this is mimicked in this example). */
	UserUart_initialize();

	/* With our VnUartProtocol and mock UART initialized, we will fake an
	 * asynchronous message output by a VectorNav sensor and received by our
	 * mock UART port. */
	UserUart_mockReceivedData(mockAsciiAsyncData, strlen(mockAsciiAsyncData));

	/* Normally you will be continually checking for new UART data and then
	 * passing any received data to the VnUartProtocol to build, parse and
	 * verify data packets. Since this is just for demonstration purposes, we
	 * just poll the mock UART for its current data. In a real world
	 * environment where you are servicing a real UART port, you would have
	 * code similar to the code lines below.
	 *
	 * char buffer[256];
	 * while (1)
	 * {
	 *     size_t numOfBytes;
	 *     if (UserUart_checkForReceivedData(buffer, 256, &numOfBytes))
	 *         VnUartPacketFinder_processReceivedData(buffer, numOfBytes);
	 * }
	 */
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);

	/* Now when we pass the data to the VnUartPacketFinder, our function
	 * validPacketFoundHandler will be called since we passed in a complete
	 * and valid data packet. Scroll down to the function validPacketFoundHandler
	 * to see how to process and extract the values from the packet. */
	VnUartPacketFinder_processData(&up,(uint8_t*) buffer, numOfBytes);

	/* Processing received asynchronous data from the sensor is fairly straight
	 * forward. However, you may wish to query or configure the sensor and in
	 * theory this is still straight forward if you do not add code to handle
	 * retransmits of commands or communication timeouts. We will show the
	 * basic structure of querying and configuring and leave it to the
	 * developer for adding edge case handling code. The file sensors.cpp in
	 * the C++ may serve as a reference for adding this extra code.
	 */

	/* We will first illustrate querying the sensor's model number. First we
	 * generate a read register command. */
	VnUartPacket_genReadModelNumber(
		genReadModelNumberBuffer,
		sizeof(genReadModelNumberBuffer),
		VNERRORDETECTIONMODE_CHECKSUM,
		&readModelNumberSize);

	/* Now send the data to the sensor. */
	gIsCheckingForModelNumberResponse = true;
	UserUart_sendData(genReadModelNumberBuffer, readModelNumberSize);

	/* Mock that the sensor responded to our request. */
	UserUart_mockReceivedData(mockReadModelNumberResponse, strlen(mockReadModelNumberResponse));

	/* Now process the mock data that our fake UART port received and hand it
	 * over to our UART packet finder. */
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);
	VnUartPacketFinder_processData(&up, (uint8_t*)buffer, numOfBytes);
	gIsCheckingForModelNumberResponse = false;

	/* Let's see how to perform a trivial configuration of the sensor. We will
	 * change the asynchronous data output frequency to 2 Hz. */
	VnUartPacket_genWriteAsyncDataOutputFrequency(
		genWriteAsyncOutputFrequencyBuffer,
		sizeof(genWriteAsyncOutputFrequencyBuffer),
		VNERRORDETECTIONMODE_CHECKSUM,
		&writeAsyncOutputFreqSize,
		2);

	/* Now send the data to the sensor. */
	gIsCheckingForAsyncOutputFreqResponse = true;
	UserUart_sendData(genWriteAsyncOutputFrequencyBuffer, writeAsyncOutputFreqSize);

	/* Mock that the sensor responded to our request. */
	UserUart_mockReceivedData(mockWriteAsyncOutputFrequencyResponse, strlen(mockWriteAsyncOutputFrequencyResponse));

	/* Now process the mock data that our fake UART port received and hand it
	 * over to our UART packet finder. */
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);
	VnUartPacketFinder_processData(&up, (uint8_t*)buffer, numOfBytes);
	gIsCheckingForAsyncOutputFreqResponse = false;

	/* Other configuration register on the VectorNav sensors have multiple
	 * fields that need to be set when we write to them. If we only are
	 * interested in one field, a safe and easy way to perform this is to first
	 * read the current configuration, change the fields we are concerned with,
	 * and then write the settings back to the sensor. We will illustrate this
	 * now by changing the sensors heading mode of the VPE Basic Control
	 * register. */

	printf("Reading current values of the VPE Basic Control register.\n");

	/* First generate a read register command. */
	VnUartPacket_genReadVpeBasicControl(
		genReadVpeBasicControlBuffer,
		sizeof(genReadVpeBasicControlBuffer),
		VNERRORDETECTIONMODE_CHECKSUM,
		&readVpeBasicControlSize);

	/* Now send the data to the sensor. */
	gIsCheckingForVpeBasicControlResponse = true;
	UserUart_sendData(genReadVpeBasicControlBuffer, readVpeBasicControlSize);

	/* Mock that the sensor responded to our request. */
	UserUart_mockReceivedData(mockFactoryDefaultReadVpeBasicControlResponse, strlen(mockFactoryDefaultReadVpeBasicControlResponse));

	/* Now process the mock data that our fake UART port received and hand it
	 * over to our UART packet finder. */
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);
	VnUartPacketFinder_processData(&up, (uint8_t*)buffer, numOfBytes);
	gIsCheckingForVpeBasicControlResponse = false;

	/* The validPacketFoundHandler will have set the current values of the VPE
	 * Basic Control register into our global variables. Let's now change the
	 * heading mode field for this register while keeping the other fields in
	 * their current state. */

	printf("Writing new values to the VPE Basic Control register.\n");

	/* Generate the write register command. */
	VnUartPacket_genWriteVpeBasicControl(
		genWriteVpeBasicControlBuffer,
		sizeof(genWriteVpeBasicControlBuffer),
		VNERRORDETECTIONMODE_CHECKSUM,
		&writeVpeBasicControlSize,
		gEnable,
		0, /* Could possibly use a value from the enum HeadingMode in sensors.h. */
		gFilteringMode,
		gTuningMode);

	/* Send the data to the sensor. */
	gIsCheckingForVpeBasicControlResponse = true;
	UserUart_sendData(genWriteVpeBasicControlBuffer, writeVpeBasicControlSize);

	/* Mock that the sensor responded to our request. */
	UserUart_mockReceivedData(mockWriteVpeBasicControlResponse, strlen(mockWriteVpeBasicControlResponse));

	/* Process the mock data that our fake UART port received and hand it
	 * over to our UART packet finder. */
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);
	VnUartPacketFinder_processData(&up, (uint8_t*)buffer, numOfBytes);
	gIsCheckingForVpeBasicControlResponse = false;

	/* The VectorNav sensor also supports binary asynchronous data output,
	 * which can be configured by the user to support flexible configuration of
	 * data output types. In this example, we will show how to configure the
	 * sensor's binary output configuration register, and then process a packet
	 * received of this binary output data. */

	/* Generate our command to configure the Binary Output 1 register. Normally
	 * when working with the functions from the file uart.h, the data types as
	 * listed in the user manual are used, without any abstractions getting in
	 * the way. However, here we use some enums defined in sensors.h for
	 * specifying the flags of the register's fields since it is much easier to
	 * understand. Here we configure the sensor to output yaw, pitch, roll and
	 * timestart data at 4 Hz. Note that the sensor's user manual requires
	 * specifying which groups are present; however, this function call will
	 * take care of determining which fields are present. */
	VnUartPacket_genWriteBinaryOutput1(
		(uint8_t*)genWriteBinaryOutput1Buffer,
		sizeof(genWriteBinaryOutput1Buffer),
		VNERRORDETECTIONMODE_CHECKSUM,
		&writeBinaryOutput1Size,
		ASYNCMODE_PORT1,
		200,
		COMMONGROUP_TIMESTARTUP | COMMONGROUP_YAWPITCHROLL, /* Note use of binary OR to configure flags. */
		TIMEGROUP_NONE,
		IMUGROUP_NONE,
		GPSGROUP_NONE,
		ATTITUDEGROUP_NONE,
		INSGROUP_NONE,
    GPSGROUP_NONE);

	/* Send the data to the sensor. */
	UserUart_sendData(genWriteBinaryOutput1Buffer, writeBinaryOutput1Size);

	/* Mock that the sensor responded to our request. */
	UserUart_mockReceivedData((char*)mockBinaryAsyncData, 26);

	/* Process the mock data that our fake UART port received and hand it
	 * over to our UART packet finder. */
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);
	VnUartPacketFinder_processData(&up, (uint8_t*)buffer, numOfBytes);

	/* Lastly, you may want to include code that checks for error messages
	 * output from the sensor. To demonstrate, we pass a fake error message to
	 * be handled by our code. */
	UserUart_mockReceivedData(mockErrorMessage, strlen(mockErrorMessage));
	UserUart_checkForReceivedData(buffer, sizeof(buffer), &numOfBytes);
	VnUartPacketFinder_processData(&up, (uint8_t*)buffer, numOfBytes);

	return 0;
}

void packetFoundHandler(void *userData, VnUartPacket *packet, size_t runningIndexOfPacketStart)
{
	/* Silence 'unreferenced formal parameters' warning in Visual Studio. */
	(runningIndexOfPacketStart);
	(userData);

	/* When this function is called, the packet will already have been
	 * validated so no checksum/CRC check is required. */

	/* First see if this is an ASCII or binary packet. */
	if (VnUartPacket_type(packet) == PACKETTYPE_ASCII)
	{
		/* Now that we know this is an ASCII packet, we can call the various
		 * ASCII functions to further process this packet. */
		if (VnUartPacket_isAsciiAsync(packet))
		{
			/* We know we have an ASCII asynchronous data packet. Let's see if
			 * this is a message type we are looking for. */
			VnAsciiAsync asyncType = VnUartPacket_determineAsciiAsyncType(packet);

			if (asyncType == VNYMR)
			{
				/* Parse the VNYMR message. */
				vec3f ypr, mag, accel, angularRate;
				char yprStr[100], magStr[100], accelStr[100], angularRateStr[100];

				VnUartPacket_parseVNYMR(packet, &ypr, &mag, &accel, &angularRate);

				str_vec3f(yprStr, ypr);
				str_vec3f(magStr, mag);
				str_vec3f(accelStr, accel);
				str_vec3f(angularRateStr, angularRate);
				printf("[Found VNYMR Packet]\n");
				printf("  YawPitchRoll: %s\n", yprStr);
				printf("  Magnetic: %s\n", magStr);
				printf("  Acceleration: %s\n", accelStr);
				printf("  Angular Rate: %s\n", angularRateStr);
			}
 		}
		else if (VnUartPacket_isResponse(packet))
		{
			if (gIsCheckingForModelNumberResponse)
			{
				char modelNumber[100];

				VnUartPacket_parseModelNumber(packet, modelNumber);

				printf("Model Number: %s\n", modelNumber);
			}
			else if (gIsCheckingForAsyncOutputFreqResponse)
			{
				uint32_t asyncOutputFreq;

				VnUartPacket_parseAsyncDataOutputFrequency(packet, &asyncOutputFreq);

				printf("Asynchronous Output Frequency: %u Hz\n", asyncOutputFreq);
			}
			else if (gIsCheckingForVpeBasicControlResponse)
			{
				char enableStr[100], headingModeStr[100], filteringModeStr[100], tuningModeStr[100];

				VnUartPacket_parseVpeBasicControl(packet, &gEnable, &gHeadingMode, &gFilteringMode, &gTuningMode);

				strFromBool(enableStr, (bool) gEnable);
				strFromHeadingMode(headingModeStr, (VnHeadingMode)gHeadingMode);
				strFromVpeMode(filteringModeStr, (VnVpeMode)gFilteringMode);
				strFromVpeMode(tuningModeStr, (VnVpeMode)gTuningMode);
				printf("[VPE Basic Control]\n");
				printf("  Enable: %s\n", enableStr);
				printf("  Heading Mode: %s\n", headingModeStr);
				printf("  Filtering Mode: %s\n", filteringModeStr);
				printf("  Tuning Mode: %s\n", tuningModeStr);
			}
		}
		else if (VnUartPacket_isError(packet))
		{
			uint8_t error;
			char errorStr[100];

			VnUartPacket_parseError(packet, &error);

			strFromSensorError(errorStr, (SensorError) error);

			printf("Sensor Error: %s\n", errorStr);
		}
 	}
	else if (VnUartPacket_type(packet) == PACKETTYPE_BINARY) {
		uint64_t timeStartup;
		vec3f ypr;
		char yprStr[100];

		/* See if this is a binary packet type we are expecting. */
		if (!VnUartPacket_isCompatible(
			packet,
			(CommonGroup)(COMMONGROUP_TIMESTARTUP | COMMONGROUP_YAWPITCHROLL),
			(TimeGroup)(TIMEGROUP_NONE),
			(ImuGroup)(IMUGROUP_NONE),
			(GpsGroup)(GPSGROUP_NONE),
			(AttitudeGroup)(ATTITUDEGROUP_NONE),
			(InsGroup)(INSGROUP_NONE),
			(GpsGroup)(GPSGROUP_NONE)))
		{
			/* Not the type of binary packet we are expecting. */
			return;
		}

		/* Ok, we have our expected binary output packet. Since there are many
		 * ways to configure the binary data output, the burden is on the user
		 * to correctly parse the binary packet. However, we can make use of
		 * the parsing convenience methods provided by the VnUartPacket structure.
		 * When using these convenience methods, you have to extract them in
		 * the order they are organized in the binary packet per the User Manual. */
		timeStartup = VnUartPacket_extractUint64(packet);
		ypr = VnUartPacket_extractVec3f(packet);
		str_vec3f(yprStr, ypr);
		printf("[Binary Packet Received]\n");
		printf("  TimeStartup: %u\n", (uint32_t) timeStartup);
		printf("  Yaw Pitch Roll: %s\n", yprStr);
	}
}

/* Some variables to enable our mock UART port. */
char* mockUartReceivedDataBuffer[256];
size_t mockUartReceivedDataSize;

/* This is a mock function which in a real development environment would
 * contain code to initialize the device's UART port. However, to keep this
 * example generic, we simply initialize our program to mimic a UART port
 * provided by the user. */
void UserUart_initialize(void)
{
	mockUartReceivedDataSize = 0;
}

/* This is another mock function which would be replaced in a real program to
 * actually query the environment's UART for any data received. We just mock
 * the data in this example. */
bool UserUart_checkForReceivedData(char* buffer, size_t bufferSize, size_t* numOfBytesReceived)
{
	/* Silence 'unreferenced formal parameters' warning in Visual Studio. */
	(bufferSize);

	if (mockUartReceivedDataSize == 0)
		return false;

	memcpy(buffer, mockUartReceivedDataBuffer, mockUartReceivedDataSize);
	*numOfBytesReceived = mockUartReceivedDataSize;

	mockUartReceivedDataSize = 0;

	return true;
}

/* This is a helper method for our mocked UART port that will mimic data that
 * has been received on a UART port. This function would not be implemented in
 * an environment that is using an actual UART port. */
void UserUart_mockReceivedData(char* buffer, size_t bufferSize)
{
	memcpy(mockUartReceivedDataBuffer, buffer, bufferSize);
	mockUartReceivedDataSize = bufferSize;
}

/* This is a method for simulating sending data to a VectorNav sensor. This
 * will need to be implemented by the developer to actually send the data over
 * the system's UART. */
void UserUart_sendData(char *data, size_t size)
{
	/* Silence 'unreferenced formal parameters' warning in Visual Studio. */
	(data);
	(size);

	/* Do nothing since we are mocking a UART port. */
}
