/*
 * Copyright (c) 2004-2005 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.c 10454 2008-12-05 03:29:40Z dlim $
 */

/*****************************************************************************/
/* Includes                                                                  */
/*****************************************************************************/

/* Endace headers. */
#include "dagapi.h"
#include "dagnew.h"
#include "dagutil.h"
#include "dagclarg.h"
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
#include <unistd.h>
#endif

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagflood.c 10454 2008-12-05 03:29:40Z dlim $";
static const char* const kRevisionString = "$Revision: 10454 $";


/*****************************************************************************/
/* 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 BURST_MAX 0x200000
#define EIGHT_MEBI 8*ONE_MEBI
#define BUFSIZE 256

#define DEFAULT_DEVICE "dag0"
static char dagname_buf[DAGNAME_BUFSIZE] = DEFAULT_DEVICE;
static char dagname[DAGNAME_BUFSIZE];

static char fname_buf[BUFSIZE];
static char dlay_buf[BUFSIZE];
static char mode_buf[BUFSIZE];
static uint32_t count;
static uint32_t tlimit;
static uint32_t bmax;

void *file_buffer = NULL;
/*****************************************************************************/
/* Windows support                                                           */
/*****************************************************************************/
#if defined(_WIN32)

/* Windows doesn't have fseeko & ftello functions, we can use these equvalents.  */
/* Note: Windows also has undocumented _fseeki64 and _ftelli64 functions, but    */
/*       they are only built in the static CRT libraries.                        */
#define fseeko(fm, pos, type) _lseeki64(_fileno(fm), (pos), (type))
#define ftello(fm) _lseeki64(_fileno(fm), 0, SEEK_CUR)

#endif



/*****************************************************************************/
/* Data structures                                                           */
/*****************************************************************************/
typedef struct
{
	char * file;          /* Input file with ERF records to transmit      */
	char device[DAGNAME_BUFSIZE]; /* Which DAG device to use              */
	int stream;
	uint32_t burst_max;   /* Maximum burst size                           */
	char run_mode;        /* Run mode: time or repeat based               */
	uint32_t repeat;      /* How many times to send the file, decrements  */
	uint32_t total_repeats; /* How many times to send the file            */
	uint32_t run_type;    /* Which API interface to use when sending      */
	uint32_t timelimit;   /* Send the file during this time (seconds)     */
	uint32_t usleep;      /* Inter burst delay (microseconds)             */
	int flush_tx_buffer;  /* 1 => flush transmit buffer on exit           */
	int accuracy;         /* 1 => include read/copy time in inter-burst gap */
	int busywait;         /* 1 => use accurate dagutil busywait functions */
} conf_t;

typedef struct
{
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	void * base;          /* Pointer to the mmap'd file                */
	void * pos;           /* Absolute pointer inside mmap buffer       */

#elif defined(_WIN32)

	char * base;          /* Pointer to the mmap'd file                */
	char * pos;           /* Absolute pointer inside mmap buffer       */
	HANDLE mapping;

#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif 
	int fd;               /* File descriptor                           */
	off_t size;          /* File size                                 */
} file_t;

typedef struct
{
	uint64_t wbytes;      /* Bytes written to stream buffer            */
	uint64_t old_wbytes;  /* Bytes written up to the previous loop     */
	uint32_t seconds;     /* Seconds the application has been running  */
	struct timeval start; /* Start time                                */
	struct timeval end;   /* End time                                  */
	uint64_t bytes_to_send; /* Number of bytes to write in repeat mode */
} count_t;

/* Commandline argument codes. */
enum
{
	CLA_FNAME,
	CLA_BMAX,
	CLA_DEVICE,
	CLA_HELP,
	CLA_TERMN,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_DELAY,
	CLA_MODE,
	CLA_NFLUSH,
	CLA_COUNT,
	CLA_ACCURACY,
	CLA_BUSYWAIT,
	CLA_MEMCPY_SIZE,
	CLA_NUMBER
};


/*****************************************************************************/
/* File-scope variables                                                      */
/*****************************************************************************/
static int uDagFd;        /* DAG file descriptor                       */
static conf_t uConfiguration; /* Configuration options                 */
static count_t uCounters; /* Counters                                  */
static char uContinue;    /* Continue main loop?                       */
static uint8_t mmap_failed = 0; /* File mapping failed ?           */
static char *tx_buf1;
static char *tx_buf2;

/*****************************************************************************/
/* Internal routines                                                      */
/*****************************************************************************/

//static void print_version(void);
static void print_usage(ClArgPtr clarg);
static void initialize_dag(void);
static void finalize_dag(void);
static void init_file (char *filename, file_t *file);
static void run_file_copy_bytes(file_t *file);
static void run_file_commit_bytes(file_t * file,int memcpy_size,int number);
static void copy_bytes();
static void commit_bytes();
void sighandler (int signal);
void init_options(void);
void report_statistics(void);
#if defined _WIN32
void remove_mapping(file_t * file);
#endif

/*****************************************************************************/
/*** Functions                                                             ***/
/*****************************************************************************/
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);
}

/*****************************************************************************/
#if 0
static void
print_version(void)
{
	printf("dagflood (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}
#endif
/*****************************************************************************/
static void
initialize_dag(void)
{
	uDagFd = dag_open(uConfiguration.device);
	if (uDagFd < 0)
		dagutil_panic("dag_open(%s): %s\n", uConfiguration.device, strerror(errno));

	if (dag_tx_get_stream_count(uDagFd) < 1)
		dagutil_panic("dag_tx_get_stream_count(%s): The firmware loaded on this DAG card does not support transmit features.\n", uConfiguration.device);

	if (uConfiguration.stream == 0) {
	    /* The default stream for tx is 1, but the command line parser doesn't know this. Fix it up here. */
	    uConfiguration.stream = 1;
	}

	if (dag_attach_stream(uDagFd, uConfiguration.stream, 0, dagutil_max(BURST_MAX, uConfiguration.burst_max)) != 0)
		dagutil_panic("dag_attach_stream(%s): %s\n", uConfiguration.device, strerror(errno));

	if (dag_start_stream (uDagFd, uConfiguration.stream) < 0)
		dagutil_panic("dag_start_stream(%s): %s\n", uConfiguration.device, strerror(errno));
}

/*****************************************************************************/
static void
finalize_dag(void)
{
	if (dag_stop_stream(uDagFd, uConfiguration.stream) < 0)
		dagutil_panic("dag_stop_stream(%s): %s\n", uConfiguration.device, strerror(errno));

	if (dag_detach_stream(uDagFd, uConfiguration.stream) != 0)
		dagutil_panic("dag_detach_stream(%s): %s\n", uConfiguration.device, strerror(errno));

	if (dag_close(uDagFd) < 0)
		dagutil_panic("dag_close(%s): %s\n", uConfiguration.device, strerror(errno));
}

/*****************************************************************************/
static void
init_file (char *filename, file_t *file)
{
	struct stat file_stats;
#if defined(_WIN32)
	HANDLE hFile, hMapHandle;
#endif /* _WIN32 */
	FILE *fd = NULL;
	int ret_val = 0;
	/* Init file data structure. */
	memset(file, 0, sizeof(*file));
	printf("Inside function init_file \n");
	/* Open file. */
	printf("file name : %s\n",filename);
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	file->fd = open(filename, O_RDONLY);
	fd = fopen(filename,"r");
	if(fd == NULL)
	{
		printf("Not able to open the file.");
	}else
	{
		printf("Opened the file \n");
	}
#elif defined(_WIN32)

	file->fd = _open(filename, _O_RDONLY | _O_BINARY ,_S_IREAD);

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

	if (-1 == file->fd)
	{
		dagutil_panic("open %s\n", filename);
	}

	/* Get file size. */
	if ((fstat(file->fd, &file_stats)) < 0) 
		dagutil_panic("fstat: %s\n", strerror(errno));

	file->size = file_stats.st_size;

	uCounters.bytes_to_send = file_stats.st_size;
	if (uConfiguration.run_mode == RUN_REPEAT)
		uCounters.bytes_to_send *= uConfiguration.repeat;

#if (defined(__SVR4) && defined(__sun))
    printf("file input size: %"PRIu64" bytes\n", (long long)file->size); //FIXME : use some general solution
#elif (_WIN32)
	printf("file input size: %"PRId32" bytes\n", file->size);
#else
    printf("file input size: %"PRIu64" bytes\n", file->size);
#endif

	if (0 == file->size)
	{
		dagutil_panic("file to transmit was empty\n");
	}

	/* Mmap file, so we have it in memory. */
	if (dagutil_get_verbosity() > 0)
	{
		fprintf(stdout, "caching file... ");
	}

#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	
	file->base = MAP_FAILED;
	/* mmap length parameter is size_t */
	/*Instead of mmapping we will read the whole file and copy it into a buffer.for better caching.*/
	#if 0
	if (file->size < 0x80000000)
		file->base = mmap(NULL, file->size, PROT_READ, MAP_SHARED, file->fd, 0);
	if (file->base == MAP_FAILED)
	{
		mmap_failed = 1;
		close(file->fd);
                if ((tx_buf1 = malloc(uConfiguration.burst_max)) == NULL)
			dagutil_panic("Could not allocate tx buffer\n");
                if ((tx_buf2 = malloc(uConfiguration.burst_max)) == NULL)
			dagutil_panic("Could not allocate tx buffer\n");
		if (dagutil_get_verbosity() > 0)
		{
			fprintf(stdout, "too large\n");
			fprintf(stdout, "transmitting from file directly instead (rate will be limited to disk speed).\n");
		}
		return;
	}
	#endif
	//file_buffer = malloc((file->size + 10));
	ret_val = posix_memalign(&file_buffer,4096,(file->size + 10));
	if(ret_val != 0)
	{
		printf("File size large. not able to allocate memory \n");
		return;
	}
 	if(fread(file_buffer,file->size,1,fd) !=0)
	{	
		printf("allocated memory equal to the file size \n");	
	}else
		printf("fread returned error \n");
 	
	file->base = file_buffer;
		
#elif defined(_WIN32)
	/* mmap is done slightly differently in Windows. */

	/* Get a mappable handle to the file. */
	hFile = (HANDLE)_get_osfhandle(file->fd);

	if (hFile == INVALID_HANDLE_VALUE)
		dagutil_panic("invalid file descriptor: %s\n",strerror(errno));

	hMapHandle =  CreateFileMapping(
		hFile,              /* Handle to file */
		NULL,               /* LPSECURITY_ATTRIBUTES lpAttributes, */
		PAGE_READONLY,      /* DWORD flProtect, */
		0,                  /* DWORD dwMaximumSizeHigh, */
		(DWORD)file->size,  /* DWORD dwMaximumSizeLow, */
		"dagfloodmap"       /* LPCTSTR lpName */
		);

	if (hMapHandle == NULL)
	{
		mmap_failed = 1;
		CloseHandle(hMapHandle);
		_close(file->fd);
		return;
	}

	/* do the mapping. */
	file->base = MapViewOfFile(
		hMapHandle,     /* HANDLE hFileMappingObject, */
		FILE_MAP_READ,  /* DWORD dwDesiredAccess, */
		0,              /* DWORD dwFileOffsetHigh, */
		0,              /* DWORD dwFileOffsetLow, */
		0               /* default map entire file */
	);

	if (file->base == NULL)
	{
		mmap_failed = 1;
		CloseHandle(hMapHandle);
		_close(file->fd);
		return;
	}

	file->mapping = hMapHandle;
	
#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif 

	if (dagutil_get_verbosity() > 0)
		fprintf(stdout, "done.\n");

	/* Update pointer inside file */
	file->pos = file->base; 
}

/*****************************************************************************/
static void
run_file_copy_bytes(file_t *file)
{
	uint32_t burst_length;
	unsigned long long wake_time;
	
	/* Main loop */
	uContinue = 1;
	while (uContinue)
	{
		if ( (uConfiguration.usleep) && (uConfiguration.accuracy) ) {
			dagutil_sleep_get_wake_time(&wake_time, uConfiguration.usleep);
		}

		/* The maximum burst length should not be greater than the one defined 
		 * as default nor the remaining bytes in the file buffer.
		 */
		burst_length = (uint32_t) dagutil_min((uint64_t) uConfiguration.burst_max, (uint64_t)(file->size + file->base - file->pos));

		/* Write to dag card (copy, so it's not zero-copy). */
		dag_tx_stream_copy_bytes (uDagFd, uConfiguration.stream, file->pos, burst_length);

		/* Advance offsets and counters. */
		uCounters.wbytes += burst_length;
		file->pos += burst_length;

		/* Keep file pointer inside boundaries. */
		if (file->pos >= file->base + file->size)
		{
			file->pos = file->base;
			uConfiguration.repeat--;
		}

		/* Should we repeat more times? */
		/* We were running dagflood with a repeat number option */
		if ((uConfiguration.run_mode == RUN_REPEAT) && (uConfiguration.repeat == 0))
		{
			uContinue = 0;
			uConfiguration.flush_tx_buffer = 1; /* Normal exit, flush buffer. */
		}

		/* Inter burst delay. */
		if (uConfiguration.usleep) {
			if (uConfiguration.accuracy) {
				if (uConfiguration.busywait)
					dagutil_sleep_until(wake_time);
				else
					dagutil_sleep_until_nobusy(wake_time);
			} else {
				if (uConfiguration.busywait)
					dagutil_microsleep(uConfiguration.usleep);
				else
					usleep(uConfiguration.usleep);
			}
		}
	}

	/* Wait for the stream to be almost empty */
	/* This way we are sure almost all the records are transmitted when we exit. */
	if (1 == uConfiguration.flush_tx_buffer)
	{
		dag_tx_get_stream_space(uDagFd, uConfiguration.stream, dag_get_stream_buffer_size(uDagFd, uConfiguration.stream) - 8);
	}
}

/*****************************************************************************/
static void
run_file_commit_bytes(file_t * file,int memcpy_size,int number)
{
	uint32_t burst_length;
	void * record;
	unsigned long long wake_time;

#if defined(_WIN32)
	MSG msg;
#endif /* _WIN32 */
	
	burst_length = memcpy_size;
	#if 0
	printf("memcpy_size : %d\n ",burst_length);		
	printf("number : %d\n ",number);
	#endif
	/* Main loop */
	uContinue = 1;
	while (uContinue)
	{
		if ( (uConfiguration.usleep) && (uConfiguration.accuracy) ) {
			dagutil_sleep_get_wake_time(&wake_time, uConfiguration.usleep);
		}

		/* The maximum burst length should not be greater than the one defined 
		 * as default nor the remaining bytes in the file buffer.
		 */
		/* Get space for writing. */
		record = dag_tx_get_stream_space(uDagFd, uConfiguration.stream, file->size/*(burst_length * number)*/);

		/* Copy bytes. */
		//for (i = 0; i < number; i++)
		{
			memcpy(record, file->pos,file->size);
			//usleep(1);
			//file->pos += burst_length;
		}
		
		/* Commit data. */
		record = dag_tx_stream_commit_bytes(uDagFd, uConfiguration.stream, file->size /*(burst_length*number)*/);
		//usleep(1);
		/* Advance offsets and counters. */
		uCounters.wbytes += file->size;

		/* Keep file pointer inside boundaries. */
		if (file->pos >= file->base + file->size)
		{
			file->pos = file->base;
		#if 0
			uConfiguration.repeat--;
		#endif
		}

		/* Should we repeat more times? */
		/* We were running dagflood with a repeat number option */
		#if 1
		if ((uConfiguration.run_mode == RUN_REPEAT) && (uConfiguration.repeat == 0))
		{
			uContinue = 0;
			uConfiguration.flush_tx_buffer = 1; /* Normal exit, flush buffer. */
		}
		#endif
#if defined(_WIN32)
		while (PeekMessage(&msg, NULL, 0, 0, PM_QS_POSTMESSAGE|PM_REMOVE) != 0)
		{
			DispatchMessage(&msg);
		}
#endif /*_WIN32 */

		/* Inter burst delay. */
		if (uConfiguration.usleep) {
			if (uConfiguration.accuracy) {
				if (uConfiguration.busywait)
					dagutil_sleep_until(wake_time);
				else
					dagutil_sleep_until_nobusy(wake_time);
			} else {
				if (uConfiguration.busywait)
					dagutil_microsleep(uConfiguration.usleep);
				else
					usleep(uConfiguration.usleep);
			}
		}

	}

	/* Wait for the stream to be almost empty.
	 * This way we are sure almost all the records are transmitted when we exit.
	 */
	//uConfiguration.flush_tx_buffer = 1;
	if (1 == uConfiguration.flush_tx_buffer)
	{
		record = dag_tx_get_stream_space(uDagFd, uConfiguration.stream, dag_get_stream_buffer_size(uDagFd, uConfiguration.stream) - 8);
	}
}
#if 0
static void
run_file_commit_bytes_new(file_t * file,int memcpy_size,int number)
{
	uint32_t burst_length;
	void * record;
	unsigned long long wake_time;
	//int number;
	int i;
#if defined(_WIN32)
	MSG msg;
#endif /* _WIN32 */
	printf("dagflood.c run_file_commit_bytes_new \n");
	
	burst_length = memcpy_size; // hardcoding it to 16K
	printf("Burst Length : %x\n",burst_length);		
	printf("number = %d\n",number);
	//number = (file->size / burst_length);
	/* Main loop */
	uContinue = 1;
	while (uContinue)
	{
		if ( (uConfiguration.usleep) && (uConfiguration.accuracy) ) {
			dagutil_sleep_get_wake_time(&wake_time, uConfiguration.usleep);
		}

		/* The maximum burst length should not be greater than the one defined 
		 * as default nor the remaining bytes in the file buffer.
		 */
		//burst_length = (uint32_t) dagutil_min((uint64_t) uConfiguration.burst_max, (uint64_t)(file->size + file->base - file->pos));
		
		#if 0	
		burst_length = 32768; // hardcoding it to 16K
		
		//printf("Burst Length : %x\n",burst_length);		

		number = (file->size / burst_length);

		#endif
		//printf("Number : %d\n",number);
		
		/* Get space for writing. */
		record = dag_tx_get_stream_space(uDagFd, uConfiguration.stream, (burst_length * number));

		/* Copy bytes. */
		for (i = 0; i < number; i++)
		{
			memcpy(record, file->pos, burst_length);
			file->pos += burst_length;
		}
		
		/* Commit data. */
		record = dag_tx_stream_commit_bytes(uDagFd, uConfiguration.stream, (burst_length*number));

		/* Advance offsets and counters. */
		uCounters.wbytes += (burst_length*number);
		//file->pos += burst_length;

		/* Keep file pointer inside boundaries. */
		if (file->pos >= file->base + file->size)
		{
			file->pos = file->base;
			uConfiguration.repeat--;
		}

		/* Should we repeat more times? */
		/* We were running dagflood with a repeat number option */
		#if 0
		if ((uConfiguration.run_mode == RUN_REPEAT) && (uConfiguration.repeat == 0))
		{
			uContinue = 0;
			uConfiguration.flush_tx_buffer = 1; /* Normal exit, flush buffer. */
		}
		#endif
#if defined(_WIN32)
		while (PeekMessage(&msg, NULL, 0, 0, PM_QS_POSTMESSAGE|PM_REMOVE) != 0)
		{
			DispatchMessage(&msg);
		}
#endif /*_WIN32 */

		/* Inter burst delay. */
		if (uConfiguration.usleep) {
			if (uConfiguration.accuracy) {
				if (uConfiguration.busywait)
					dagutil_sleep_until(wake_time);
				else
					dagutil_sleep_until_nobusy(wake_time);
			} else {
				if (uConfiguration.busywait)
					dagutil_microsleep(uConfiguration.usleep);
				else
					usleep(uConfiguration.usleep);
			}
		}

	}

	/* Wait for the stream to be almost empty.
	 * This way we are sure almost all the records are transmitted when we exit.
	 */
	printf("calling dag_tx_get_stream_space ; wait for the stream to be almost empty \n");
	if (1 == uConfiguration.flush_tx_buffer)
	{
		record = dag_tx_get_stream_space(uDagFd, uConfiguration.stream, dag_get_stream_buffer_size(uDagFd, uConfiguration.stream) - 8);
	}
}
#endif
/*****************************************************************************/
static void
copy_bytes()
{
	FILE *fp = NULL;
	uint32_t byte_cnt;
	void *tx_buf[2];
	uint8_t flip = 0;
	unsigned long long wake_time;

	uContinue = 1;
	tx_buf[0] = tx_buf1;
	tx_buf[1] = tx_buf2;

	if ((fp = fopen(uConfiguration.file,"rb")) == NULL)
	{
		exit(EXIT_FAILURE);
	}

	byte_cnt = fread(tx_buf[flip], sizeof(char), uConfiguration.burst_max, fp);
	if (byte_cnt < uConfiguration.burst_max)
	{
		if (!feof(fp))
			dagutil_panic("file error\n");
	}

	/* Main loop */
	while (uContinue)
	{
		if ( (uConfiguration.usleep) && (uConfiguration.accuracy) ) {
			dagutil_sleep_get_wake_time(&wake_time, uConfiguration.usleep);
		}

		/* Write to dag card (copy, so it's not zero-copy). */
		dag_tx_stream_copy_bytes (uDagFd, uConfiguration.stream, (void*)tx_buf[flip], byte_cnt);
		flip = !flip;

		/* Advance counters. */
		uCounters.wbytes += byte_cnt;
		
		if (feof(fp))
		{
			uConfiguration.repeat--;
			if (uConfiguration.repeat)
				if (fseek(fp,0L,SEEK_SET))
					dagutil_panic("file error\n");
		}
					
       	/* Should we repeat more times? */
		/* We were running dagflood with a repeat number option */


		if ((uConfiguration.run_mode == RUN_REPEAT) && (uConfiguration.repeat == 0))
		{
			uContinue = 0;
			uConfiguration.flush_tx_buffer = 1; /* Normal exit, flush buffer. */
			fclose(fp);
		}
		else
		{
			byte_cnt = fread(tx_buf[flip], sizeof(char), uConfiguration.burst_max, fp);
			if (byte_cnt < uConfiguration.burst_max)
			{
				if (!feof(fp))
					dagutil_panic("file error\n");
			}
		}

		/* Inter burst delay. */
		if (uConfiguration.usleep) {
			if (uConfiguration.accuracy) {
				if (uConfiguration.busywait)
					dagutil_sleep_until(wake_time);
				else
					dagutil_sleep_until_nobusy(wake_time);
			} else {
				if (uConfiguration.busywait)
					dagutil_microsleep(uConfiguration.usleep);
				else
					usleep(uConfiguration.usleep);
			}
		}

	}

	/* Wait for the stream to be almost empty */
	/* This way we are sure almost all the records are transmitted when we exit. */
	if (1 == uConfiguration.flush_tx_buffer)
	{
		dag_tx_get_stream_space(uDagFd, uConfiguration.stream, dag_get_stream_buffer_size(uDagFd, uConfiguration.stream) - 8);
	}
}
/*****************************************************************************/
static void
commit_bytes()
{
	FILE *fp = NULL;
	uint32_t byte_cnt;
	void * record;
	unsigned long long wake_time;

#if defined(_WIN32)
	MSG msg;
#endif /* _WIN32 */
	
	fp = fopen(uConfiguration.file,"rb");
	if (fp == NULL)
	{
		exit(EXIT_FAILURE);
	}

	/* Main loop */
	uContinue = 1;
	while (uContinue)
	{
		if ( (uConfiguration.usleep) && (uConfiguration.accuracy) ) {
			dagutil_sleep_get_wake_time(&wake_time, uConfiguration.usleep);
		}

		/* Get space for writing. */
		record = dag_tx_get_stream_space(uDagFd, uConfiguration.stream, uConfiguration.burst_max);
		if (!record)
			dagutil_panic("dag_tx_get_stream_space\n");
		
		byte_cnt = fread(record , sizeof(char), uConfiguration.burst_max, fp);
		if (byte_cnt < uConfiguration.burst_max)
		{
			if (!feof(fp))
				dagutil_panic("file error\n");
		}

		/* Commit data. */
		record = dag_tx_stream_commit_bytes(uDagFd, uConfiguration.stream, byte_cnt);

		/* Advance counters. */
		uCounters.wbytes += byte_cnt;

		if (feof(fp))
		{
			uConfiguration.repeat--;
			if (uConfiguration.repeat)
			{
				if (fseeko(fp,0,SEEK_SET))
				{
					dagutil_panic("file error\n");
				}
			}
		}
		
        /* Should we repeat more times? */
		/* We were running dagflood with a repeat number option */
		if ((uConfiguration.run_mode == RUN_REPEAT) && (uConfiguration.repeat == 0))
		{
			uContinue = 0;
			uConfiguration.flush_tx_buffer = 1; /* Normal exit, flush buffer. */
			fclose(fp);
		}

#if defined(_WIN32)
		while (PeekMessage(&msg, NULL, 0, 0, PM_QS_POSTMESSAGE|PM_REMOVE) != 0)
		{
			DispatchMessage(&msg);
		}
#endif /*_WIN32 */

		/* Inter burst delay. */
		if (uConfiguration.usleep) {
			if (uConfiguration.accuracy) {
				if (uConfiguration.busywait)
					dagutil_sleep_until(wake_time);
				else
					dagutil_sleep_until_nobusy(wake_time);
			} else {
				if (uConfiguration.busywait)
					dagutil_microsleep(uConfiguration.usleep);
				else
					usleep(uConfiguration.usleep);
			}
		}

	}

	/* Wait for the stream to be almost empty.
	 * This way we are sure almost all the records are transmitted when we exit.
	 */
	if (1 == uConfiguration.flush_tx_buffer)
	{
		record = dag_tx_get_stream_space(uDagFd, uConfiguration.stream, dag_get_stream_buffer_size(uDagFd, uConfiguration.stream) - 8);
	}
}
/*****************************************************************************/
/* 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);
	}
	uContinue = 0;
	set_run = 1;
}

/*****************************************************************************/

/* Time signal handling. */
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

void
time_handler(int signal)

#elif defined(_WIN32)

VOID CALLBACK
time_handler(LPVOID lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue)

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

{
	uint64_t temp1;
	uint32_t per1, per2;

	/* Increment number of seconds the application has been running. */
	uCounters.seconds++;

	/* Report some statistics. */
	if (dagutil_get_verbosity() > 0)
	{
		fprintf (stderr, "Line Rate: %4.2ld MBytes/sec PCI Rate: %4.2ld MBytes/sec\r",
			 (long int) ((uCounters.wbytes - uCounters.old_wbytes) / 1000000),(long int)(((uCounters.wbytes - uCounters.old_wbytes)* 64) / 88000000)
			/* (long int) ((uCounters.wbytes - uCounters.old_wbytes) / ONE_MEBI)*/);
		switch(uConfiguration.run_mode) {
		case RUN_TIMELIMIT:
			if (uConfiguration.timelimit) {
				temp1 = uCounters.seconds*10000/uConfiguration.timelimit;
				per1 = (uint32_t)temp1/100;
				per2 = (uint32_t)temp1%100;
				fprintf (stderr, "Run: %d sec Remaining: %d sec Completion: %02d.%02d%%\r",
					 uCounters.seconds,
					 uConfiguration.timelimit-uCounters.seconds,
					 per1, per2);
			} else {
				fprintf (stderr, "Run: %d sec no timeout set\r",
					 uCounters.seconds);	
			}
			break;
		case RUN_REPEAT:
			temp1 = uCounters.wbytes*10000/uCounters.bytes_to_send;
			per1 = (uint32_t)temp1/100;
			per2 = (uint32_t)temp1%100;
			#if 0 //this info is not relavent anymore.	
			fprintf (stderr, "Repeat: %d/%d Completion: %02d.%02d%%\r",
				 uConfiguration.total_repeats-uConfiguration.repeat+1,
				 uConfiguration.total_repeats,
				 per1, per2);
			#endif 
			break;
		default:
			dagutil_panic("unknown run mode %d", uConfiguration.run_mode);
		}

		/* Update counters. */
		uCounters.old_wbytes = uCounters.wbytes;
	}

	/* Have we reached the time limit? */
	if (uCounters.seconds == uConfiguration.timelimit)
	{
		/* End program. */
		uContinue = 0;
	}
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	else
	{
		/* In Windows the signal is already set up to elapse again after a second.
		 * Reprogram alarm.
		 */
		alarm(1);
	}
#endif 
}

/*****************************************************************************/
void
init_options(void)
{
	memset (&uConfiguration, 0, sizeof(uConfiguration));

	uConfiguration.burst_max = BURST_MAX;
	uConfiguration.run_mode  = RUN_REPEAT;
	uConfiguration.run_type  = 1;
	uConfiguration.flush_tx_buffer = 1;
	uConfiguration.repeat = 1;
	uConfiguration.total_repeats = 1;
	uConfiguration.accuracy = 0;
	uConfiguration.busywait = 0;
	strncpy(uConfiguration.device,dagname,DAGNAME_BUFSIZE);
}



/*****************************************************************************/
#if defined(_WIN32)
void
remove_mapping(file_t * file)
{
	UnmapViewOfFile(file->base);

	CloseHandle(file->mapping);
}
#endif /* _WIN32 */ 
/*****************************************************************************/
void
report_statistics(void)
{
	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: Line Rate %lld MegaBytes PCI Rate %lld MegaBytes \n", (long long int) uCounters.wbytes / 1000000,(long long int) (uCounters.wbytes * 64)/ 88000000);
	
#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: Line Rate %4.2f MB/s  PCI Rate %4.2f MB/s \n", (uCounters.wbytes / total_time) / 1000000, ((uCounters.wbytes / total_time)*64)/88000000);
}

/*****************************************************************************/
int
dagflood_main(int argc, char **argv)
{
	file_t file; /* file queue */
	FILE* errorfile = NULL;
	ClArgPtr clarg = NULL;
	int argindex;
	int code;
	int result;
	int memcpy_size = 16384;
	int number = 1000;
	
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	struct  sigaction sigact; /* signal catching */
	
#elif defined(_WIN32)

	HANDLE hTimer = NULL;
	LARGE_INTEGER liDueTime;
	/* set the timer to expire every second */
	liDueTime.QuadPart = -10000000;
	
#else
#error Compiling on an unsupported platform - please contact <support@endace.com> for assistance.
#endif /* Platform-specific code. */

	dagutil_set_progname("dagflood");

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

	init_options();

	/* 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", fname_buf, 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", &tlimit, CLA_TERMN);
	dagclarg_add_uint(clarg, "set burst_max (maximum data burst length).  Default: 1 MiB.", "--burst-max", 'l', "bytes", &bmax, CLA_BMAX);
	dagclarg_add_string(clarg, "inter burst delay (microseconds)","--delay", 'u', "microseconds", dlay_buf, BUFSIZE, CLA_DELAY);
	dagclarg_add(clarg, "do not flush transmit buffer when a signal is caught", "--no-flush", 'x', CLA_NFLUSH);
	dagclarg_add_string(clarg, "API mode. 1 = commit bytes, 2 = copy bytes.","--mode", 'r', "api-mode", mode_buf, BUFSIZE, CLA_MODE);
	dagclarg_add(clarg, "make inter-burst delay (-u) include the data reading/copying time. Recommended.", "--accuracy", 'a', CLA_ACCURACY);
	dagclarg_add(clarg, "increases accuracy of inter-burst delay (-u) timing, but uses 100% of CPU", "--busywait", 'b', CLA_BUSYWAIT);

	dagclarg_add_int(clarg, "This denotes the size of data for one memcpy operation into the memory hole.", "--memcpy-size", 'm', "memcpy size", &memcpy_size, CLA_MEMCPY_SIZE);
	dagclarg_add_int(clarg, "This denotes the number of memcpy-sizes in one burst for the card.", "--number", 'n', "number", &number, CLA_NUMBER);

	/* 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, uConfiguration.device, DAGNAME_BUFSIZE, &uConfiguration.stream))
			{
				dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
			}
			dagutil_verbose_level(2, "device=%s:%d\n", uConfiguration.device, uConfiguration.stream);
			
			break;

		case CLA_FNAME:
			uConfiguration.file = fname_buf;
			break;

		case CLA_COUNT:
			uConfiguration.repeat = count;
			uConfiguration.total_repeats = count;
			uConfiguration.run_mode  = RUN_REPEAT;
			break;

		case CLA_TERMN:
			uConfiguration.timelimit = tlimit;
			uConfiguration.run_mode  = RUN_TIMELIMIT;
			break;

		case CLA_BMAX:
			uConfiguration.burst_max = bmax;
			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:
			uConfiguration.run_type = strtoul(mode_buf, NULL, 0);
			break;

		case CLA_DELAY:
			uConfiguration.usleep = strtoul(dlay_buf, NULL, 0);
			break;
		
		case CLA_NFLUSH:
			uConfiguration.flush_tx_buffer = 0;
			break;

		case CLA_ACCURACY:
			uConfiguration.accuracy = 1;
			break;

		case CLA_BUSYWAIT:
			uConfiguration.busywait = 1;
			break;

		case CLA_MEMCPY_SIZE:
			break;

		case CLA_NUMBER:
			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 (uConfiguration.file != NULL)
	{
		init_file(uConfiguration.file, &file);
	}
	else
	{
		print_usage(clarg);
		dagutil_panic("error: must specify one transmit file\n");
	}

	/* ClargPtr should no longer be necessary. */
	dagclarg_dispose(clarg);

	/* Init dag session and device */
	initialize_dag();

	/* Catch signals */
	dagutil_set_signal_handler(sighandler);

	/* Handle alarm signals in another function */
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	sigact.sa_handler = time_handler;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = SA_RESTART;
	if (sigaction(SIGALRM, &sigact, NULL) < 0)
		dagutil_panic("sigaction SIGALRM: %s\n", strerror(errno));
	
	/* Set alarm signal every second */
	alarm(1);
	
#elif defined(_WIN32)

	/* Create a  timer. */
	if (SetTimer(NULL, 34, 1000, (TIMERPROC)time_handler)==0) 
		dagutil_panic("Set Timer failed (%d)\n", GetLastError());

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

	/* Record start time */
	gettimeofday(&uCounters.start, 0);

	/* Every uContinue type uses a diferent API interface.
	 * This is only for educational and testing purposes.
	 * You really only need one of these two interfaces.
	 */
	switch (uConfiguration.run_type)
	{
		case 1:
			if (!mmap_failed)
			{
				run_file_commit_bytes(&file,memcpy_size,number);
			}
			else
				commit_bytes();
			break;
			
		case 2:
			if (!mmap_failed)
				run_file_copy_bytes(&file);
			else
				copy_bytes();
			break;
		
		default:
			dagutil_panic("uContinue type not supported (%d)\n", uConfiguration.run_type);
			break;
	}

	/* Record end time */
	gettimeofday(&uCounters.end, 0);

	/* Report some statistics */
	report_statistics();

#if defined(_WIN32)
	/* Clean up the file structures used if running on Windows */
	if (!mmap_failed)
		remove_mapping(&file);
#endif /* WIN32 */

	/* Close dag session and device */
	finalize_dag();

	return EXIT_SUCCESS;
}


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