#import <c.h>
#import <libc.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>

#import <sys/time.h>

#import "LoadView.h"
#import "LoadWrap.h"

#define LOCALUPDATE 10

#define MAXERRORS 3

extern int loadave();

void swap(float array[], int indexA, int indexB);

void timer(DPSTimedEntry, double, id);

/*---------------------------------------------------------------------------
Besides creating the new frame, initialize variables to default settings.
With the addition of the RPC/UDP support, some new variables were added.
These variables, initial settings, and impact of the settings follow:

                  Initial
    Variable      Setting   Comment
    hostName      NULL      overridden later to host name,
    nErrors       MAX...    forces the view white if the first client/server
                            call fails; then records # consecutive errors,
                            turning the view white after MAXERRORS,
-----------------------------------------------------------------------------*/

@implementation LoadView

- initFrame:(const NXRect *) frameRect;
{
	const char *string;
	int qLength;

	[(self = [super initFrame:frameRect]) allocateGState];

	qLength = ALL;
	if ((string = getDefault("QueueLength")) && sscanf(string, "%d", &qLength) != 1) qLength = ALL;

	switch (qLength) {
		case 15: queue = Q15MIN; break;
		case  5: queue =  Q5MIN; break;
		case  1: queue =  Q1MIN; break;
		default: queue =    ALL; break;
		}

	index = -1;
	maxLoad = (float) 0.0;

	hostName = NULL;
	
	timedEntry = NULL;

	nErrors = MAXERRORS - 1; /* Forces view white if initial call fails. */

	return self;
}

- drawSelf:(const NXRect *) rects :(int) rectCount
{
	int width = (int) bounds.size.width;
	float currentLoad;
	int oldIndex;

	oldIndex = index;
	index = ++index % width;

	if ((currentLoad = [self getLoadAverage:index]) == (float) CERROR) {
		index = oldIndex;
		if (++nErrors == MAXERRORS) [self displayAll:NX_WHITE];
		return self;
		}
	
	if (index == --width) [[self moveArray] displayAll:NX_LTGRAY];
	else
		if (currentLoad > maxLoad || nErrors >= MAXERRORS) [self displayAll:NX_LTGRAY];
		else [self drawOneLoad:index];
	    
	nErrors = 0;
	
	return self;
}

- free
{
	[self freeGState];
	if (timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);
	
	return [super free];
}

- startTimer
{
	const char *string;
	int time = LOCALUPDATE;

	if ((string = getDefault("LocalUpdateSeconds")) && sscanf(string, "%d", &time) != 1)
		time = LOCALUPDATE;
	
	timedEntry = DPSAddTimedEntry((double) time, (DPSTimedEntryProc) &timer, self, NX_BASETHRESHOLD);
	
	return self;
}

- (float) getLoadAverage:(int) offset 
{
	int i, scale, first = 0, last = QUEUES;
	long vector[QUEUES];
	float largest = 0.0;
	
	if ([self loadAverage:vector loadScale:&scale] == nil) return (float) CERROR;	   
	
	if (queue != ALL) last = 1 + (first = queue);
    
	for (i = first; i < last; i++) {
	   load[offset][i] = vector[i] / (float) scale;
	   if (load[offset][i] > largest) largest = load[offset][i];
	}
	
	return largest;
}

- loadAverage:(long *) vector loadScale:(int *) scale
{
	if ((*scale = loadave(vector)) == CERROR) return nil;
	else return self;
}

- drawOneLoad:(int) offset 
{
	float vector[QUEUES], colors[QUEUES] = { NX_BLACK, NX_DKGRAY, NX_WHITE };
	int i, j, first = 0, last = QUEUES;

	if (queue != ALL) last = 1 + (first = queue);

	for (i = first; i < last; i++) vector[i] = (bounds.size.height / maxLoad) * load[offset][i];

	if (queue == ALL) 
		for (j = 1; j < QUEUES; j++)
			for (i = 1; i < QUEUES; i++)
				if (vector[i] > vector[i - 1]) {
					swap(vector, i, i - 1);
					swap(colors, i, i - 1);
					}
    
	for (i = first; i < last; i++)
		if (vector[i] > 0.0) drawload((float) offset, vector[i], colors[i]);

	return self;
}

- moveArray
{
	int i, j, first = 0, last = QUEUES;
    
	index = (int) bounds.size.width / 2;
    
	if (queue != ALL) last = 1 + (first = queue);
    
	for (i = 0; i < index; i++)
		for (j = first; j < last; j++)
			load[i][j] = load[i + index][j];
    
	--index;
    
	return self;
}

- displayAll:(float) color
{
	int i;

	maxLoad = (float) ceil((double) [self maxOfArray]);

	PSsetgray(color);
	NXRectFill(&bounds);

	if (maxLoad > 1.0) {
		float deltaScale = bounds.size.height / maxLoad;
		for (i = 1; i < (int) maxLoad; i++)
			drawline(rint(deltaScale * (float) i), bounds.size.width, NX_BLACK);
		}
 
	for (i = 0; i <= index; i++) [self drawOneLoad:i];

	return self;
}

- (float) maxOfArray
{
	int i, j, first = 0, last = QUEUES;
	float largest = 0.0;

	if (queue != ALL) last = 1 + (first = queue);

	for (i = 0; i <= index; i++)
		for (j = first; j < last; j++)
			if (load[i][j] > largest) largest = load[i][j];
	    
	return(largest);
}

- setHostName:(const char *) name
{
	hostName = NXCopyStringBuffer(name);
	
	return self;
}

void swap(float array[], int indexA, int indexB)
{
	float temp = array[indexA];
	array[indexA] = array[indexB];
	array[indexB] = temp;
}

void timer(DPSTimedEntry teNumber, double now, id sender)
{
	[sender display];
}

@end
