#include "loadmeter.h"

void checkgeom(), Meminfo();
static char *utime = NULL;
int omask = 65535;
int longest;
#ifndef LINUX
extern void usleep();
#endif


/* Popup the stats window */
void do_popup()
{
unsigned int wr, h, w;
static char *buf=NULL;
static char *lbuf=NULL;
int which, numfs = 0, ht, x, y;

	if(popup != 0)
		XDestroyWindow(disp, popup);
	if(utime == NULL)
		utime = (void *)malloc(64);
	if(buf == NULL)
		buf = (void *)malloc(64);
	if(lbuf == NULL)
		lbuf = (void *)malloc(64);
	GetUptime(utime);
	GetDiskUsage();
	GetMemInfo();
	sprintf(lbuf, "%.2f %.2f %.2f", load, avg, davg);
	longest = strlen(lbuf);
	pwidth = XTextWidth(pfont, lbuf, longest) + 5;
/* Find the width the window needs to be by finding the longest string */
	which = 0;
	while (usages[which].path != NULL && which < MAXFILESYSTEMS) {
		numfs++;
		sprintf(buf, "%s: %.1f%%", usages[which].path, 
				(float)(usages[which].blocks-
				usages[which].bfree)*100/usages[which].blocks);
		ht = strlen(buf);
		wr = XTextWidth(pfont, buf, ht) + 5;
		if(wr > pwidth)
			pwidth = wr;
		if(ht > longest)
			longest = ht;
		which++;
	}
	wr = XTextWidth(pfont, memory.suse, strlen(memory.suse)) + 5;
	if (wr > pwidth)
		pwidth = wr;
	wr = XTextWidth(pfont, memory.muse, strlen(memory.muse)) + 5;
	if (wr > pwidth)
		pwidth = wr;
	ht = strlen(utime);
	wr = XTextWidth(pfont, utime, ht) + 5;
	if (wr > pwidth)
		pwidth = wr;
	if(ht > longest)
		longest = ht;
	ht = pfont->max_bounds.ascent + pfont->max_bounds.descent;
	pheight = (numfs + 4) * ht + 9;
/* Obtain the window geometry WRT root */
	Getgeom(win, &x, &y, &w, &h);
	pw = pwidth;
	ph = pheight;
	y += h + 1;	
/* Don't let the window extend beyond the screen edges */
	checkgeom(&x, &y, &pwidth, &pheight);
	popup = MakePopup(x, y, pwidth, pheight, popbg.pixel, 0);

	XGrabPointer(disp, popup, True,  ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);

 	draw_popup(0);
}

void draw_popup(int highlight)
{
int yt, ln, ht, x, wr, which=0;
float pct;
static char buf[128], lbuf[64], bf[8];
static int hprev=0;

/* Get font/text geometry info */
	wr = XTextWidth(pfont, utime, strlen(utime)) + 5;
	yt = pfont->max_bounds.ascent+1;
	ht = pfont->max_bounds.ascent + pfont->max_bounds.descent;
/* Draw the uptime centered at the top */
	x = pwidth/2 - (wr-5)/2;
	XDrawString(disp, popup, ptextgc, x+1, yt, utime, strlen(utime));
	yt += pfont->max_bounds.descent;
/* 3 load averages */
	XClearArea(disp, popup, 1, yt, pwidth-2, yt, False);
	yt += pfont->max_bounds.ascent+1;
	sprintf(lbuf, "%.2f %.2f %.2f", load, avg, davg);
	wr = XTextWidth(pfont, lbuf, strlen(lbuf));
	x = pwidth/2 - (wr-5)/2;
	XDrawString(disp, popup, ptextgc, x+1, yt, lbuf, strlen(lbuf));
/* Separator line */
	yt += pfont->max_bounds.descent+1;
	XDrawLine(disp, popup, popbgc, 1, yt, pwidth-1, yt);
	XDrawLine(disp, popup, poptgc, 1, yt+1, pwidth-1, yt+1);
	yt+=2;
/* Draw filesystem usages */
	while (usages[which].path != NULL && which < MAXFILESYSTEMS) {
/* Determine percent full and create the string */
		pct = (float)(usages[which].blocks-usages[which].bfree)*100/usages[which].blocks;
		sprintf(bf, "%.1f%%", pct);
		for(ln=0;bf[ln] != '\0';ln++);
		ln += strlen(usages[which].path) + 2;
		for (wr=0;wr<longest-ln;wr++)
			lbuf[wr] = ' ';
		lbuf[wr] = '\0';
		sprintf(buf, "%s:%s%s", usages[which].path, lbuf, bf);
/* 2 cases for highlighting the particular filesystem */
		if(hprev == which+1 && menuhl)
			XFillRectangle(disp, popup, popgc, 1, yt, pwidth-2, ht);
		else if(hprev != 0 && highlight != which+1 && menuhl)
		{
			yt += pfont->max_bounds.ascent;
			yt += pfont->max_bounds.descent;
			which++;
			continue;
		}
		if(pct >= warnpct)
			XFillRectangle(disp, popup, warngc, 1, yt, pwidth-2, ht);
		if(highlight == which+1)
				dobevels(popup, 1, yt, pwidth-2, ht, poptgc, popbgc);
/* Actual string */
		yt += pfont->max_bounds.ascent;
		XDrawString(disp, popup, ptextgc, 4, yt, buf, strlen(buf));
		yt += pfont->max_bounds.descent;
		which++;
	}
	if(hprev < which && highlight <= which && highlight != 0)
	{
		hprev = highlight;
		return;
	}
/* Draw memory stats..*/
	XDrawLine(disp, popup, popbgc, 0, yt, pwidth, yt);
	XDrawLine(disp, popup, poptgc, 0, yt+1, pwidth, yt+1);
	yt += ht+1;
	XDrawString(disp, popup, ptextgc, 4, yt, memory.muse, strlen(memory.muse));
	yt += ht;
	XDrawString(disp, popup, ptextgc, 4, yt, memory.suse, strlen(memory.suse));
/* And finally bevels.. */
	dobevels(popup, 0, 0, pwidth, pheight, poptgc, popbgc);
	hprev = highlight;
	XSync(disp, False);
}

/* Pop down the stats window */
void do_popdown()
{
int h;
	if (popup == 0)
		return;
	XUngrabPointer(disp, CurrentTime);
	if(info != 0)
		XDestroyWindow(disp, info);
	info = 0;
	/* Make it interesting by shrinking the window gradually :) */
	for(h = ph ; h > 0 ; h -= 8)
	{
		if (!shrinkpopup) {
			break;
		}
		XResizeWindow(disp, popup, pw, h);
		XSync(disp, False);
		usleep(5000);
	}
	XDestroyWindow(disp, popup);
	popup = 0;
}

/* Create, map and return a popup window with a specified background.
   Override-redirect is True, to bypass the window manager. Saveunders
   are enabled too.
 */
Window MakePopup(int x, int y, unsigned int w, unsigned int h, unsigned int background, int bw)
{
unsigned long valuemask;
XSetWindowAttributes attributes;
Window win;

	valuemask = CWBackingStore | CWSaveUnder | CWOverrideRedirect | CWBackPixel;
	attributes.save_under = True;
	attributes.override_redirect = True;
	attributes.background_pixel = background;
	attributes.backing_store = WhenMapped;
	win = XCreateWindow(disp, root, x, y, w, h, bw,
				CopyFromParent, CopyFromParent, CopyFromParent,
				valuemask, &attributes);
	XMapWindow(disp, win);
	return win;
}

/* Display detailed stats about a particular filesystem */
void ShowInfo(int which)
{
int mask, wd;
unsigned int width, height, x, y;
int xr, yr, wr, hr, ow, h;
float pct;
char buf[8][50];

/* Find which filesystem we're showing */
	for(mask = 0; usages[mask].path != NULL && mask < MAXFILESYSTEMS; mask++)
		if(which == mask+1)
			break;
/* Return if no FS matches */
	if(usages[mask].path == NULL)
		if(which < mask || which < 1)
		{
			infodown();
			return;
		} else {
			Meminfo(mask+1);
			return;
		}
	infodown();
/* Window height */
	height = (pfont->max_bounds.ascent + pfont->max_bounds.descent)*9 + 7;
/* Get filesystem capacity percentage */
	pct = (float)(usages[mask].blocks - usages[mask].bfree)*100/usages[mask].blocks;
/* Make the strings that we'll display */
	sprintf(buf[0], "Filesystem info for");
	sprintf(buf[2], "Device: %s", usages[mask].dev);
	sprintf(buf[3], "Type:   %s", usages[mask].type);
	sprintf(buf[1], "%s", usages[mask].path);
	sprintf(buf[4], "Total:  %s Kb", commanum(usages[mask].blocks));
	sprintf(buf[5], "Used:   %s Kb", commanum(usages[mask].blocks - usages[mask].bfree));
	sprintf(buf[6], "Free:   %s Kb", commanum(usages[mask].bfree));
	sprintf(buf[7], "Capacity: %.1f%%", pct);

/* Longest string will determine window width */
	ow = width = 0;
	for(mask = 0; mask < 7 ; mask++)
	{
		wd = strlen(buf[mask]);
		if(wd > ow)
		{
			width = XTextWidth(pfont, buf[mask], wd) + 5;
			ow = wd;
		}
	}
/* Obtain geometry of 'popup' to determine positioning */
	Getgeom(popup, &xr, &yr, &wr, &hr);
	x = xr + wr - 10;
	y = yr + (which+1) * (pfont->max_bounds.ascent + pfont->max_bounds.descent)+4;
/* Generate the window */
	checkgeom(&x, &y, &width, &height);
	info = MakePopup(x, y, width, height, popbg.pixel, 0);
/* Draw first couple of lines of text, and a separator */
	y = pfont->max_bounds.ascent + 1;
	wd = width/2 - XTextWidth(pfont, buf[0], strlen(buf[0])) / 2;
	XDrawString(disp, info, ptextgc, wd+1, y, buf[0], strlen(buf[0]));
	y += pfont->max_bounds.descent+pfont->max_bounds.ascent+1;
	wd = width/2 - XTextWidth(pfont, buf[1], strlen(buf[1])) / 2;
	XDrawString(disp, info, ptextgc, wd+1, y, buf[1], strlen(buf[1]));
	y += pfont->max_bounds.descent + 1;
	XDrawLine(disp, info, popbgc, 0, y, width, y);
	XDrawLine(disp, info, poptgc, 0, y+1, width, y+1);
	y += pfont->max_bounds.ascent + 2;
/* Draw the rest of the text stuff */
	for(mask = 2 ; mask < 8 ; mask++)
	{
		XDrawString(disp, info, ptextgc, 3, y, buf[mask],
			strlen(buf[mask]));
		y += pfont->max_bounds.ascent + pfont->max_bounds.descent;
	}
/* Draw a graphical representation of disk usage, with 'E' and 'F' marks */
	y++;
	wd = width/2 - (3*width/4)/2;
	h = pfont->max_bounds.ascent + pfont->max_bounds.descent-1;
	ow = 3*width/4;
	wr = XTextWidth(pfont, "E", 1);
/* 'E' Mark */
	XDrawString(disp, info, ptextgc, wd - wr - 1, y, "E", 1);
	dobevels(info, wd, y-pfont->max_bounds.ascent, ow, h, popbgc, poptgc);
/* Inner grey rectangle */
	XFillRectangle(disp, info, metergc, wd+1, y+1-pfont->max_bounds.ascent,ow*pct/100, h-2);
/* 'F' Mark */
	XDrawString(disp, info, ptextgc, wd + ow +2, y, "F", 1);
	dobevels(info, 0, 0, width, height, poptgc, popbgc);
	XSync(disp, False);
}

/* Pop down the info window if it exists */
void infodown()
{
	if(info ==0)
		return;
	XDestroyWindow(disp, info);
	omask = 65535;
	info = 0;
}

/* Make sure a given geometry does not extend beyond the screen
 * as follows: If xpos + width > screen_width, then xpos =
 * screen_width - width. Same for y ordinate;
 */
void checkgeom(int *x, int *y, int *w, int *h)
{

	if (*x + *w > rwidth)
		*x = rwidth - *w;
	if (*y + *h > rheight)
		*y = rheight - *h;
}

/* Generate and display the memory info sub-window */
void Meminfo(int which)
{
int x, y, height, width;
/* Array of 7x25-byte buffers */
char buf[7][25];
int ow, mask, wd;
int xr, yr, wr, hr;
#ifdef LINUX
#define MEMLINES 7
#else
#define MEMLINES 5
#endif

	if(omask == which+1 && info != 0)
		return;
	else
		infodown();
	height = (pfont->max_bounds.ascent + pfont->max_bounds.descent)*MEMLINES + 6;
	sprintf(buf[0], "Memory info");
	sprintf(buf[1], "Mem Total:  %s Kb", commanum(memory.memtotal));
	sprintf(buf[2], "Mem Free:   %s Kb", commanum(memory.memfree));
#ifdef LINUX
	sprintf(buf[3], "Mem Shared: %s Kb", commanum(memory.shared));
	sprintf(buf[4], "Cached:     %s Kb", commanum(memory.cached));
#else
	buf[3][0] = buf[4][0] = 0;
#endif
	sprintf(buf[5], "Swap Total: %s Kb", commanum(memory.swaptotal));
	sprintf(buf[6], "Swap Free:  %s Kb", commanum(memory.swapfree));
/* Get window width */
	ow = width = 0;
	for(mask = 0; mask < 6 ; mask++)
	{
		wd = strlen(buf[mask]);
		if(wd > ow)
		{
			width = XTextWidth(pfont, buf[mask], wd) + 5;
			ow = wd;
		}
	}
/* Obtain geometry of 'popup' to determine positioning */
	Getgeom(popup, &xr, &yr, &wr, &hr);
	x = xr + wr - 10;
	y = yr + (which +1) * (pfont->max_bounds.ascent + pfont->max_bounds.descent)+5;
/* Generate the window */
	checkgeom(&x, &y, &width, &height);
	info = MakePopup(x, y, width, height, popbg.pixel, 0);
/* Draw first couple of lines of text, and a separator */
	y = pfont->max_bounds.ascent + 1;
	wd = width/2 - XTextWidth(pfont, buf[0], strlen(buf[0])) / 2;
	XDrawString(disp, info, ptextgc, wd+1, y, buf[0], strlen(buf[0]));
	y += pfont->max_bounds.descent+pfont->max_bounds.ascent+1;
	wd = width/2 - XTextWidth(pfont, buf[1], strlen(buf[1])) / 2;
	XDrawString(disp, info, ptextgc, wd+1, y, buf[1], strlen(buf[1]));
	y += pfont->max_bounds.descent + 1;
	XDrawLine(disp, info, popbgc, 0, y, width, y);
	XDrawLine(disp, info, poptgc, 0, y+1, width, y+1);
	y += pfont->max_bounds.ascent + 2;
/* Draw the rest of the text stuff */
	for(mask = 2 ; mask < 8 ; mask++)
	{
		if (buf[mask][0] == 0)
			continue;
		XDrawString(disp, info, ptextgc, 3, y, buf[mask],
			strlen(buf[mask]));
		y += pfont->max_bounds.ascent + pfont->max_bounds.descent;
	}
	XSync(disp, False);
	dobevels(info, 0, 0, width, height, poptgc, popbgc);
	omask = which+1;
}
