#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/utsname.h>

#include <sys/wait.h>
#include <sys/param.h>
#include <sys/types.h>

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

#include "../wmgeneral/wmgeneral.h"
#include "../wmgeneral/misc.h"

#include "asmon-master.xpm"
#include "asmon-mask.xbm"

#define EXEC_ON_CLICK 1
#define ASMON_VERSION "0.51"
#define CHAR_WIDTH 5
#define CHAR_HEIGHT 7

#define BCHAR_WIDTH 6
#define BCHAR_HEIGHT 9

#define BOFFX   (9)
#define BOFFY   (111)
#define BREDX   (3)
#define BREDY   (111)
#define BGREENX (87)
#define BGREENY (66)

#define LITEW   (4)
#define LITEH   (4)

#define B_OFF   (0)
#define B_RED   (1)
#define B_GREEN (2)

/* Evil globals I haven't removed yet */
long last_pageins=0, last_pageouts=0;
long last_swapins=0, last_swapouts=0;

#ifdef EXEC_ON_CLICK
char Command[256]="";
#endif

/* functions */
void DrawUptime(void);
void usage(void);
void printversion(void);
void asmon_routine(int Xpid, int allmem);
void DrawLite(int state, int dx, int dy);
void DrawCPU(void);
void DrawLoad(void);
void DrawXmem(int Xpid, float total);
float DrawMemSwap(float total, int allmem);

int main(int argc, char *argv[]) 
{
	FILE *fp;
	int i;
	int allmem=1;
	int Xpid=1;
	char *ProgName;

	ProgName = argv[0];

	if (strlen(ProgName) >= 5)
		ProgName += (strlen(ProgName) - 5);

	for (i=1; i<argc; i++) {
		char *arg = argv[i];
		if (*arg=='-') {
			switch (arg[1]) {
			case 'd' :
				if (strcmp(arg+1, "display")) {
					usage();
					exit(1);
				}
				break;
#ifdef EXEC_ON_CLICK
			case 'e' :
				if ( argv[++i] ) {
					strncpy( Command, argv[i], 253);
					strcat( Command, " &"); 
				} else {
					usage();
					exit(0);
				}
				break;
#endif
			case 'v' :
				printversion();
				exit(0);
				break;
			case 'u' :
				Xpid=0;
				break;
			case 'm' :
				allmem=0;
				break;
			default:
				usage();
				exit(0);
				break;
			}
		}
	}
	if ( Xpid != 0) {
	    if ( (fp = fopen("/var/run/server.0.pid", "r")) != NULL)
	    {
    		fscanf(fp, " %d", &Xpid);
    		fclose(fp);
	    } else {
		if ( (fp = fopen("/tmp/.X0-lock", "r")) != NULL)
		{
    		    fscanf(fp, " %d", &Xpid);
    		    fclose(fp);
		} else {		
		    Xpid=0;
		}
	    }
	}
	/* Open 64x64 window */
	openXwindow(argc, argv, asmon_master_xpm, asmon_mask_bits, asmon_mask_width, asmon_mask_height);
	asmon_routine(Xpid, allmem);
	return(0);
}

/**************************************************************************/

void usage(void) {
    fprintf(stderr, "\nasmon %s - by Brad Hall (brad@rio.dhs.org)\n\t\toriginally based on Timecop's wmcpu\n\n", ASMON_VERSION);
    fprintf(stderr, "The top bar: left is the CPU usage, right is the load average\n");
    fprintf(stderr, "The middle bar: left memory usage devided by ticks into shared, buffers, and\n\t\t cached, respectively, and the number of megs used\n");
    fprintf(stderr, "The lower bar: the left swap usage and the number of megs swappedd avg\n");
    fprintf(stderr, "The bottom: the left is a set of LED's marking page's and swap's, the right is\n\t\t a bar representing the amount of memory that the X server \n\t\t is taking up, and the exact megs\n\n usage:\n");
    fprintf(stderr, "\t-display <display name>\n");
    fprintf(stderr, "\t-h\tthis screen\n");
    fprintf(stderr, "\t-v\tprint the version number\n");
    fprintf(stderr, "\t-u\tforce asmon to show uptime, rather than X mem use\n");
    fprintf(stderr, "\t-m\tmake asmon show only shared mem, like most other mem meters\n");
#ifdef EXEC_ON_CLICK
    fprintf(stderr, "\t-e cmd\texecute 'cmd' on mouse click\n");
#endif
    fprintf(stderr, "\n");
}

/**************************************************************************/

void printversion(void) 
{
    fprintf(stderr, "asmon %s\n", ASMON_VERSION);
}

/**************************************************************************/

void asmon_routine(int Xpid, int allmem)
{
	int xpm_X=0, xpm_Y=0, count=0;
	XEvent Event;
	float total=0.0;

	while(1) 
	{
	    DrawCPU();
	    DrawLoad();

	    /* Only run every 15 iterations */
	    if ( count == 0 || count == 15) 
		total=DrawMemSwap(total, allmem);

	    /* X mem or Uptime? */
	    if (Xpid == 0) 
	    {
		DrawUptime();
	    } else {
		if (count == 5) 
		    DrawXmem(Xpid, total);
	    }

	    /* Redraw Windows */
	    RedrawWindowXY(xpm_X, xpm_Y);
	    while (XPending(display))
	    {
		XNextEvent(display, &Event);
		switch (Event.type)
		{	
#ifdef EXEC_ON_CLICK
			case ButtonPress:
#if 0
				fprintf(stderr,"system(%s)\n",Command);
#endif
				if (Command[ 0 ]) system(Command);
				break;
#endif
			case Expose:
				RedrawWindowXY(xpm_X, xpm_Y);
				break;
			case DestroyNotify:
				XCloseDisplay(display);
				exit(0);
				break;
		}
	    }
	    count++;
	    if (count > 30) 
	        count = 0;
   	    usleep(150000);
    }
}


/**************************************************************************/

/* CPU Usage Meter */
void DrawCPU(void)
{
	FILE *fp;
	static float cpustat[4];   /* remember the statistics read last time */
	float fields[4], info[4], cputotal=0.0;
	long pageins=0, pageouts=0, swapins=0, swapouts=0;
	char buf[128];
	int i;

	if( (fp = fopen("/proc/stat", "r")) != NULL)
	{
	    // CPU data
	    fscanf(fp, "cpu %f %f %f %f", info, info+1, info+2, info+3);

	    // gather data for LED's
	    while( fgets(buf, 127, fp) )
	    {
		if (strstr(buf, "page"))
    		    sscanf(buf, "page %ld %ld", &pageins, &pageouts);

		if (strstr(buf, "swap"))
    		    sscanf(buf, "swap %ld %ld", &swapins, &swapouts);
	    } 
	    fclose(fp);

	    // Calculate CPU stuff
	    for(i = 0; i < 4; i++)
	    { 
		fields[i] = info[i] - cpustat[i]; 
		cputotal += fields[i]; 
		cpustat[i] = info[i];
	    }

	    // CPU Bar
	    if(cputotal > 0)
	    {
		cputotal = (cputotal-(fields[3]+fields[4]))*1.55;
		if ( cputotal > 26 ) cputotal = 26;
		copyXPMArea(3,84,cputotal,9,5,5);
		copyXPMArea(15,105,(27-cputotal),9,(5+cputotal),5);
		copyXPMArea(16,46,2,14,32,2);
	    }

	    // Page In/Out
	    if (pageins > last_pageins)
	    {
    	        DrawLite(B_RED, 5, 48);
    	    } else {
    	        DrawLite(B_OFF, 5, 48);
	    }

	    if (pageouts > last_pageouts)
	    {
    	        DrawLite(B_RED, 10, 48);
	    } else {
    		    DrawLite(B_OFF, 10, 48);
	    }
	    last_pageins = pageins;
	    last_pageouts = pageouts;

	    // Swap In/Out
	    if (swapins > last_swapins)
	    {
    	        DrawLite(B_RED, 5, 53);
	    } else {
    	        DrawLite(B_OFF, 5, 53);
	    }

    	    if (swapouts > last_swapouts)
	    {
    	        DrawLite(B_RED, 10, 53);
	    } else {
    		DrawLite(B_OFF, 10, 53);
	    }
	    last_swapins = swapins;
	    last_swapouts = swapouts;
	}
}

/**************************************************************************/

/* Load Average */
void DrawLoad(void)
{
    FILE *fp;
    int tempy, tempa, i;
    static float oldv = 0.0;
    float ftmp;
		
    if( (fp = fopen("/proc/loadavg", "r")) != NULL)
    {
	fscanf(fp, "%f", &ftmp);
	fclose(fp);
	if(oldv != ftmp)
	{
	    oldv = ftmp;
	    i=ftmp*26;
	    tempa=ftmp*100;
	    tempy=tempa%10;
	    copyXPMArea(3+(tempy*6),66,6,9,50,5);
	    tempy=tempa/10;
	    tempy=tempy%10;
	    copyXPMArea(3+(tempy*6),66,6,9,44,5);
	    copyXPMArea(65,66,3,9,41,5);
	    tempy=tempa/100;
	    if ( tempy > 9 ) 
	    	{
		tempy=(tempy-10);
		copyXPMArea(3+(tempy*6),95,6,9,34,5); 
	    } else {		
	    	copyXPMArea(3+(tempy*6),66,6,9,34,5); 
	    }
	}
    }
}

/**************************************************************************/

/* Mem/Swap Meter */
float DrawMemSwap(float total, int allmem)
{
    FILE *fp;
    if( (fp = fopen("/proc/meminfo", "r")) != NULL)
    {
	static float stotal=0.0, sshared=0.0, sbuffers=0.0, scached=0.0;
	char junk[128];
	float used, freeM, shared, buffers, cached, swaptotal, 
	    swapused, swapfreeM;
	unsigned long MEMshar,MEMbuff,MEMcach, MEMswap;
	int tempy, tempa;
			
	fgets(junk, 80, fp);
	fscanf(fp, "Mem: %f %f %f %f %f %f\nSwap: %f %f %f",  &total, &used, &freeM, &shared, &buffers, &cached, &swaptotal, &swapused, &swapfreeM);
	fclose(fp);

	if ( allmem == 1) {
	/* All mem areas */
		if(stotal != total || sshared != shared || sbuffers != buffers || scached != cached)
		{
			stotal = total; sshared = shared; sbuffers = buffers; scached = cached;
			if ( (total/101048576) >= 1) 
			{
			    MEMshar=(shared/total)*27;
			    MEMbuff=(buffers/total)*27;
			    MEMcach=(cached/total)*27;
			} else {
			    MEMshar=(shared/total)*33;
			    MEMbuff=(buffers/total)*33;
			    MEMcach=(cached/total)*33;
			}
			// Bar
			if ( (total/101048576) >= 1) 
			{
				copyXPMArea(3,75,((used/total)*28),9,5,19);
			} else {
				copyXPMArea(3,75,((used/total)*34),9,5,19);
			}
			// Separators
			copyXPMArea(15,105,1,9,5+MEMshar,19);
			copyXPMArea(15,105,1,9,7+MEMshar+MEMbuff,19);
			copyXPMArea(15,105,(36-(used/total)*34),9,(5+(used/total)*34),19);
			// Numbers			
			tempa=used/1048576;
			tempy=tempa%10;
    			copyXPMArea(3+(tempy*6),66,6,9,50,19);
			tempy=(tempa/10)%10;
			copyXPMArea(3+(tempy*6),66,6,9,44,19);
			tempy=(tempa/100)%10;
			if ( (total/101048576) >= 1) {
			    copyXPMArea(3+(tempy*6),66,6,9,38,19);
			    copyXPMArea(16,46,2,14,35,16);  
			} else {
			    copyXPMArea(16,46,2,14,41,16); 
			} 
		}
	} else {
	/* Only Shared Memory */ 
		if(stotal != total || sshared != shared)
		{
			stotal = total; sshared = shared; sbuffers = buffers; scached = cached;
			if ( (total/101048576) >= 1) {
			    MEMshar=(shared/total)*27;
			} else {
			    MEMshar=(shared/total)*35;
			}
			// Bar
			copyXPMArea(3,75,MEMshar,9,5,19);
			copyXPMArea(3,102,(36-(shared/total)*36),9,(5+(shared/total)*36),19);
			// Numbers			
			tempa=shared/1048576;
			tempy=tempa%10;
    			copyXPMArea(3+(tempy*6),66,6,9,50,19);
			tempy=(tempa/10)%10;
			copyXPMArea(3+(tempy*6),66,6,9,44,19);
			tempy=(tempa/100)%10;
			if ( (total/101048576) >= 1) {
			    copyXPMArea(3+(tempy*6),66,6,9,38,19);
			    copyXPMArea(16,46,2,14,35,16);  
			} else {
			copyXPMArea(16,46,2,14,41,16); 
			} 
		}
	}
	/* SWAP Meter */
	if ( swaptotal == 0 ) MEMswap = 0; 
	else { 
	    if ( (total/101048576) >= 1) 
		MEMswap=(swapused*31)/swaptotal;
	    else MEMswap=(swapused*36)/swaptotal;
	    }
	// Bar
	copyXPMArea(3,75,MEMswap,9,5,33);
	copyXPMArea(15,105,(36-(MEMswap)),9,5+MEMswap,33);
	// Numbers
	tempa=swapused/1048576;
	tempy=tempa%10;
	copyXPMArea(3+(tempy*6),66,6,9,50,33);
	tempy=tempa/10;
	copyXPMArea(3+(tempy*6),66,6,9,44,33);
	tempy=tempa/100;
	if ( tempy != 0 ) {
	    copyXPMArea(3+(tempy*6),66,6,9,38,33);
	    copyXPMArea(16,46,2,14,42,16); 
	} else {
	    copyXPMArea(16,46,2,14,41,30); 
	}	
    }
    return(total);
}

/**************************************************************************/

/* X Mem Usage */
void DrawXmem(int Xpid, float total) 
{
    FILE *fp;
    char buf[128], XFileName[256];
    float ratio;
    long old_Xsize=-1, Xsize=0;

    sprintf(XFileName, "/proc/%d/status", Xpid);

    if ((fp = fopen(XFileName, "r")) != NULL)
    {
	while( fgets(buf, 127, fp) )
	{
    	    if (strstr(buf, "VmSize"))
        	sscanf(buf, "VmSize: %ld", &Xsize);
	}
        if(old_Xsize!=Xsize)
	{
    	    int tempy, tempa;
	    old_Xsize=Xsize;
	    ratio=Xsize/(total/1024);
	    if ( Xsize > (total/1024) ) Xsize=total/1024;
	    Xsize=Xsize/1024;
	    tempy=Xsize%10;
	    copyXPMArea(3+(tempy*6),66,6,9,50,48);
	    tempa=Xsize/10;
	    tempy=tempa%10;
	    copyXPMArea(3+(tempy*6),66,6,9,44,48); 
	    copyXPMArea(16,46,2,14,41,46);  
	    copyXPMArea(3,84,((ratio)*22),11,18,47);
	    copyXPMArea(15,105,(23-((ratio)*22)),11,(18+(ratio*22)),47);
	}
	fclose(fp);
    }
}
/**************************************************************************/

/* Uptime */
void DrawUptime(void)
{
	FILE *fp;
	int upt, days=0,hours=0,mins=0, old_mins=-1,old_hours=-1;
	if( (fp = fopen("/proc/uptime", "r")) != NULL)
		fscanf(fp, "%d",&upt);
	fclose(fp);
	mins=(upt/60)%60;
	hours=(upt/3600)%24;
	days=(upt/86400);
	if(old_hours!=hours) 
	    old_hours=hours;
	if(old_mins!=mins)
	{
	    int tempy;
	    old_mins=mins;
	    if ( days > 9 ) 
	    	{
	    	tempy=hours%10;
	    	copyXPMArea(3+(tempy*6),66,6,9,50,48);
	    	tempy=hours/10;
	    	copyXPMArea(3+(tempy*6),66,6,9,44,48);
	    	copyXPMArea(63,66,3,9,41,48);
    	    	tempy=days%10;
		copyXPMArea(3+(tempy*6),66,6,9,34,48);
	    	tempy=days/10;
		copyXPMArea(3+(tempy*6),66,6,9,28,48);
		tempy=days/100;
		copyXPMArea(3+(tempy*6),66,6,9,22,48);
	    } else {
	    	tempy=mins%10;
	    	copyXPMArea(3+(tempy*6),66,6,9,50,48);
	    	tempy=mins/10;
	    	copyXPMArea(3+(tempy*6),66,6,9,44,48);
	    	copyXPMArea(63,66,3,9,41,48);
	    	tempy=hours%10;
	    	copyXPMArea(3+(tempy*6),66,6,9,34,48);
	    	tempy=hours/10;
	    	copyXPMArea(3+(tempy*6),66,6,9,28,48);
	    	copyXPMArea(63,66,3,9,25,48);
    	    	tempy=days%10;
	    	copyXPMArea(3+(tempy*6),66,6,9,18,48); 
	    
	    }
	}
}

/**************************************************************************/

/* Drawing LED's */
void DrawLite(int state, int dx, int dy)
{
    switch(state)
    {
    case B_RED:
        copyXPMArea(BREDX, BREDY, LITEW, LITEH, dx, dy);
        break;
    case B_GREEN:
        copyXPMArea(BGREENX, BGREENY, LITEW, LITEH, dx, dy);
        break;
    default:
    case B_OFF:
        copyXPMArea(BOFFX, BOFFY, LITEW, LITEH, dx, dy);
        break;
    }

}

/* EOF */
