/*
 * Copyright (c) 2002-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: prtio.c 12352 2010-01-11 03:42:23Z wilson.zhu $
 *
 * Write an ASCII representation of a record. Based on the printing code in
 * dagbits2.
 */

/* dagconvert headers. */
#include "inputs.h"
#include "outputs.h"
#include "utils.h"

/* Endace headers. */
#include "dagerf.h"


/*
 * Output stream. 
 */
static FILE * uOutfile;

/*
 * Counts number of packets printed 
 */
static uint64_t uPacketCounter = 0ULL;


/*
 * Set the stream on which output is to be produced.
 */
void
set_prt_output(char *out)
{
#if defined(__FreeBSD__) || defined(__linux__) || defined(__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)
	int fd;

#endif
	if (out == NULL)
	{
		uOutfile = stdout;
	}
	else
	{
#if defined(__FreeBSD__) || defined(__linux__) || defined(__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)

		fd = open(out, O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE, 0664);
		if (fd == -1) 
			dagutil_panic("Could not open %s for writing.\n", out);
		uOutfile = fdopen(fd, "w");
		
#elif defined (_WIN32)

		uOutfile = fopen(out, "wb");

#endif /* Platform-specific code. */

		if (uOutfile == NULL)
		{
			dagutil_panic("Could not open %s for writing.\n", out);
		}
	}
}


/* Close the currently open input (if any). */
void
close_prt_output(void)
{
	if (uOutfile && uOutfile != stdout)
		fclose(uOutfile);

	uOutfile = NULL;
}


/*
 * Write an ASCII representation of a record. If the record
 * is successfully written 1 is returned.
 */
int
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

write_prt_record(dag_record_t * rec, void *payload)

#else /* _WIN32 */

write_prt_record(dag_record_t * rec, char *payload)

#endif /* _WIN32 */

{
	int i;
	time_t time;
	struct tm *dtime;
	char dtime_str[80];
	int len;

	char *pload = (char*)payload;
	char abuf[17];

#ifdef PEDANTIC
	if (rec == NULL)
		dagutil_panic("Null record header passed to write_prt_record\n");
#endif /* PEDANTIC */

	uPacketCounter++;
	len = ntohs(rec->rlen);
	time = (time_t) (rec->ts >> 32);
	dtime = gmtime((time_t *) (&time));
	strftime(dtime_str, sizeof(dtime_str), "%Y-%m-%d %T", dtime);
	fprintf(uOutfile, "ts=0x%.16"PRIx64" %s.%07.0f UTC\n",
		rec->ts, dtime_str,
		(double) (rec->ts & 0xffffffffll) / 0x100000000ll *
		10000000);
	fprintf(uOutfile, "type: %s\n", dagerf_type_to_string(rec->type, TT_ERROR));

	if (dagerf_is_legacy_type((uint8_t*) rec))
	{
		dagutil_panic("Did not expect legacy data here.\n");
	}
	else
	{
		/* ERF format */
		fprintf(uOutfile, "dserror=%d rxerror=%d trunc=%d vlen=%d iface=%d rlen=%d lctr=%d wlen=%d\n",
			(int) rec->flags.dserror, (int) rec->flags.rxerror,
			(int) rec->flags.trunc, (int) rec->flags.vlen,
			(int) rec->flags.iface, ntohs(rec->rlen),
			ntohs(rec->lctr), ntohs(rec->wlen));
		
		if (dagerf_is_pos_type((uint8_t*) rec))
		{
			pos_rec_t *pos = (pos_rec_t *) payload;
			
			fprintf(uOutfile, "hdlc header=0x%08x\n", (uint32_t) ntohl(pos->hdlc));
			if (payload)
			{
				pload = (payload + 4);
				len -= 4;
			}
		}
		else if (dagerf_is_ethernet_type((uint8_t*) rec))
		{
			eth_rec_t *eth = (eth_rec_t *) payload;
			
			fprintf(uOutfile, "offset=%d etype=0x%04x\n", eth->offset, ntohs(eth->etype));
			fprintf(uOutfile, "dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
				eth->dst[0], eth->dst[1],
				eth->dst[2], eth->dst[3],
				eth->dst[4], eth->dst[5],
				eth->src[0], eth->src[1],
				eth->src[2], eth->src[3],
				eth->src[4],
				eth->src[5]);
			
			if (payload)
			{
				pload = (payload + 16);
				len -= 16;
			}
		}
		else if (ERF_TYPE_ATM == rec->type)
		{
			atm_rec_t *atm = (atm_rec_t *) payload;
			
			fprintf(uOutfile, "ATM header=0x%08x\n", (uint32_t) ntohl(atm->header));
			if (payload)
			{
				pload = (payload + 4);
				len -= 4;
			}
		}
		else if (ERF_TYPE_AAL5 == rec->type)
		{
			atm_rec_t *atm = (atm_rec_t *) payload;
			
			fprintf(uOutfile, "ATM header=0x%08x\n", (uint32_t) ntohl(atm->header));
			if (payload)
			{
				pload = (payload + 4);
				len -= 4;
			}
		}
		else
		{
			fprintf(uOutfile, "print %"PRIu64": record printing not implemented for ERF type %d\n", uPacketCounter, rec->type);
		}
	}
	
	len -= dag_record_size; /* don't try to print erf header */

	if (payload)
	{
		if (dagutil_get_verbosity() == 1)
		{
			for (i = 0; i < len; i++)
				fprintf(uOutfile, "%02x%s", *pload++ & 0xFF, ((i & 0xF) == 0xF) ? "\n" : " ");
		}
		else if (dagutil_get_verbosity() > 1)
		{
			memset(abuf, 0, 17);
			for (i = 0; i < len; i++)
			{
				abuf[i & 0xF] = isprint(*pload & 0xFF) ? (*pload & 0xFF) : '.';
				fprintf(uOutfile, "%02x ", *pload++ & 0xFF);
				if ((i & 0xF) == 0xF)
				{
					fprintf(uOutfile, "        %s\n", abuf);
					memset(abuf, 0, 17);
				}
			}
			
			if ((i & 0xF) != 0xF)
			{
				while (i++ & 0xF)
				{
					fprintf(uOutfile, "   ");
				}
				fprintf(uOutfile, "        %s\n", abuf);
			}
		}
	}
	fprintf(uOutfile, "\n");

	return 1;
}


/* Set the stream from which input is obtained. */
void
set_prt_input(char *in)
{
	dagutil_panic("prt is not a valid input device.\n");
}


/* Close the current input stream. */
void
close_prt_input(void)
{
	dagutil_panic("prt is not a valid input device.\n");
}


/*
 * Get pointer to the ERF header for the next packet
 * in the input stream. Returns null if no further
 * packets are available.
 */
dag_record_t *
get_next_prt_header(void)
{
	dagutil_panic("prt is not a valid input device.\n");
	return NULL;
}


/*
 * Returns a pointer to the payload of the most recent
 * packet. Returns null if there is no current packet.
 */
void *
get_prt_payload(void)
{
	dagutil_panic("prt is not a valid input device.\n");
	return NULL;
}
