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

/* dagsnap headers. */
#include "dagsnap_model.h"
#include "dagsnap_view.h"

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


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


#define OPTBUFSIZE ONE_KIBI
#define BUFSIZE 256


typedef struct 
{
	DagsnapPtr dagsnap;
	DagsnapInfoPtr info;
	struct timeval last_timeval;
	char label[80];

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

} view_state_t , * ViewStatePtr;


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


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


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

static void*
report_thread(void* context)
{
	ViewStatePtr vstate = (ViewStatePtr) context;
	DagsnapPtr dagsnap = vstate->dagsnap;
	dagsnap_info_t info;
	
	memset(&info, 0, sizeof(info));
	vstate->info = &info;
	
	for (;;)
	{
		sleep(1);
		dagsnap_get_status(dagsnap, vstate->info);
		display_view(vstate);
	}
	
	return NULL;
}

#elif defined(_WIN32)

static DWORD WINAPI
ReportThread(LPVOID lpParameter)
{
	ViewStatePtr vstate = (ViewStatePtr) context;
	DagsnapPtr dagsnap = vstate->dagsnap;
	dagsnap_info_t info;
	
	memset(&info, 0, sizeof(info));
	vstate->info = &info;

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


static void
start_report_thread(ViewStatePtr vstate)
{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	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 */
}


static void
display_view(ViewStatePtr vstate)
{
	DagsnapInfoPtr info = vstate->info;
	struct timeval now;
	struct timeval tdiff;
	uint32_t odiff;
	double rate;

	gettimeofday(&now, NULL);
	timersub(&now, &vstate->last_timeval, &tdiff);
	odiff = (uint32_t) (info->file_offset - info->lastoff);
	/*
	 * Bytes/mebisecond == Mebibytes/second.
	 */
	if ((tdiff.tv_sec == 0) && (tdiff.tv_usec == 0))
	{
		rate = 0;
	}
	else
	{
		rate = ((double) odiff) / (((double) tdiff.tv_sec * ONE_MEBI) + tdiff.tv_usec);
	}
	
	dagutil_verbose("%s%10.3f MiBytes %8.3f MiBytes %8.3f MiBytes/sec (%d Mbps)\n", vstate->label,
		((double) info->file_offset) / ONE_MEBI, ((double) info->diff) / ONE_MEBI, rate, (unsigned int) (8 * rate));
	info->lastoff = info->file_offset;
	vstate->last_timeval = now;
}


/* Public routines. */
void
dagsnap_view_init(DagsnapPtr dagsnap_ref, const char* dagname, int verbosity)
{
	ViewStatePtr vstate = get_new_view_state(dagsnap_ref);

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