/*
 * Copyright (c) 2004-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: dagflood_cli.c 4037 2006-04-03 05:03:37Z koryn $
 */

/* dagflood headers. */
#include "dagflood_config.h"
#include "dagflood_model.h"
#include "dagflood_view.h"

/* Endace headers. */
#include "dagapi.h"
#include "dagnew.h"
#include "dagutil.h"
#include "dagclarg.h"


/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagflood_cli.c 4037 2006-04-03 05:03:37Z koryn $";
static const char* const kRevisionString = "$Revision: 4037 $";


/* Macros and constants */
#define RUN_TIMELIMIT 0     /* Do a time based uContinue */
#define RUN_REPEAT 1        /* Do a repeat uCounters uContinue */

#define BURST_MAX ONE_MEBI
#define EIGHT_MEBI 8*ONE_MEBI
#define BUFSIZE 256

#define DEFAULT_DEVICE "dag0"

/* Commandline argument codes. */
enum
{
	CLA_FNAME,
	CLA_BMAX,
	CLA_DEVICE,
	CLA_HELP,
	CLA_TERMN,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_DELAY,
	CLA_MODE,
	CLA_NO_FLUSH,
	CLA_COUNT
};


/* Internal routines. */
static void print_version(void);
static void print_usage(ClArgPtr clarg);
static void sighandler(int signal);
static void report_statistics(DagfloodPtr flooder);


/* Implementation of internal routines. */
static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagflood - Endace DAG card transmit utility.\n");
	printf("Usage: dagflood [options]\n");
	dagclarg_display_usage(clarg, stdout);
}


static void
print_version(void)
{
	printf("dagflood (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}


/* Signal processing */
void
sighandler (int signal)
{
	static int set_run = 0;

	if (1 == set_run)
	{
		/* Already been signalled to stop - assume that we're stuck in the final dag_tx_get_stream_space(). */
		exit(EXIT_FAILURE);
	}

	set_run = 1;
}


void
report_statistics(DagfloodPtr flooder)
{
#if 0
	double total_time;

	total_time = (uCounters.end.tv_sec - uCounters.start.tv_sec) + (((uCounters.end.tv_usec) - uCounters.start.tv_usec) % 1000000) / 1000000.0;

	printf ("-------------------------------------------------\n");
	
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	printf ("total bytes sent to dag card: %lld (%.2f MiB)\n", (long long int) uCounters.wbytes, ((double) uCounters.wbytes) / ONE_MEBI);
	
#elif defined(_WIN32)

	printf ("total bytes sent to dag card: %10I64u (%.2f MiB)\n", uCounters.wbytes, ((double) uCounters.wbytes) / ONE_MEBI);

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform-specific code. */

	printf ("approx. average speed: %4.2f Mbps (%4.2f MiB/s)\n", (uCounters.wbytes * 8 / total_time) / 1000000, (uCounters.wbytes / total_time) / 1000000);
#endif /* 0 */

	printf("FIXME\n");
}


int
dagflood_main(int argc, char **argv)
{
	DagfloodPtr flooder = NULL;
	DagfloodConfigPtr config = NULL;
	FILE* errorfile = NULL;
	ClArgPtr clarg = NULL;
	int argindex;
	int code;
	int result;
	unsigned int run_mode;
	int dagstream;
	uint32_t burst_microseconds;
	uint32_t flush_tx_buffer;
	char dagname_buf[DAGNAME_BUFSIZE] = DEFAULT_DEVICE;
	char dagname[DAGNAME_BUFSIZE];
	
	char infile_buffer[BUFSIZE];
	uint32_t count;
	uint32_t runtime_seconds;
	uint32_t data_bytes;

	dagutil_set_progname("dagflood");

	/* Set up default DAG device. */
	if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
	}

	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char* const *) argv);

	dagclarg_add_string(clarg, "file containing ERF records to send", "--fname", 'f', "filename", infile_buffer, BUFSIZE, CLA_FNAME);
	dagclarg_add_uint(clarg, "transmit file <count> times before exiting", "--count", 'c', "count", &count, CLA_COUNT);
	dagclarg_add(clarg, "display help (this page)", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');
	dagclarg_add(clarg, "display version information", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "increase verbosity", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add_string(clarg, "DAG device to use.  Default: dag0.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add_uint(clarg, "set program to terminate after <n> seconds.  Default: 0 (continue indefinitely).", "--terminate", 't', "seconds", &runtime_seconds, CLA_TERMN);
	dagclarg_add_uint(clarg, "set burst_max (maximum data burst length).  Default: 1 MiB.", "--burst-max", 'l', "bytes", &data_bytes, CLA_BMAX);
	dagclarg_add_uint(clarg, "inter burst delay (microseconds)","--delay", 'u', "microseconds", &burst_microseconds, CLA_DELAY);
	dagclarg_add(clarg, "do not flush transmit buffer when a signal is caught", "--no-flush", 'x', CLA_NO_FLUSH);
	dagclarg_add_uint(clarg, "API mode. 1 = commit bytes, 2 = copy bytes.","--mode", 'r', "api-mode", &run_mode, CLA_MODE);

	/* Parse the command line options. */
	result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == result)
	{
		switch (code)
		{
		case CLA_DEVICE:
			if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
			{
				dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
			}
			dagutil_verbose_level(2, "device=%s\n", dagname);
			
			break;

		case CLA_FNAME:
			/* Do nothing. */
			break;

		case CLA_COUNT:
			run_mode  = RUN_REPEAT;
			break;

		case CLA_TERMN:
			run_mode  = RUN_TIMELIMIT;
			break;

		case CLA_BMAX:
			/* Do nothing. */
			break;

		case CLA_HELP:
			print_usage(clarg);
			return EXIT_SUCCESS;
			break;

		case CLA_VERBOSE:
			dagutil_inc_verbosity();
			errorfile = stderr;
			break;

		case CLA_VERSION:
			print_version();
			return EXIT_SUCCESS;
			break;

		case CLA_MODE:
			break;

		case CLA_DELAY:
			break;
		
		case CLA_NO_FLUSH:
			flush_tx_buffer = 0;
			break;

		default:
			/* Unknown option. */
			dagutil_error("unknown option %s\n", argv[argindex]); 
			print_usage(clarg);
			return EXIT_FAILURE;
		}

		result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	if (-1 == result)
	{
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}
	
	/* Check for data file */
	if (0 == strlen(infile_buffer))
	{
		print_usage(clarg);
		dagutil_panic("error: must specify one transmit file\n");
	}

	/* Catch signals */
	dagutil_set_signal_handler(sighandler);
	
	/* ClargPtr should no longer be necessary. */
	dagclarg_dispose(clarg);
	
	/* Create a configuration to pass to the main capture loop. */
	config = dagflood_config_init();

	/* Set up the configuration options. */
	dagflood_config_set_device(config, dagname);
	dagflood_config_set_infile_name(config, (const char*) infile_buffer);
	dagflood_config_set_dagstream(config, dagstream);
	dagflood_config_set_run_seconds(config, runtime_seconds);
	dagflood_config_set_verbosity(config, dagutil_get_verbosity());

	flooder = dagflood_create(config);
	if (NULL == flooder)
	{
		return EXIT_FAILURE;
	}

	if (dagutil_get_verbosity())
	{
		dagflood_view_init(flooder, dagname, dagutil_get_verbosity());
	}

	result = dagflood_transmit(flooder);

	/* Report some statistics */
	report_statistics(flooder);

	return result;
}


#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagflood_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */
