/*
 * xlru_comm_stubs.cpp
 *
 */


#include "xlru_comm.h"

#include "xuart_driver.h"

#include "xlru_serial_over_udp.h"

#include "rtlog.h"

#include <string.h>

const char* xlru_comm_error_string(xlru_comm_sts_t error_code)
{
	static const char* err_msg[]=
	{
		"OK",
		"ERR",	/*Generic error*/
		"Device fail",
		"parity",
		"overrun",
		"buffer overrun"
	};
	int ecode=static_cast<int>(error_code);
	if ((ecode<0) || (ecode>=(int)((sizeof err_msg)/(sizeof err_msg[0]))))
			return "????";
	return err_msg[ecode];
}

#define USE_ETH

#ifdef USE_ETH

#include "grifo_inet_ports.h"
#include "mrk_net.h"

#define XLRU_PORT			grifo_inet::xlru_simulator_master
#define XLRU_PORT_SLAVES	grifo_inet::xlru_simulator_slaves


static mrk_net_udp_socket_t xlru_socket;
static mrk_net_ip_raw_address_t sender_address;
static unsigned short sender_port;
static unsigned int override_mask;

static struct xlru::xlru_udp_master_t master_pkt;

static bool udp_monitor_enabled;

void xlru_comm_udp_monitor_enable(bool ena)
{
	udp_monitor_enabled=ena;
	if (sender_address==0)
	{
		sender_address=mrk_net_myip_address_broadcast(0);
	}
}

enum xlru_comm_sts_t xlru_comm_initialize(void)
{
	master_pkt.marker=xlru::xlru_udp_master_t::k_marker;
	master_pkt.payload_size=0;

	sender_address=mrk_net_myip_address_broadcast(0);
	sender_port=XLRU_PORT_SLAVES;

	mrk_net_udp_socket(&xlru_socket, "XLRU");
	mrk_net_udp_bind(&xlru_socket, 0, XLRU_PORT);

	mrk_net_udp_set_capacity(&xlru_socket, 1);


	xuart_initialize(0, 0, 0);
	xuart_initialize(1, 0, 0);
	xuart_initialize(2, 0, 0);

	return xlru_comm_ok;
}


void xlru_comm_dbg_add_data(unsigned int /*source*/, const char* data, unsigned int len)
{
	unsigned int cursor=master_pkt.payload_size;
	int left=(sizeof master_pkt.payload)-cursor;
	//int frag_size=sizeof(xlru::xlru_udp_data_t)+len;
	//frag_size=(frag_size+3) & (~0x03);
	if (left>0)
	{
		//xlru::xlru_udp_data_t* d=reinterpret_cast<xlru::xlru_udp_data_t*>(&master_pkt.payload[cursor]);
		//d->source=source;
		//d->len=len;
		//memcpy(d->data, data, len);
		char* p=reinterpret_cast<char*>(&master_pkt.payload[cursor]);
		memcpy(p, data, len);

		master_pkt.payload_size+=len;
	}
	else
	{
		//mrk_net_udp_send(&xlru_socket, &master_pkt, sizeof master_pkt, sender_address, sender_port, 0);
		//master_pkt.payload_size=0;
		xlru_comm_dbg_flush();
	}
}

void xlru_comm_dbg_flush()
{
	if (master_pkt.payload_size && udp_monitor_enabled)
	{
		if (sender_address==0)
			sender_address=mrk_net_myip_address_broadcast(0);
		if (!sender_address)
			return;

		master_pkt.payload[master_pkt.payload_size]=0;

		unsigned int len=((sizeof master_pkt)-sizeof(master_pkt.payload))+master_pkt.payload_size;
		mrk_net_udp_send(&xlru_socket, &master_pkt, len, sender_address, sender_port, 0);
	}
	master_pkt.payload_size=0;
}

enum xlru_comm_sts_t xlru_comm_extract_pending_data(xlru_comm_device_id_t id, char* destination_buffer, unsigned int* len, unsigned int max_len)
{
	unsigned int received_len=0;

	*len=0;

	if (id<1)
		return xlru_comm_err;

	if (override_mask==0)
	{
		xlru_comm_sts_t rx_sts=xlru_comm_ok;

		for(; received_len<max_len; ++received_len)
		{
			int r=xuart_receive(id-1, &destination_buffer[received_len]);
			if (r==xuart_rx_nodata)
			{
				break;
			}
			if (r<0)
			{
				if (r==xuart_rx_overrun)
					rx_sts=xlru_comm_err_overrun; //high priority error
				else if (rx_sts==xlru_comm_ok)
					rx_sts=xlru_comm_err_parity;
			}
		}
		if (received_len>0)
		{
			*len=received_len;
			return rx_sts;
		}
	}
	return xlru_comm_ok;
}

enum xlru_comm_sts_t xlru_comm_dbg_get_data(char* destination_buffer, unsigned int* len, unsigned int max_len)
{
	*len=0;

	if (!udp_monitor_enabled)
	{
		return xlru_comm_ok;
	}

	static struct xlru::xlru_udp_slave_t pkt;

	unsigned int received_len=0;

	mrk_net_ip_raw_address_t remote_address=sender_address;
	unsigned short remote_port=XLRU_PORT_SLAVES;

	mrk_net_result_t r=mrk_net_udp_receive(&xlru_socket, &pkt, sizeof pkt, &remote_address, &remote_port, &received_len, 0);

	if (r==mrk_net_ok)
	{
		if (pkt.marker==xlru::xlru_udp_slave_t::k_marker)
		{
			if (sender_address!=remote_address)
			{
				rtlog_printf("XLRU", rtlog_info|rtlog_breaking_news, "XLRU stub connected to %X:%u", (unsigned int)sender_address, sender_port);
				master_pkt.marker=xlru::xlru_udp_master_t::k_marker;
				master_pkt.payload_size=0;
			}
			sender_address=remote_address;
			sender_port=remote_port;

			override_mask=pkt.override_enable_mask;
			if (!override_mask)
			{
				return xlru_comm_ok;
			}
			if (pkt.payload_size)
			{
				if ((pkt.payload_size<sizeof pkt.payload) && (pkt.payload_size<max_len))
				{
				  *len=pkt.payload_size;
				  memcpy(destination_buffer, pkt.payload, pkt.payload_size);
				  return xlru_comm_ok;
				}
			}
			return xlru_comm_err_overrun;
		}
	}
	return xlru_comm_ok; //note: received_len==0!
}

enum xlru_comm_sts_t  xlru_comm_raw_transmit(xlru_comm_device_id_t id, const char* data, unsigned int len)
{
	if (id<1)
		return xlru_comm_err;

	if (override_mask==0)
	{
#if 0
		static const char* c="\r";
		int r=xuart_send(id-1, c, 2);
		if (r)
			return xlru_comm_err;
#endif
		int r=xuart_send(id-1, data, len);
		if (r)
			return xlru_comm_err;
	}
/*
	if (sender_address)
	{
		xlru_comm_dbg_add_data(0, data, len);
		//xlru_comm_dbg_flush();
	}
*/
	return xlru_comm_ok;
}


#endif




