#import <stdio.h>
#import <stdlib.h>
#import <appkit/Font.h>
#import <dpsclient/psops.h>
#import <math.h>
#import <string.h>

#import "Monitorwraps.h"
#import "StripChart.h"

@implementation StripChart

- initFrame:(const NXRect *) frameRect maximum:(float) maxval scale:(float) scale title:(char *) string
{
    [super initFrame: frameRect];
    [[self allocateGState] notifyToInitGState: YES];
    [self setOpaque: YES];

    ceiling = maxval;
    factor  = scale;
    title   = string;

    inited = 0;

    xoffset = 0.0;
    yoffset = 0.0;

    width   = 0.0;
    height  = 0.0;

    count = (int) frameRect->size.width;
    if (!(_values = (float *) NXZoneMalloc([self zone],4 * count * sizeof(float)))){
	fprintf(stderr, "Monitor: insufficient memory for display\n");
	exit(1);
    }
    bzero(_values, 4 * count * sizeof(float));
    index  = 0;
    number = 0;

    if (!(upath = (float *) NXZoneMalloc([self zone],4 * count * sizeof(float)))){
	fprintf(stderr, "Monitor: insufficient memory for display\n");
	exit(1);
    }
    nupath = 0;
    if (!(uops = (char *) NXZoneMalloc([self zone],2 * count))){
	fprintf(stderr, "Monitor: insufficient memory for display\n");
	exit(1);
    }
    nuops = 0;
    return(self);
}

/* Private Instance Methods */

- (BOOL)setMaximum:(float) maxval
{
    if (ceiling >= 0.0){
	maxval = (int) maxval + 1;
	if (maxval > ceiling)
	    maxval = ceiling;
    }else
	if (number < 4)
	    maxval = (int) (1.25 * maxval) + 1;
	else
	    maxval = (int) maxval;

    if (maximum != maxval){
	maximum = maxval;
	return(YES);
    }else
	return(NO);
}

- (BOOL)scrollStats:(float) maxval
{
    int	 shift;
    int	 i;
    float total;

    index -= (shift = index >> 1);
    bcopy(&values(shift, 0), _values, 4 * index * sizeof(float));
    bzero(&values(index, 0), 4 * shift * sizeof(float));

    switch (number){
    case 4:
	for (i = 0; i < index; i++){
	    total = values(i, 0) + values(i, 1) + values(i, 2) + values(i, 3);
	    if (total > maxval)
		maxval = total;
	}
	break;

    case 3:
	for (i = 0; i < index; i++){
	    total = values(i, 0) + values(i, 1) + values(i, 2);
	    if (total > maxval)
		maxval = total;
	}
	break;

    case 2:
	for (i = 0; i < index; i++){
	    total = values(i, 0) + values(i, 1);
	    if (total > maxval)
		maxval = total;
	}
	break;

    case 1:
	for (i = 0; i < index; i++)
	    if (values(i, 0) > maxval)
		maxval = values(i, 0);
    }

    [self setMaximum: maxval];
    return(YES);
}

- userPath:(int) stat scale:(float) scale color:(NXColor ) color
{
    char  drawing;
    float point = 0.0;
    float saved = 0.0;
    int	 start = 0;
    int	 i;

    for (drawing = NO, nupath = nuops = i = 0; i <= index; i++){
	switch (stat){
	case 2:
	    point = rint((values(i, 2) + values(i, 1) + values(i, 0)) * scale);
	    break;

	case 1:
	    point = rint((values(i, 1) + values(i, 0)) * scale);
	    break;

	case 0:
	    point = rint(values(i, 0) * scale);
	}

	if (drawing){
	    if (point == 0.0 || i == index){
		upath[nupath++] = i;
		upath[nupath++] = saved;
		uops[nuops++]   = dps_lineto;

		upath[nupath++] = i;
		upath[nupath++] = 0.0;
		uops[nuops++]   = dps_lineto;

		upath[nupath++] = start;
		upath[nupath++] = 0.0;
		uops[nuops++]   = dps_lineto;

		uops[nuops++] = dps_closepath;

		drawing = NO;
	    }else
		if (point != saved){
		    upath[nupath++] = i;
		    upath[nupath++] = saved;
		    uops[nuops++]   = dps_lineto;

		    upath[nupath++] = i;
		    upath[nupath++] = point;
		    uops[nuops++]   = dps_lineto;

		    saved = point;
		}
	}else
	    if (i < index && point > 0.0){
		upath[nupath++] = i;
		upath[nupath++] = point;
		uops[nuops++]   = dps_moveto;

		start = i;
		saved = point;

		drawing = YES;
	    }
    }

    if (nuops){
	NXSetColor(color);
	DPSDoUserPath(upath, nupath, dps_float, uops, nuops, ubbox, dps_ufill);
    }
    return(self);
}

/* Public Instance Methods */

- free
{
    [self freeGState];

    free(_values);
    free(upath);
    free(uops);

    [super free];
    return(self);
}

- initGState
{
    PSsetgray(0.0);
    [[Font newFont: "Screen-Helvetica" size: 8.0 matrix: NX_IDENTITYMATRIX] set];
    return(self);
}

- drawSelf:(const NXRect *) rects :(int) rectCount
{
    float  scale;
    char   value[8];
    float  vwidth;
    float  vheight;

    if (!inited){
	inited = 1;

	NXSetColor(NX_COLORLTGRAY);
	fill_strip(0.0, 0.0, bounds.size.width, bounds.size.height);

	string_size("0000", &xoffset, &yoffset);
	xoffset += 2.0;
	yoffset += 5.0;

	draw_frame(0.5, 3.5, title, xoffset + 0.5, yoffset + 1.5, bounds.size.width - 0.5, bounds.size.height - 0.5);

	width  = bounds.size.width  - xoffset - 4.0;
	height = bounds.size.height - yoffset - 5.0;

	count = (int) width;

	[self translate: xoffset + 2.0: yoffset + 3.0];

	ubbox[0] = 0.0;
	ubbox[1] = 0.0;
	ubbox[2] = width;
	ubbox[3] = height;
    }

    if (maximum > 0.0){
      NXSetColor(NX_COLORLTGRAY);
      fill_strip(-xoffset - 2.0, 0.0, xoffset, bounds.size.height - yoffset - 3.0);
      if (factor != 0.0){
	sprintf(value, "%.2f", maximum / factor);
	if (value[1] != '.')
	  sprintf(value, "%.1f", maximum / factor);
      }else
	sprintf(value, "%d", (int) rint(maximum));
      string_size(value, &vwidth, &vheight);
      draw_limits(-vwidth - 3.0, height - vheight - 1.0, value);
    }

    NXSetColor(NX_COLORWHITE);
    fill_strip(0.0, 0.0, width, height);

    if (index){
	scale  = (maximum > 0.0) ? height / maximum : 1.0;

	switch (number){
	case 4:
	    [self userPath: 2 scale: scale color: NX_COLORBLUE /* NX_COLORLTGRAY */];

	case 3: case 2:
	    [self userPath: 1 scale: scale color: NX_COLORGREEN /* NX_COLORDKGRAY */];

	case 1:
	    [self userPath: 0 scale: scale color: NX_COLORRED /* NX_COLORBLACK */];
	}
    }
    return(self);
}

- plotStats:(float) black count:(int) samples
{
    float scale;
    float point;
    int	 i;

    if (number < 1)
	number = 1;

    if ((index + samples > count && [self scrollStats: black]) ||
	(black > maximum && [self setMaximum: black]) )
	[self update];

    for (i = 0; i < samples; i++)
	values(index + i, 0) = black;

    scale = (maximum > 0.0) ? height / maximum : 1.0;
    point = rint(black * scale);

    if (point > 0.0)
	if ([self isAutodisplay]){
	    [self lockFocus];
	    draw_1_stat(index, samples, point);
	    [self unlockFocus];
	}else
	    [self setNeedsDisplay: YES];

    index += samples;
    return(self);
}

- plotStats:(float) black :(float) dgray count:(int) samples
{
    float total;
    float scale;
    float points[2];
    int	 i;

    if (number < 2)
	number = 2;

    total = black + dgray;
    if ((index + samples > count && [self scrollStats: total]) ||
	(total > maximum && [self setMaximum: total]) )
	[self update];

    for (i = 0; i < samples; i++){
	values(index + i, 0) = black;
	values(index + i, 1) = dgray;
    }

    scale = (maximum > 0.0) ? height / maximum : 1.0;
    points[0] = rint(black * scale);
    points[1] = rint(total * scale) - points[0];

    if (points[0] + points[1] > 0.0)
	if ([self isAutodisplay]){
	    [self lockFocus];
	    draw_2_stats(index, samples, points[0], points[1]);
	    [self unlockFocus];
	}
	else
	    [self setNeedsDisplay: YES];

    index += samples;
    return(self);
}

- plotStats:(float) black :(float) dgray :(float) white count:(int) samples
{
    float total;
    float scale;
    float points[2];
    int	 i;

    if (number < 3)
	number = 3;

    total = black + dgray + white;
    if ((index + samples > count && [self scrollStats: total]) ||
	(total > maximum && [self setMaximum: total]) )
	[self update];

    for (i = 0; i < samples; i++){
	values(index + i, 0) = black;
	values(index + i, 1) = dgray;
	values(index + i, 2) = white;
    }

    scale = (maximum > 0.0) ? height / maximum : 1.0;
    points[0] = rint(black * scale);
    points[1] = rint((black + dgray) * scale) - points[0];

    if (points[0] + points[1] > 0.0)
	if ([self isAutodisplay]){
	    [self lockFocus];
	    draw_2_stats(index, samples, points[0], points[1]);
	    [self unlockFocus];
	}else
	    [self setNeedsDisplay: YES];

    index += samples;
    return(self);
}

- plotStats:(float) black :(float) dgray :(float) lgray :(float) white count:(int) samples
{
    float total;
    float scale;
    float points[3];
    int	 i;

    if (number < 4)
	number = 4;

    total = black + dgray + lgray + white;
    if ((index + samples > count && [self scrollStats: total]) ||
	(total > maximum && [self setMaximum: total]) )
	[self update];

    for (i = 0; i < samples; i++){
	values(index + i, 0) = black;
	values(index + i, 1) = dgray;
	values(index + i, 2) = lgray;
	values(index + i, 3) = white;
    }

    scale = (maximum > 0.0) ? height / maximum : 1.0;
    points[0] = rint(black * scale);
    points[1] = rint((black + dgray) * scale) - points[0];
    points[2] = rint((black + dgray + lgray) * scale) - points[1] - points[0];

    if (points[0] + points[1] + points[2] > 0.0)
	if ([self isAutodisplay]){
	    [self lockFocus];
	    draw_3_stats(index, samples, points[0], points[1], points[2]);
	    [self unlockFocus];
	}else
	    [self setNeedsDisplay: YES];

    index += samples;
    return(self);
}

@end
