/*==============================================================================

	M A I N . C

	This is the main module for the 'wmnet' project.This code was
	originally based on the 'wmifs' project done by Martijn Pieterse
	(pieterse @ xs4all.nl).I was going to originally port his code, but
	since most of the guts was Linux - specific, I really only ended up
	using the X windows code.

	main.c,v 1.15 1999/01/30 05:32:23 rneswold Exp
==============================================================================*/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/xpm.h>

#include "wmnet.h"
#include "wmgeneral.h"
#include "wmnet-master.xpm"
#include "wmnet-mask.xbm"

#define LED_NET_1			(1)
#define LED_NET_2			(2)
#define LED_NET_3			(3)

#define LED_SZE_X (4)
#define LED_SZE_Y (4)

#define LED_ON_NET_X (87)
#define LED_ON_NET_Y (66)
#define LED_OFF_NET_X (93)
#define LED_OFF_NET_Y (66)
#define LED_ERR_NET_X (81)
#define LED_ERR_NET_Y (66)

#define LED_PWR_X (53)
#define LED_PWR_Y (7)
#define LED_SND_X (47)
#define LED_SND_Y (7)
#define LED_RCV_X (41)
#define LED_RCV_Y (7)

/* Function prototypes...  */

static void DrawActiveIFS(char const *);
static void DrawStats(unsigned, int, int, int);
static unsigned findInterface(char const*);
static void printversion(void);
static char const *rateToStr(unsigned long);
static void SetErrLED(int);
static void SetOffLED(int);
static void SetOnLED(int);
static void usage(void);
static void wmnet_routine(int, char **);

/* Local variables...  */

static char* active_interface = NULL;
static int delay = 1;

/*------------------------------------------------------------------------------
  DrawActiveIFS
------------------------------------------------------------------------------*/
static void DrawActiveIFS(char const* name)
{
    copyXPMArea(5, 84, 30, 10, 5, 5);

	if (name) {
		int ii, kk = 5;

		for (ii = 0; name[ii]; ++ii) {
			int const ch = toupper(name[ii]);

			if (ch >= 'A' && ch <= 'Z')
				copyXPMArea((ch - 'A') * 6, 74, 6, 9, kk, 5);
			else
				copyXPMArea((ch - '0') * 6, 64, 6, 9, kk, 5);
			kk += 6;
		}
	}
}

/*------------------------------------------------------------------------------
  DrawStats
------------------------------------------------------------------------------*/
static void DrawStats(unsigned ifCurr, int height, int x_left, int y_bottom)
{
    unsigned baseLine = y_bottom - height / 2;
    unsigned maxVal = 0, ii;
    char const* ptr = 0;

    /* Find the maximum value. We'll scale the graph to this value. */

	for (ii = 0; ii < G_WIDTH; ++ii) {
		unsigned long rcv, xmt;

		ifGetData(ifCurr, ii, &xmt, &rcv);

		xmt /= delay;
		rcv /= delay;

		if (rcv > maxVal)
			maxVal = rcv;
		if (xmt > maxVal)
			maxVal = xmt;
    }
    ptr = rateToStr(maxVal);

    /* Now draw the data points. */

	for (ii = 0; ii < G_WIDTH; ++ii) {
		unsigned long rcv, xmt;
		unsigned long start = baseLine, stop = baseLine;
		int jj;

		if (maxVal > 0) {
			ifGetData(ifCurr, ii, &xmt, &rcv);
			xmt /= delay;
			rcv /= delay;
			start = baseLine + (xmt * height) / (2 * maxVal);
			stop = baseLine - (rcv * height) / (2 * maxVal);
		}
		for (jj = 0; jj < height; ++jj)
			if (y_bottom - jj <= start && y_bottom - jj >= stop)
				copyXPMArea(100 + 1, 68, 1, 1, ii + x_left, y_bottom - jj);
			else
				copyXPMArea(100, 68, 1, 1, ii + x_left, y_bottom - jj);

		/* Draw the base line. */

		copyXPMArea(100 + 3, 68, 1, 1, ii + x_left, baseLine);
	}

    /* Now draw the data rate... */

	for (ii = 5; *ptr; ++ptr)
		if (isdigit(*ptr)) {
			copyXPMArea((*ptr - '0') * 6, 64, 6, 9, 1 + ii, y_bottom - 10);
			ii += 6;
		} else if ('.' == *ptr) {
			copyXPMArea(60, 64, 4, 9, 1 + ii, y_bottom - 10);
			ii += 4;
		} else {
			copyXPMArea((toupper(*ptr) - 'A') * 6, 74, 6, 9, 1 + ii,
						y_bottom - 10);
			ii += 6;
		}

    /* Finally, update the LEDs. */

	if (ifIsUp(ifCurr))
		SetOnLED(LED_NET_1);
	else
		SetOffLED(LED_NET_1);

    if (ifIsRunning(ifCurr))
		SetOnLED(LED_NET_2);
    else
		SetOffLED(LED_NET_2);

    if (ifIsPromisc(ifCurr))
		SetErrLED(LED_NET_3);
    else
		SetOffLED(LED_NET_3);
}

/*------------------------------------------------------------------------------
	findInterface
	
	This function attempt to find the indicated interface. It returns the
	index of the interface, or zero if the interface isn't found.
------------------------------------------------------------------------------*/
static unsigned findInterface(char const* name)
{
	unsigned ii;

	/* Since strcasecmp doesn't like NULL pointers, make sure we don't
       pass one to it. */

	if (name)
		for (ii = 0; ii < ifTotal(); ++ii)
			if (!strcasecmp(name, ifName(ii)))
				return ii;
	return 0;
}

/*------------------------------------------------------------------------------
  printversion
------------------------------------------------------------------------------*/
static void printversion(void)
{
    fprintf(stderr, "wmnet, v1p2\n");
}

/*------------------------------------------------------------------------------
  rateToStr

  Converts a data rate into its string representation.The string is
  saved in a static buffer which is overwritten for each call.
------------------------------------------------------------------------------*/
static char const* rateToStr(unsigned long rate)
{
	static char buffer[7];
	char scaleFactor, *ptr = buffer;

	/* Based upon its magnitude, determine how much 'rate' needs to be
	   scaled and also indicate its unit of scale. */

	if (rate > 1000000) {
		rate /= 100000;
		scaleFactor = 'M';
	} else if (rate > 1000) {
		rate /= 100;
		scaleFactor = 'K';
	} else {
		rate *= 10;
		scaleFactor = '\0';
	}

	/* Transform the value into a left - justified string. */

	if (rate >= 1000)
		*ptr++ = rate / 1000 + '0';
	if (rate >= 100)
		*ptr++ = (rate / 100) % 10 + '0';
	if (rate >= 10)
		*ptr++ = (rate / 10) % 10 + '0';
	if (scaleFactor) {
		*ptr++ = '.';
		*ptr++ = rate % 10 + '0';
	}
	*ptr++ = scaleFactor;
	*ptr = '\0';
	return buffer;
}

/*------------------------------------------------------------------------------
  SetErrLED
------------------------------------------------------------------------------*/
static void SetErrLED(int led)
{
    if (LED_NET_3 == led)
		copyXPMArea(LED_ERR_NET_X, LED_ERR_NET_Y, LED_SZE_X, LED_SZE_Y,
					LED_PWR_X, LED_PWR_Y);
}

/*------------------------------------------------------------------------------
  SetOffLED
------------------------------------------------------------------------------*/
static void SetOffLED(int led)
{
	int x, y;

    switch (led) {
	 case LED_NET_1:
		x = LED_RCV_X;
		y = LED_RCV_Y;
		break;

	 case LED_NET_2:
		x = LED_SND_X;
		y = LED_SND_Y;
		break;

	 case LED_NET_3:
		x = LED_PWR_X;
		y = LED_PWR_Y;
		break;

	 default:
		return;
    }
	copyXPMArea(LED_OFF_NET_X, LED_OFF_NET_Y, LED_SZE_X, LED_SZE_Y, x, y);
}

/*------------------------------------------------------------------------------
  SetOnLED
------------------------------------------------------------------------------*/
static void SetOnLED(int led)
{
	int x, y;

    switch (led) {
	 case LED_NET_1:
		x = LED_RCV_X;
		y = LED_RCV_Y;
		break;

	 case LED_NET_2:
		x = LED_SND_X;
		y = LED_SND_Y;
		break;

	 case LED_NET_3:
		x = LED_PWR_X;
		y = LED_PWR_Y;
		break;

	 default:
		return;
    }
	copyXPMArea(LED_ON_NET_X, LED_ON_NET_Y, LED_SZE_X, LED_SZE_Y, x, y);
}

/*------------------------------------------------------------------------------
  usage
------------------------------------------------------------------------------*/
static void usage(void)
{
    static char const txt[] =
		"\nwmnet, v1p2 - A network interface monitor for WindowMaker.\n\n"
		"\t-h         this help screen\n"
		"\t-i name    start with interface 'name'\n"
		"\t-u delay   seconds between samples (defaults to 1)\n"
		"\t-v         print the version number\n";

    fputs(txt, stderr);
}

/*------------------------------------------------------------------------------
  wmnet_routine
------------------------------------------------------------------------------*/
static void wmnet_routine(int argc, char **argv)
{
    unsigned ifCurr = findInterface(active_interface);
    int i;
    XEvent Event;
    int but_stat = -1;

	/* Build up our X connection. */

    openXwindow(argc, argv, wmnet_master_xpm, wmnet_mask_bits,
				wmnet_mask_width, wmnet_mask_height);

    /* > Button */

    AddMouseRegion(0, 5, 5, 35, 15);
    AddMouseRegion(1, 5, 20, 58, 58);

    DrawActiveIFS(ifName(ifCurr));
    DrawStats(ifCurr, 40, 5, 58);
    RedrawWindow();

    while (1) {
		static int count = 0;

		/* After 1 second (10 * 100 milliseconds), take new samples and
		   redraw the graph. */

		if (++count == 10 * delay) {
			ifSample();
			DrawStats(ifCurr, 40, 5, 58);
			RedrawWindow();
			count = 0;
		}
		while (XPending(display)) {
			XNextEvent(display, &Event);
			switch (Event.type) {
			case Expose:
				RedrawWindow();
				break;

			case DestroyNotify:
				XCloseDisplay(display);
				exit(0);
				break;

			case ButtonPress:
				but_stat = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
				break;

			case ButtonRelease:
				i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
				if (but_stat == i && !but_stat) {
					ifCurr = (ifCurr + 1) % ifTotal();
					DrawActiveIFS(ifName(ifCurr));
					DrawStats(ifCurr, 40, 5, 58);
				}
				but_stat = -1;
				RedrawWindow();
				break;
			}
		}
		usleep(100000L);
    }
}

/*------------------------------------------------------------------------------
  main
------------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
    int ii;

    for (ii = 1; ii < argc; ++ii) {
		char const* const arg = argv[ii];

		if (*arg == '-') {
			switch (arg[1]) {
			case 'i':
				if (ii + 1 < argc)
					active_interface = argv[++ii];
				else {
					printf("wmnet: must specify the interface\n");
					return 0;
				}
				break;

			case 'u':
				if (ii + 1 < argc) {
					delay = atoi(argv[++ii]);
					if (delay < 1)
						delay = 1;
				} else {
					printf("wmnet: must specify a delay\n");
					return 0;
				}
				break;

			case 'v':
				printversion();
				return 0;

			default:
				usage();
				return 0;
			}
		}
    }
    (void) ifInit();
    wmnet_routine(argc, argv);
    return 0;
}
