#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef linux
	#include <arpa/inet.h>
#endif
#ifdef _WIN32
	#include <winsock2.h>
#endif
#include "includes/serialization.h"


/* Takes in float values, casts them to uint16_t and creates a Serial_Data struct */
Serial_Data Serial_create_data(float pad_x, float pad_y, float ball_x, float ball_y) {
	Serial_Data data;
	data.pad_x = (uint16_t)pad_x;
	data.pad_y = (uint16_t)pad_y;
	data.ball_x = (uint16_t)ball_x;
	data.ball_y = (uint16_t)ball_y;
	
	return data;
}

/* Serializes a 'Data' struct into a byte array, converted to network-byte order */
uint8_t* Serial_serialize(Serial_Data data) {
	/* Create a pointer that can fit the entire struct */
	uint8_t* serialized = malloc(4 * sizeof(uint16_t) + 1);
	uint8_t* pad_x_ptr;
	uint8_t* pad_y_ptr;
	uint8_t* ball_x_ptr;
	uint8_t* ball_y_ptr;
	memset(serialized, 0, 4 * sizeof(uint16_t) + 1); // Zero out the memory
	pad_x_ptr = serialized;
	pad_y_ptr = pad_x_ptr + sizeof(uint16_t);
	ball_x_ptr = pad_y_ptr + sizeof(uint16_t);
	ball_y_ptr = ball_x_ptr + sizeof(uint16_t);

	*((uint16_t *)pad_x_ptr) = data.pad_x;
	*((uint16_t *)pad_x_ptr) = htons(*((uint16_t *)pad_x_ptr));
	
	*((uint16_t *)pad_y_ptr) = data.pad_y;
	*((uint16_t *)pad_y_ptr) = htons(*((uint16_t *)pad_y_ptr));
	
	*((uint16_t *)ball_x_ptr) = data.ball_x;
	*((uint16_t *)ball_x_ptr) = htons(*((uint16_t *)ball_x_ptr));
	
	*((uint16_t *)ball_y_ptr) = data.ball_y;
	*((uint16_t *)ball_y_ptr) = htons(*((uint16_t *)ball_y_ptr));
	*(ball_y_ptr + sizeof(uint16_t)) = '\0';

	return serialized;
}

/* Deserialize a byte array into a 'Data' struct, converted to host byte order */
Serial_Data Serial_deserialize(uint8_t* serialized) {
	Serial_Data deserialized;
	/* Use successive chunks of memory address to create pointers to the data */
	uint8_t* pad_x_ptr = serialized;
	uint8_t* pad_y_ptr = serialized + sizeof(uint16_t);
	uint8_t* ball_x_ptr = pad_y_ptr + sizeof(uint16_t);
	uint8_t* ball_y_ptr = ball_x_ptr + sizeof(uint16_t);

	/* Dereference (and cast) the pointers, and store them into the struct */
	deserialized.pad_x = *((uint16_t *)pad_x_ptr);
	deserialized.pad_x = ntohs(deserialized.pad_x);
	
	deserialized.pad_y = *((uint16_t *)pad_y_ptr);
	deserialized.pad_y = ntohs(deserialized.pad_y);
	
	deserialized.ball_x = *((uint16_t *)ball_x_ptr);
	deserialized.ball_x = ntohs(deserialized.ball_x);
		
	deserialized.ball_y = *((uint16_t *)ball_y_ptr);
	deserialized.ball_y = ntohs(deserialized.ball_y);
		
	return deserialized;
}

