/*
 * Copyright (c) 2006-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_view.c 4038 2006-04-03 05:14:25Z koryn $
 */

/* dagflood headers. */
#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_view.c 4038 2006-04-03 05:14:25Z koryn $";
static const char* const kRevisionString = "$Revision: 4038 $";


typedef struct 
{
	DagfloodPtr flooder;
	DagfloodInfoPtr info;
	struct timeval last_timeval;
	uint64_t previous_bytes_written;
	uint32_t seconds;
	char label[80];

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)
	pthread_t report_thread;
#endif

} view_state_t , * ViewStatePtr;


/* Internal routines. */
static void display_view(ViewStatePtr vstate);
static void start_report_thread(ViewStatePtr vstate);
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)
static void* report_thread(void* context);
#elif defined(_WIN32)
static DWORD WINAPI ReportThread(LPVOID lpParameter);
#endif /* _WIN32 */


/* Implementation of internal routines. */
static ViewStatePtr
get_new_view_state(DagfloodPtr flooder)
{
	ViewStatePtr result = (ViewStatePtr) dagutil_malloc(sizeof(*result));
	
	if (NULL == result)
	{
		return NULL;
	}
	
	memset(result, 0, sizeof(*result));
	result->flooder = flooder;
	
	return result;
}


static void
display_view(ViewStatePtr vstate)
{
	/* Increment number of seconds the application has been running. */
	vstate->seconds++;

	/* Report some statistics. */
	if (dagutil_get_verbosity() > 0)
	{
		uint64_t bytes_written = vstate->info->bytes_written;
		uint64_t difference = bytes_written - vstate->previous_bytes_written;
		
		fprintf (stderr, "Rate: %4.2ld Mbps (%4.2ld MiB/s)\r",
			(long int) (difference * 8 / 1000000),
			(long int) (difference / ONE_MEBI));

		/* Update counters. */
		vstate->previous_bytes_written = bytes_written;
	}
}


static void
start_report_thread(ViewStatePtr vstate)
{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)
	int result = pthread_create(&vstate->report_thread, NULL, report_thread, (void*) vstate);
	
	if (0 != result)
	{
		dagutil_panic("unable to create the report thread: %s\n", strerror(result));
	}

	result = pthread_detach(vstate->report_thread);
	if (0 != result)
	{
		dagutil_panic("unable to detach the report thread: %s\n", strerror(result));
	}

#elif defined(_WIN32)
	if (CreateThread(NULL, 0, ReportThread, (void*) vstate, 0, NULL) == NULL)
	{
		dagutil_panic("unable to start the report thread\n");
	}
#endif /* _WIN32 */
}


#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)

static void*
report_thread(void* context)
{
	ViewStatePtr vstate = (ViewStatePtr) context;
	DagfloodPtr flooder = vstate->flooder;
	dagflood_info_t info;
	
	memset(&info, 0, sizeof(info));
	vstate->info = &info;
	
	for (;;)
	{
		sleep(1);
		dagflood_get_status(flooder, vstate->info);
		display_view(vstate);
	}
	
	return NULL;
}

#elif defined(_WIN32)

static DWORD WINAPI
ReportThread(LPVOID lpParameter)
{
	ViewStatePtr vstate = (ViewStatePtr) context;
	DagfloodPtr flooder = vstate->flooder;
	dagflood_info_t info;
	
	memset(&info, 0, sizeof(info));
	vstate->info = &info;

	for (;;)
	{
		Sleep(1000);
		dagflood_get_status(flooder, vstate->info);
		display_view(vstate);
	}
	
	return 0;
}
#endif /* _WIN32 */


/* Public routines. */
void
dagflood_view_init(DagfloodPtr flooder, const char* dagname, int verbosity)
{
	ViewStatePtr vstate = get_new_view_state(flooder);

	gettimeofday(&vstate->last_timeval, NULL);
	if (verbosity > 1)
	{
		snprintf(vstate->label, sizeof(vstate->label), "%s\t", dagname);
	}
	start_report_thread(vstate);
}
