#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 MAXERRORS 3

extern int loadave();

void swap(void **array, unsigned int indexA, unsigned 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;
{
	unsigned int i;

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

	hostName = NULL;
	
	for(i = 0; i < QUEUES; i++) which[i] = YES;

	index = -1;
	
	nErrors = MAXERRORS - 1; /* Forces view white if initial call fails. */
	
	timedEntry = NULL;
	
	maxLoad = 0.0;

	shift = getFloatDefault("ShiftDisplay");
	
	update = getFloatDefault("LocalUpdateSeconds");
	
	divisionColor = getColorDefault("DivisionColor");
	backgroundColor = getColorDefault("BackgroundColor");
	quiescentColor = getColorDefault("QuiescentColor");
	
	queueColors[Q1MIN] = getColorDefault("Q1Color");
	queueColors[Q5MIN] = getColorDefault("Q5Color");
	queueColors[Q15MIN] = getColorDefault("Q15Color");

	return self;
}

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

	index = ++index % width;

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

	nErrors = 0;

	return self;
}

- free
{
	[self freeGState];

	if (timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);
	
	if (hostName != NULL) free((void *) hostName);
	
	return [super free];
}

- startTimer
{
	timedEntry = DPSAddTimedEntry(update, (DPSTimedEntryProc) &timer, self, NX_BASETHRESHOLD);
	
	return self;
}

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

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

- drawOneLoad:(int) offset 
{
	float vector[QUEUES];
	unsigned int i, j, order[QUEUES];
	
	for (i = 0; i < QUEUES; i++) vector[order[i] = i] = (bounds.size.height / maxLoad) * load[offset][i];

	for (j = 1; j < QUEUES; j++)
		for (i = 1; i < QUEUES; i++)
			if (vector[order[i]] > vector[order[i - 1]]) swap((void **) order, i, i - 1);
    
	for (i = 0; i < QUEUES; i++)
		if (which[order[i]] && vector[order[i]] > 0.0) {
			NXSetColor(queueColors[order[i]]);
			drawload((float) offset, vector[order[i]]);
			}

	return self;
}

- moveArray
{
	unsigned int i, j, increment;
    
	index = (unsigned int) (bounds.size.width - (bounds.size.width * shift));
	
	increment = (unsigned int) (bounds.size.width - index);
    
	for (i = 0; i < index; i++)
		for (j = 0; j < QUEUES; j++)
			load[i][j] = load[i + increment][j];
    
	--index;
    
	return self;
}

- displayAll
{
	unsigned int i;
	
	NXSetColor((nErrors >= MAXERRORS) ? quiescentColor : backgroundColor);
	NXRectFill(&bounds);
	
	if (index < 0) return self;
	
	maxLoad = (float) ceil((double) [self maxOfArray]);

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

	return self;
}

- (float) maxOfArray
{
	unsigned int i, j;
	float largest = 0.0;

	for (i = 0; i <= index; i++)
		for (j = 0; j < QUEUES; j++)
			if (which[j] && load[i][j] > largest) largest = load[i][j];
	    
	return(largest);
}

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

- setWhich:(BOOL *) array
{
	unsigned int i;
	
	for(i = 0; i < QUEUES; i++) which[i] = array[i];
	
	return self;
}

- (float) shift { return shift; }
- setShift:(float) value { shift = value; return self; }

- (float) update { return update; }
- setUpdate:(float) value { update = value; return self; }

- setDivisionColor:(NXColor) color { divisionColor = color; return self; }

- setBackgroundColor:(NXColor) color { backgroundColor = color; return self; }

- setQuiescentColor:(NXColor) color { quiescentColor = color; return self; }

- setQueueColors:(NXColor *) colors
{
	unsigned int i;
	
	for (i = 0; i < QUEUES; i++) queueColors[i] = colors[i];
	
	return self;
}

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

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

@end
