#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include <stdio.h>
#include <string.h>

#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_mib.h>
#endif

#include "bungmeter.h"
#include "callbacks.h"
#include "interface.h"
#include "support.h"


/* peaks bytes during a timeslice */
gint max_in;
gint max_out;

/* average bytes */
gint avg_in[AVG_RATE];
gint avg_out[AVG_RATE];
short int avg_pos = 0;

/* total bytes */
long unsigned ttl_in = 0;
long unsigned ttl_out = 0;

/* bytes this timeslice */
long unsigned bytes_in;
long unsigned bytes_out;

/* transfer history, for the actual graph */
gint graph_in[AREAW] = {0};
gint graph_out[AREAW] = {0};

gint nowgra=0;
gint big=1;
gint timer=0;
char *i;

gboolean thetimer(gpointer data) {
	if (nowgra < AREAW - 1) {
		nowgra++;
	} else {
		nowgra=0;
	}

	updatevalue();
	flash(data);

	return TRUE;
}

void updatevalue() {
	int n;
	unsigned long stack_in_old, stack_in_new, stack_out_old, stack_out_new;

	stack_in_old = bytes_in;
	stack_out_old = bytes_out;
	getvalue(interface, &bytes_in, &bytes_out);
	stack_in_new = bytes_in;
	stack_out_new = bytes_out;
	graph_in[nowgra] = stack_in_new - stack_in_old;
	graph_out[nowgra] = stack_out_new - stack_out_old;

	/* Keep track of max. peaks */ 
	if (max_in < graph_in[nowgra]) max_in = graph_in[nowgra];
	if (max_out < graph_out[nowgra]) max_out = graph_out[nowgra];

	/* Keep track of average speed */
	avg_in[avg_pos] = graph_in[nowgra];
	avg_out[avg_pos] = graph_out[nowgra];
	avg_pos++;
	if(avg_pos >= AVG_RATE) avg_pos=0;

	/* keep track of totals */
	ttl_in += graph_in[nowgra];
	ttl_out += graph_out[nowgra];

	big = 1; 
	for (n=0; n < AREAW; n++) {
		if (graph_in[n] > big) big = graph_in[n];
		if (graph_out[n] > big) big = graph_out[n];
	}
}

#ifdef __linux__
int getvalue(char *interface, long unsigned *inb, long unsigned *outb) {
	int static opened_proc_file = 0;
	char buffer[BUFSIZE];
	char *ptr;
	char *devname;
	int dump;
	short int foundinterface = 0;
	
	if(!opened_proc_file) {
		proc_net_dev = fopen(PATH_NET_DEV, "r");
		if (proc_net_dev == 0) {
			printf("Unable to open proc file.\n");
			return 0;
		}
		opened_proc_file = 1;
	}
    /* do not parse the first two lines as they only contain static garbage */
	fseek(proc_net_dev, 0, SEEK_SET);
	fgets(buffer, 256, proc_net_dev);
	fgets(buffer, 256, proc_net_dev);

	while (fgets(buffer, 256, proc_net_dev) != NULL) {
		/* find the device name and substitute ':' with '\0' */
		ptr = buffer;
		while (*ptr == ' ') {
			ptr++;
		}
		devname = ptr;
		while (*ptr != ':') {
			ptr++;
		}
		*ptr = '\0';
		ptr++;
		if (!strcmp(devname, interface)) {
			/* read stats and fill struct */
			sscanf(ptr, "%lu %d %d %d %d %d %d %d %lu %d %d %d %d %d %d %d",
				inb, &dump, &dump, &dump, &dump, &dump, &dump, &dump,
				outb, &dump, &dump, &dump, &dump, &dump, &dump, &dump);
			foundinterface = 1;
			continue; /* break, as we won't get any new information */
		}
	}

	return foundinterface;
}
#elif __FreeBSD__
int getvalue(char *interface, unsigned long *inb, unsigned long *outb) {
	size_t len;
	int name[6], num_iface, i;
    struct ifmibdata ifmd;

	len = sizeof(num_iface);
    sysctlbyname("net.link.generic.system.ifcount", &num_iface, &len, NULL, 0);
	for (i=1; i <= num_iface; i++) {
		name[0] = CTL_NET;
		name[1] = PF_LINK;
		name[2] = NETLINK_GENERIC;
		name[3] = IFMIB_IFDATA;
		name[4] = i;
		name[5] = IFDATA_GENERAL;

		len = sizeof(ifmd);
		sysctl(name, 6, &ifmd, &len, NULL, 0);
		if (strcmp(ifmd.ifmd_name, (char *)interface) == 0) {
			len = sizeof(ifmd);
	        sysctl(name, 6, &ifmd, &len, NULL, 0);
			*inb = ifmd.ifmd_data.ifi_ibytes;
			*outb = ifmd.ifmd_data.ifi_obytes;
			return 1;
		}
	}
	return 0;
}
#else
int getvalue(char *interface, unsigned long *inb, unsigned long *outb) {
	printf("Invalid operating system detected.\n");
	*inb = 0;
	*outb = 0;
	return 1;
}
#endif

gboolean flash (GtkWidget  *drawingarea) {
	int n,p;
	float avg;
	char s[50];
	char slabel[100];  

	GdkDrawable *drawable;
	GdkGC *gc;
	GdkColor color0={0,0x0000,0x0000,0x0000};
	GdkColor color1={0,0xffff,0x5555,0x5555};
	GdkColor color2={0,0x5555,0xffff,0x5555};

	drawable = drawingarea -> window;

	gdk_colormap_alloc_color (gdk_colormap_get_system (), &color0, FALSE, TRUE);
	gc=gdk_gc_new(drawable);
	gdk_gc_set_foreground(gc,&color0);
	gdk_draw_rectangle(drawable, gc, TRUE, 0, 0, AREAW, AREAH);

	gdk_colormap_alloc_color (gdk_colormap_get_system (), &color2, FALSE, TRUE);
	gc=gdk_gc_new(drawable);
	gdk_gc_set_foreground(gc,&color2);
	for (n=1; n < 4; n++)
		gdk_draw_line (drawable, gc, 0, AREAH*n/4 , AREAW, AREAH*n/4); 
	for (n=1; n < 6; n++) 
		gdk_draw_line (drawable, gc, AREAW*n/6, 0, AREAW*n/6, AREAH); 

	gdk_colormap_alloc_color (gdk_colormap_get_system (), &color1, FALSE, TRUE);
	gc=gdk_gc_new(drawable);
	gdk_gc_set_foreground(gc,&color1);

	for (n=0; n < AREAW; n++) {
		p = nowgra - n;
		if (p < 0) p = p + AREAW;  
		gdk_draw_line (drawable, gc, n, AREAH, n, AREAH - graph_in[p] * AREAH / big );
		gdk_draw_line (drawable, drawingarea->style->white_gc, n, AREAH, n,
				AREAH - graph_out[p] * AREAH / big );  
	}

	/* Current average in speed */
	strcpy(slabel,"In: ");
	avg = 0;
	for(n=0; n < AVG_RATE; n++) {
		avg += avg_in[n];
	}
	avg /= AVG_RATE;
	bits2bytes(avg, s);
	strcat(slabel, s);

	/* Total transfer in */
	strcat(slabel, "/s (");
	bits2bytes(ttl_in,s);
	strcat(slabel, s);
	strcat(slabel, " total)\n");

	/* Current average out speed */
	strcat(slabel, "Out: ");
	avg = 0;
	for(n=0; n < AVG_RATE; n++) {
		avg += avg_out[n];
	}
	avg /= AVG_RATE;
	bits2bytes(avg, s);
	strcat(slabel, s);

	/* Total transfer out */
	strcat(slabel, "/s (");
	bits2bytes(ttl_out,s);
	strcat(slabel, s);
	strcat(slabel, " total)");

	gtk_label_set_text(GTK_LABEL(lookup_widget(GTK_WIDGET (drawingarea), "label2")), slabel);
	gdk_gc_unref (gc);  

	return TRUE;
}

void bits2bytes (float f, char * s) {
	char suffix[3];
	
#ifdef DISPLAY_BITS
	f = f * 8;
#endif

	if (f < 1e6) {
		f = f / 1e3;
		strcpy(suffix, "KiB");
	} else if (f < 1e9) {
		f = f / 1e6;
		strcpy(suffix, "MiB");
	} else if (f < 1e12) {
		f = f / 1e9;
		strcpy(suffix, "GiB");
	} else {
		f = f / 1e12;
		strcpy(suffix, "TiB");
	}
	sprintf(s, "%.2f%s", f, suffix);
	return;
}

gboolean on_drawingarea1_expose_event  (GtkWidget       *widget,
                                        GdkEventExpose  *event,
                                        gpointer         user_data) {
	if(!timer) {
		timer_init(widget);
	}
 
	flash(lookup_widget(GTK_WIDGET (widget), "drawingarea1"));
	return FALSE;
}


void on_window1_destroy (GtkObject *object, gpointer user_data) {
	#ifdef __linux__
		fclose(proc_net_dev);
	#endif
	gtk_main_quit();
}

void timer_init(GtkWidget *widget) {
	char title[20];

	strcpy(title, "bungmeter (");
    strcat(title, interface);
   	strcat(title, ")");
	gtk_window_set_title (GTK_WINDOW (lookup_widget(GTK_WIDGET (widget), "window1")), title);


	getvalue(interface, &bytes_in, &bytes_out);
	timer = gtk_timeout_add(1000, thetimer, lookup_widget(GTK_WIDGET (widget), "drawingarea1"));
}
 
