/*
 *  RemoteLoad.c
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  RemoteLoad, a more or less complete rewrite of Loadav
 *     (c) Copyright 1993, Eric mehlhaff, 
 *
 *  LOADAV  [frequency]     Connect to remote UNIX system and display
 *			    load average.  Default is every 5 minutes.
 *
 *  frequency in seconds, default is every 60 seconds
 *
 */

/* #define DEBUG */
/* #define TEST */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <stdio.h>
#include <proto/all.h>
#include <devices/conunit.h>
#include <devices/timer.h>

#ifdef __GNUC__
#include <signal.h>  /* for signal trapping */
#endif


#ifdef DEBUG
#define IFDEBUG(foo)  foo
#else
#define IFDEBUG(foo)  
#endif

#include "server/servers.h"


typedef struct IntuiMessage 	IMESS;
typedef struct IntuiText 	ITEXT;
typedef struct Library 		LIB;


unsigned char Title[128];   /* string that goes in the title window */
unsigned char Uptime[128];  /* Current uptime reading, from remote */

#define LOADWINBORDER 2   /* pixels to either side of the LoadWinBorder */


struct NewWindow Nw = {
    0, 0, /* width */320, /* height */50, -1, -1,
    NEWSIZE|CLOSEWINDOW,
    WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|NOCAREREFRESH,
    NULL, NULL, Title, NULL, NULL,
    32, 18, -1, -1, WBENCHSCREEN
};

struct Window *Win;
struct RastPort  *Rp;


struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;


int main (int, char **);

int broken = 0;  /* set to on when we received a signal (via gcc's/ixemul's
		 ** pseudo-signaling system 
		 */
void brk()
{
    broken = 1; 
}

struct LoadInfo {
   short NumUsers;  /* number of users online */
   short Load1;  /*  1 minute average -- first value from uptime */
   short Load2;  /*  5 minute average -- second value from uptime */
   short Load3;  /* 15 minute average -- third value from uptime */
};

struct LoadInfo * LoadElement( int );

int NumLoadData = 0;   /* number of load points */

struct LoadInfo * LoadData = NULL;  /* will point to the load data */
    /* LoadData will be a dynamically allocated array of LoadInfo
    ** structures
    */

int LoadScale;     /* maximum load, fix the update window so that it scales
		   ** to this
		   */
int MaxUsers;      /* maximum number of users, to use with load scale */

int LoadStart;     /* index of start of load data, within LoadData structure
		   ** used when we're concerned about the CPU load of just
		   ** moving the entire structure everytime it is updated
		   */

int UpdateIndex = -1;   /* index point of last element filled. Should range 
			** 0 to NumLoadData-1 , -1 if nothing's been filled
			*/

char * strsep();

int
main(argc,argv)
char *argv[];
{
    void *chan = NULL;
    short numsecs = 60;
    long imask, tmask, dmask, mask;
    char notdone = 1;
    char *host = NULL;
    struct MsgPort *TimPort = CreatePort(NULL, 0);
    struct timerequest Iot;

#ifdef __GNUC__
    signal( SIGINT, brk); /* disable normal ^C handler */
#else
    onbrk(brk);
#endif

    sprintf(Title, "%s V.5", argv[0]);

    {
	int i;
	int usage = 0;
	for (i = 1; i < argc; ++i) {
	    if( *argv[i] != '-'){
	        numsecs = atoi(argv[i]);
		if(numsecs < 1){
		    printf("Bad time value %s, try another number\n", argv[i]);
		} else {
		    printf("Remote load polling period %d seconds\n", numsecs);
		}
	    } else {
	        char swchar = *(argv[i]+1);
	        switch( swchar ){
		  case 'W': /* set window width */
		  	if( argv[i+1])  Nw.Width = atoi(argv[i+1]);
			i++; break;
		  case 'H': /* set window Height */
		  	if( argv[i+1])  Nw.Height = atoi(argv[i+1]);
			i++; break;
		  case 'T': /* set window Top */
		  	if( argv[i+1])  Nw.TopEdge = atoi(argv[i+1]);
			i++; break;
		  case 'L': /* set window LeftEdge */
		  	if( argv[i+1])  Nw.LeftEdge = atoi(argv[i+1]);
			i++; break;
		  case 'N':  /* set host to connect to */
		  	if( argv[i+1]) host = argv[i+1];
			i++; break;
		  default:
		    printf("unknown option -%c \n", swchar);
		  case '?':
		  case 'h':
		    usage = 1;
	        }
	    }
	}
	if(usage){
	    printf("%s Usage:\n", argv[0]);
	    printf("-L num\tLeftEdge\n");
	    printf("-T num\tTopEdge\n");
	    printf("-H num\tHeight\n");
	    printf("-W num\tWidth\n");
	    printf("-N name\tDNet Host to connect to\n");
	    printf("-h or -?\t get usage information.\n");
	    printf(" num\t load polling period, in seconds" ); 
	    goto fail;
	}
    }


    Iot.tr_node.io_Device = NULL;

    if (OpenDevice("timer.device", UNIT_VBLANK, (struct IORequest *)&Iot, 0)){
	printf("TimerDevice open failed??!\n");
	goto fail;
    }
    Iot.tr_node.io_Command = TR_ADDREQUEST;
    Iot.tr_node.io_Message.mn_ReplyPort = TimPort;
    Iot.tr_time.tv_micro = 1;
    Iot.tr_time.tv_secs  = 0;
    SendIO((struct IORequest *)&Iot);

#if !defined( LATTICE) && !defined( __GNUC__)
    Enable_Abort = 0; 
#endif

    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
    if(IntuitionBase == NULL){
       puts("Ack, couldn\'t open intuition.library!");
       goto fail;
    }
    if((GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0)) == NULL){
       puts("Ack, couldn\'t open graphics.library!");
       goto fail;
    }

#ifndef TEST
    chan = (void *) DOpen(host, PORT_LOADAV, 25, 25);
    if (chan == NULL) {
	puts("DOpen failed to connect!");
	IFDEBUG(sleep(1);)
	goto fail;
    }
    IFDEBUG(puts("DOpen() done"));
#endif /* TEST */

    AdjustNewWindow(&Nw);  /* for negative offsets in windows and such */

    Win = OpenWindow(&Nw);
    if (Win == NULL) {
	puts("Unable to open window");
	goto fail;
    }
    IFDEBUG(printf("Window opened...\n"));

    Rp = Win->RPort;
    imask   = 1 << Win->UserPort->mp_SigBit;
#ifndef TEST
    dmask   = 1 << ((struct MsgPort *)chan)->mp_SigBit;
#else
    dmask   = 0 ;  /* What SigBit? */
#endif
    tmask   = 1 << TimPort->mp_SigBit;

    IFDEBUG(printf("signal masks: i %x, d %x, t %x\n", imask, dmask, tmask));

    DrawLoadWindow();
    IFDEBUG(printf("LoadWindow Drawn...\n"));

#ifdef TEST
    /* so we don't have to wait as long for data to test out with */
    for(notdone=1;notdone<100;notdone++){
	RandomLoadUpdate();   /* generate a random load input */
	UpdateLoad();
    }
    notdone = 1;
#endif

    while (notdone) {
	mask = Wait(imask|dmask|tmask|SIGBREAKF_CTRL_C);
        IFDEBUG(printf("signal(s) gotten:%x\n", mask));
	if (mask & SIGBREAKF_CTRL_C){
	    notdone = 0;
	}

	if (mask & imask) {  /* handle intuition messages */
	    IMESS *im;
	    while (im = (IMESS *)GetMsg(Win->UserPort)) {
		IFDEBUG(printf("UserPort message:%d\n", im->Class);)
		switch(im->Class) {
		case NEWSIZE:
		    DrawLoadWindow();
		    break;
		case CLOSEWINDOW:
		    notdone = 0;
		    break;
		}
		ReplyMsg((struct Message *)im);
	    }
	}

	if (mask & dmask) {  /* DNet communications message */
	    char dummy;
	    if ((dummy = DNRead(chan, &dummy, 1)) != 0)
		notdone = 0;
	    IFDEBUG(printf("DNRead resulted %d \n", dummy));
	}

	if (mask & tmask) { /* a timer message */
	    char len = 0;

	    IFDEBUG(printf("tmask\n"));

	    if (GetMsg(TimPort)) {
		Iot.tr_time.tv_micro = 0;
		Iot.tr_time.tv_secs  = numsecs;
		SendIO((struct IORequest *)&Iot);

#ifndef TEST
		/* synch dnet by writing one '\0' character, and then
		** getting a response of one character.
		** this response is the length of the `uptime` string
		** we need to parse
		*/
		if (DWrite(chan, &len, 1) == 1 && DRead(chan, &len, 1) == 1) {
		    if (len < sizeof(Uptime) && DRead(chan, Uptime, len) == len) {
			Uptime[len] = 0;
			IFDEBUG(printf("updatewindow() \n"));
			UpdateLoad();
		    } else {
			puts("Error: Could not get uptime!");
		    	if(len < sizeof(Uptime)){
			    puts("DRead() confusion, I give up"); 
			    notdone = 0;
			}  else {
			    printf("Overlong(%d) uptime string received...\n",
			    	len );
			}
		    }
		} else {
		    puts("Remote DNet Error, bye..");
		    notdone = 0;
		}
#else  /* TEST */
		RandomLoadUpdate();   /* generate a random load input */
		UpdateLoad();
#endif
	    }
	}
    }


fail:
    IFDEBUG(printf("Shutting things down...\n"));
    if (Iot.tr_node.io_Device) {
	AbortIO((struct IORequest *)&Iot);
	WaitIO((struct IORequest *)&Iot);
	CloseDevice((struct IORequest *)&Iot);
    }
    DeletePort(TimPort);
    IFDEBUG(printf("TimerPort deleted...\n"));
    if (Win){
	CloseWindow(Win);
	IFDEBUG(printf("Window closed...\n"));
    }
    if (chan){
	DClose(chan);
	IFDEBUG(printf("channel DClosed()...\n"));
    }
    if (IntuitionBase)
	CloseLibrary((LIB *)IntuitionBase);
    if (GfxBase)
	CloseLibrary((LIB *)GfxBase);

    if ( LoadData){
	IFDEBUG(printf("LoadData freed at %x...\n", LoadData));
    	free(LoadData);
    }

    IFDEBUG(printf("all done...\n"));
}

/*
 *  Graphics routines.	************************************************
 */

short 	WOx, /* XOffset of window edge */
	WOy, /* Y offset of window edge */
 	Ww,  /* width of drawable area inside window */
	Wh;  /* height of drawable area inside window */

short LoadYPos(  short val , short maxrange){
    short ypos;
    ypos = Wh * val / maxrange;
    return WOy + Wh - ypos ;
}

/* draw all the graphics for the window */
DrawWindow()
{
    short i, j, d;
    short ypos;
    register struct LoadInfo * ldta;
    int notmoved = 1;

    IFDEBUG(printf("Start DrawWindow()\n"));


    WOx = Win->BorderLeft;  /* the start of the drawable areas of the window */
    WOy = Win->BorderTop;

    /* get sizes of drawable area of the window */
    Ww	= Win->Width - Win->BorderRight - Win->BorderLeft -1 ;   
    Wh	= Win->Height- Win->BorderTop	- Win->BorderBottom -1 ; 

    SetAPen(Rp, 0);   /* set pen to draw 'clear' color */
    RectFill(Rp, WOx, WOy, Ww + WOx, Wh + WOy);  /* fill rectangle */


    /* shrink in a few pixels for an internal border ? */
    WOx += LOADWINBORDER; 
    WOy += LOADWINBORDER; 
    Ww -= LOADWINBORDER * 2; 
    Wh -= LOADWINBORDER * 2;   

    if(Ww < 0) Ww = 0; 
    if(Wh < 0) Wh = 0; 

    /* if window's too small to draw anytyhing, don't... */
    if(Ww == 0) return;
    if(Wh == 0) return;

    /* draw the load scale lines, if any */
    if( LoadScale > 1 ){
	IFDEBUG(printf("Drawing Max Load scale lines Max: %d\n", LoadScale));
	SetAPen(Rp,  3 );
        for(i=1;i< LoadScale;i++){
	    ypos = LoadYPos( i * 100, LoadScale * 100 );
	    Move(Rp, WOx, ypos);
	    Draw(Rp, WOx+Ww, ypos);
	}
    }

    /*
     *	Redraw the graph.  Scale values relative to LoadScale and Wh.
     *
     */

    /* Someting to do later:  move all these move/draw calls into a single
    ** PolyDraw
    */

    /* draw curve for number of users */
    SetAPen(Rp, 3 );
    notmoved = 1;
    for (i = 0; i <= UpdateIndex; i++) {
	if( (ldta = LoadElement(i)) == NULL) continue;
	if(ldta->NumUsers  == -1) continue;
	ypos = LoadYPos(ldta->NumUsers, MaxUsers);
	if( notmoved ) {
	    Move( Rp, WOx + i, ypos ) ;
	    notmoved = 0;
	} else {
	    Draw( Rp, WOx + i, ypos ) ;
	}
    }
    IFDEBUG(printf("done Drawing lines for Numusers \n"));

    /* draw curve for load 0 */
    SetAPen(Rp, 1 );
    notmoved = 1;
    for (i = 0; i < NumLoadData; i++) {
	if( (ldta = LoadElement(i)) == NULL) continue;
	if(ldta->NumUsers  == -1) continue;
	ypos = LoadYPos( ldta->Load1, LoadScale * 100 );
	if(notmoved) {
	    Move( Rp, WOx + i, ypos ) ;
	    notmoved = 0;
	} else {
	    Draw( Rp, WOx + i, ypos ) ;
	}
    }
    IFDEBUG(printf("Done Drawing lines for Load \n"));
}


UpdateLoadWindow(){
    struct LoadInfo *ptr1, *ptr2;
    short Xpos, OldX, Ypos, OldY;
    IFDEBUG(puts("UpdateLoadWindow"));

    ptr1 = LoadElement( UpdateIndex - 1 );
    ptr2 = LoadElement( UpdateIndex );

    if(ptr1 == NULL || ptr2 == NULL ) return;

    if((ptr1->NumUsers == -1) ||(ptr2->NumUsers == -1)) {
	IFDEBUG(printf("UpdateLoadWindow bad Numusers: %d, %d\n",ptr1->NumUsers, ptr2->NumUsers));
	return;
    }

    if(UpdateIndex >= NumLoadData - 1){
	ScrollRaster(Rp, 1, 0, WOx, WOy, WOx + Ww + 1 , WOy + Wh );
    }

    Xpos = WOx + UpdateIndex + 1 ;
    OldX = WOx + UpdateIndex ;

    /* draw the load scale lines, if any */
    if( LoadScale > 1 ){
	short i, ypos;
	IFDEBUG(printf("Updating Load scale lines Max: %d\n", LoadScale));
        for(i=1;i< LoadScale;i++){
	    ypos = LoadYPos( i * 100, LoadScale * 100 );
	    SetAPen(Rp,  3 );
	    Move(Rp, OldX, ypos);
	    Draw(Rp, Xpos, ypos);
	}
    }

    /* update NumUsers */
    OldY = LoadYPos(ptr1->NumUsers , MaxUsers);
    Ypos = LoadYPos(ptr2->NumUsers , MaxUsers);
    SetAPen(Rp, 3);
    Move(Rp, OldX, OldY);
    Draw(Rp, Xpos, Ypos);
    IFDEBUG(printf("Users update: %d,%d to %d,%d\n", OldX, OldY, Xpos, Ypos));

    /* Update Load average */
    OldY =  LoadYPos(ptr1->Load1 , LoadScale * 100);
    Ypos =  LoadYPos(ptr2->Load1 , LoadScale * 100);
    SetAPen(Rp, 1);
    Move(Rp, OldX, OldY);
    Draw(Rp, Xpos, Ypos);
    IFDEBUG(printf("Load update: %d,%d to %d,%d\n", OldX, OldY, Xpos, Ypos));

    IFDEBUG(puts("UpdateLoadWindow done");) 
}

DrawLoadWindow(){
    /* for now, just do basic stuff */
    IFDEBUG(puts("DrawLoadWindow()"));

    InitLoadData();

    DrawWindow();
}

/*
 *  uptime and load information held in string array Uptime[], in a format
 *  something like this:
 *
 *  5:44pm up 2 days, 22:30, 37 users, load average: 5.98, 7.93, 7.97
 *		      7 mins,
 *
 *  this needs to be parsed...
 */

/* Uptime will be parsed and stored into these : */
char TimeStr[10];   /* the string holding the time.  i.e. 5:44pm */
int  NumUsers;      /* the number of users */
short Loads[3];     /* the 3 load averages.  Multiplied by 100 */

/* parse the Uptime string... */
ParseUptime(){
    char **tokens[20]; /* array of pointers to string arrays */
    char ** ap = (char **) tokens;
    char * val;
    char * p;
    int i;

    IFDEBUG(printf("Parsing Uptime:%s\n", Uptime));

    TimeStr[0]='\0';
    NumUsers = -1;
    Loads[1]=Loads[2]=Loads[3]=-1;


    /* Fun with strsep, practically straight from the manpage! */
    for( p = Uptime; p != NULL; ){
	while((val = strsep(&p, " \t")) != NULL && *val == '\0');
	/* IFDEBUG(printf("Separated string %s\n", val)); */
	*ap++ = val;
    }
    *ap = 0;

    if(*tokens[0] != NULL){
   	strcpy(TimeStr, (char *)tokens[0]);
	IFDEBUG(printf("TimeString parsed as \"%s\"\n", TimeStr));
    }

    /* search for number of users token */
    for(ap = (char **) tokens ; *ap != NULL; ap++){
	if(strncmp(*ap, "users",4)) continue;
	NumUsers = atoi( *(ap - 1));
	IFDEBUG(printf("NumUsers parsed as %d\n", NumUsers));
	break;
    }

   /*  IFDEBUG(puts("Looking for loads...")); */

    /* now search out load average tokens */
    for(ap = (char **) tokens ; *ap != NULL; ap++){
	if(strncmp(*ap, "average",7)) continue;
        /* IFDEBUG(puts("found \"average\"")); */
	ap++;  if( *ap == NULL ) break;
        /* IFDEBUG(printf("string for Load1 is %s\n", *ap ) ); */
	Loads[0] = LoadParse( *ap );
	ap++;  if( *ap == NULL ) break;
        /* IFDEBUG(printf("string for Load2 is %s\n", *ap ) ); */
	Loads[1] = LoadParse( *ap );
	ap++;  if( *ap == NULL) break;
	Loads[2] = LoadParse( *ap );
	break;
    }
    IFDEBUG(printf("Loads parsed as %d %d %d\n", Loads[0], Loads[1], Loads[2]));
}

/* LoadParse
** return load value parsed from string */
int LoadParse(char * string){
    char * strptr;
    int loadval = 0;

    for(strptr = string; *strptr != '\0';strptr++){
        if(*strptr == '.') continue;
	if(*strptr >= '0' && *strptr <= '9'){
		loadval = 10 * loadval + *strptr - '0';
	} else 
		break;
    }
    return loadval;
}

/* return pointer to LoadInfo element for numbered 'index' */
struct LoadInfo * LoadElement( int index){
   int lindex;
   struct LoadInfo *ptr;

   if(index < 0 || index >= NumLoadData) return NULL;
   if(LoadData == NULL) return NULL;

   lindex = index + LoadStart;

   if( lindex >= NumLoadData) 
   	lindex -= NumLoadData;

   if(lindex < 0 || lindex >= NumLoadData){
	IFDEBUG(printf("LoadElement() error, wacky lindex %d\n", lindex);)
	IFDEBUG(printf("out of range 0-%d, sent %d, start %d\n", NumLoadData, index, LoadStart);)
   	return NULL;
   }

   ptr= &(LoadData[lindex]);

   /* Way too much debugging info... 
   IFDEBUG(printf("LoadElement(%d) is %x (of %x to %x)\n", index, ptr, LoadData, &(LoadData[NumLoadData -1]) ));
   */

   return ptr;
}


/* add current load information to the stored LoadData  */
UpdateLoadInfo(){
   struct LoadInfo *addptr;

   UpdateIndex++;

   if(UpdateIndex >= NumLoadData -1){
	LoadStart++;
	if(LoadStart >= NumLoadData -1) LoadStart = 0;
	UpdateIndex = NumLoadData -1 ;
   }

   addptr = LoadElement(UpdateIndex);
   if(addptr == NULL) return;

   addptr->NumUsers = NumUsers;
   addptr->Load1 = Loads[0];
   addptr->Load2 = Loads[1];
   addptr->Load3 = Loads[2];

}

UpdateLoad()
{
    short d;
    char refresh = 0;
    short tmpscale = LoadScale * 100;

    IFDEBUG(puts("starting UpdateLoad()"));

    if(Uptime[0])
    	ParseUptime();
    else
    	return;

    if( LoadData == NULL) {
    	InitLoadData();
	if(LoadData == NULL){
	    puts("InitLoadData FAILED!");
	    return;
	}
    }

    UpdateLoadInfo();

    if(Loads[0] > tmpscale || Loads[1] > tmpscale || Loads[2] > tmpscale ||
    		NumUsers > MaxUsers ){
    	refresh = 1;
    }

    if (refresh) {
	DrawLoadWindow();
    } else {
        UpdateLoadWindow();
    }

#ifdef HOZERS
    sprintf(Title, "%s, %d hozers, Load: %2.2f %2.2f %2.2f", 
        TimeStr, NumUsers, (double)Loads[0]/100, (double)Loads[1]/100, 
	(double)Loads[2]/100);
#else
    snprintf(Title, sizeof(Title), "Load: %2.2f, %d users, %s", 
    	(double)Loads[0]/100, NumUsers, TimeStr);
#endif
    SetWindowTitles(Win, Title, (UBYTE *)-1);


    IFDEBUG(puts("UpdateLoad() done"));
    return;
}



/* also used to re-init load data, when window sizes change */
InitLoadData(){
    struct LoadInfo * newloadarry = NULL;
    int winwidth = 0;
    int newscale;
    int i;

    IFDEBUG(puts("InitLoadData() started "));

    if(Win != NULL)
	winwidth = Win->Width - Win->BorderRight  
		- Win->BorderLeft - 2 * LOADWINBORDER;  
    if( winwidth < 0) winwidth = 0 ;

    if(winwidth != NumLoadData || LoadData == NULL){
        newloadarry = (struct LoadInfo *) calloc( winwidth, 
						    sizeof(struct LoadInfo));
	IFDEBUG(printf("NewLoadarray is %x\n", newloadarry));
	if(newloadarry == NULL) {
	    /* this is pretty serious. probably should go away */
	    return;
	}

	/* 'clear the newloadarray */
	for(i=0;i<winwidth;i++) 
		newloadarry[i].NumUsers = -1;

	if( LoadData != NULL){
	    MapOldLoadDataOntoNew( winwidth, newloadarry);
	    IFDEBUG(printf("Freeing old LoadData at %x\n", LoadData));
	    free(LoadData);
	    LoadData = NULL;
	}

	LoadStart = 0;
	LoadData = newloadarry;
	NumLoadData = winwidth;
	if(UpdateIndex >= NumLoadData ) UpdateIndex = NumLoadData -1 ;
    }

    GetLoadScale();
    IFDEBUG(puts("InitLoadData() done "));
}

#ifndef MAX
#define MAX(a,b) ((a) > (b)? (a) : (b))
#endif

/* return the largest load average within the known load data */
int GetLoadScale(){
   int i=0;
   int maxload = 0;
   int maxusers = 1;
   struct LoadInfo * lp;
   IFDEBUG(int lastload = maxload;)

   IFDEBUG(printf("GettingLoadScale() num elements:%d\n", UpdateIndex));
   IFDEBUG(printf("Num: %d, Start: %d",NumLoadData, LoadStart));
   for(i=0;i <= UpdateIndex;i++){
   	lp = LoadElement(i);
	if(lp == NULL) continue;
IFDEBUG(printf("Elem %d, #u: %d, ld %d\n",i, lp->NumUsers, lp->Load1));
	if(lp->NumUsers == -1) continue;
	maxusers = MAX(maxusers, lp->NumUsers);
	maxload = MAX(maxload,  lp->Load1);
	maxload = MAX(maxload,  lp->Load2);
	maxload = MAX(maxload,  lp->Load3);
#ifdef DEBUG
	if(lastload != maxload) {
		printf("New maxload %d at %d\n", maxload, i);
		lastload = maxload;
	}
#endif
   }
   maxload = (int)(maxload / 100) +1;
   LoadScale = maxload;
   MaxUsers = maxusers;

   IFDEBUG(printf("GotLoadScale(%d) Load: %d  Users: %d \n",NumLoadData, LoadScale, MaxUsers) );
}

/* copy over the old data to the new array 
** it has to start at 0 in the new array.
*/
MapOldLoadDataOntoNew(int winwidth, struct LoadInfo * newloadarry){
    int startindex, i;
    int lindex;

    struct LoadInfo * ptr1;
    struct LoadInfo * ptr2; 

    IFDEBUG(printf("MappingLoadData Old %d, New %d, Start %d, Num %d \n",NumLoadData, winwidth, LoadStart, UpdateIndex) );

    /* only keep most recent data if window is shrinking */
    if( winwidth < UpdateIndex ){
    	startindex = UpdateIndex + 1 -  winwidth;
    } else {
    	startindex = 0 ;
    }

    for (i=startindex,lindex = 0; i <= UpdateIndex;i++){
        ptr1 = LoadElement(i);
	if(ptr1 == NULL) continue;

	ptr2 = &newloadarry[lindex];
        IFDEBUG(if(lindex>=winwidth)printf("Map:lindex too big %d\n", lindex);)
        lindex++;
	memcpy( ptr2, ptr1, sizeof(struct LoadInfo));
    }
}


/* we have a NewWindow structure that might have negative values in ts
** size or position fields
** we interpret these negative values to mean relative to relevant screen
** size.
** i.e. negative LeftEdge is that distance from screen's right edge
*/
AdjustNewWindow( struct NewWindow * NewW){
    int screenwidth = 320;
    int screenheight = 200;
    struct Screen *ScreenInfo;

    /* grab the default screen and hold it until we get info on it... */
    if((ScreenInfo =LockPubScreen( NULL )) != NULL){
	screenwidth = ScreenInfo->Width;
	screenheight = ScreenInfo->Height;
        UnlockPubScreen(NULL,ScreenInfo);
    } else {
        printf("Hrm, can\'t get information on default screen!\n");
    }

    if(NewW->Height < 0){
    	NewW->Height = screenheight + NewW->Height;
	if(NewW->Height < 0)  /* someone specified a way negative size? */
		NewW->Height = screenheight;
    } else if(NewW->Height == 0){
        NewW->Height = screenheight / 5;   /* a sane default size? */
    } else { /* newW->Height > 0 */
        if(NewW->Height > screenheight ){  /* you really want a big window?? */
	    NewW->Height = screenheight;
	}
    }
    if(NewW->Width < 0){
    	NewW->Width = screenwidth + NewW->Width;
	if(NewW->Width < 0)  /* someone specified a way negative size? */
		NewW->Width = screenwidth;
    } else if(NewW->Width == 0){
        NewW->Width = screenwidth / 5;   /* a sane default size? */
    } else { /* newW->Width > 0 */
        if(NewW->Width > screenwidth ){  /* you really want a big window?? */
	    NewW->Width = screenwidth;
	}
    }

    /* handle negative offsets for window positions */
    if(NewW->LeftEdge < 0){
    	NewW->LeftEdge = screenwidth + NewW->LeftEdge;
    } 
    if(NewW->TopEdge < 0){
    	NewW->TopEdge = screenheight + NewW->TopEdge;
    } 

    if(NewW->LeftEdge + NewW->Width > screenwidth){
    	NewW->LeftEdge = screenwidth - NewW->Width;
    }
    if(NewW->TopEdge + NewW->Height > screenheight){
    	NewW->TopEdge = screenheight - NewW->Height;
    }

}

#ifdef TEST

#include <time.h>

char * NowClock(){
  static char timebuf[10];
  long curtime;
  struct tm *tm;

  time(&curtime);
  tm = (struct tm *)localtime(&curtime);
  sprintf(timebuf, "%02d:%02d", tm->tm_hour, tm->tm_min);

  return timebuf;
}

/* generate a random load -- fake a report from the other end */
#define LOADSIZ 10
#define LOADRANGE 3
RandomLoadUpdate(){
    static long FooLoad = -1;
    static int LoadGens[LOADSIZ];
    int numusers = 0;
    int rnum;
    int i;
    int load;


    if(FooLoad == -1) srandom(FindTask(NULL));

    FooLoad = random() % (100 * LOADRANGE) ;

    rnum = random() % LOADSIZ;

    LoadGens[rnum] = FooLoad;

    load= 0;
    for(i=0;i<LOADSIZ;i++) {
    	load += LoadGens[i];
	if( LoadGens[i] > LOADRANGE * 50) 
		numusers++;
    }

    load = load / LOADSIZ;

    sprintf( Uptime, 
	" %s up 2 minutes, 0:23, %d users, load average: %2.2f %2.2f %2.2f", 
	NowClock(), numusers, (double) load / 100, (double) load/100, 
	(double) load/100);
    IFDEBUG(printf("Faked uptime: %s\n", Uptime);)
}

#endif /* TEST */
