#import <defaults/defaults.h>
#import <appkit/graphics.h>
#import <appkit/tiff.h>
#import <mach/mach.h>
#import <mach-o/nlist.h>
#import <stdio.h>
#import <stdlib.h>
#import <strings.h>
#import <sys/file.h>

#import "Monitor.h"
#import "Animator.h"

#define LSCALE 1000

#define SECONDS_PER_MINUTE	(60)
#define SECONDS_PER_HOUR	(60 * 60)
#define SECONDS_PER_DAY		(24 * 60 * 60)
#define SECONDS_PER_WEEK	(7 * 24 * 60 * 60)

#define	peek(index,value) \
do { \
	 if (lseek(kmem, (off_t) (index), L_SET) != (off_t) (index)){ \
	       perror("Monitor: cannot seek in /dev/kmem"); \
	       exit(EXIT_FAILURE); \
	  } \
         if (read(kmem, &(value), sizeof(value)) != sizeof(value)) { \
               perror("Monitor: cannot read from /dev/kmem");\
               exit(EXIT_FAILURE); \
	 } \
    } while (0)

#define lscale(nrun)	((float)((float)(nrun)/(float)LSCALE))

static struct nlist namelist[] ={
     {{"_avenrun"},     0, 0, 0, 0},
#define X_AVENRUN	(0)
     {{"_boottime"},    0, 0, 0, 0},
#define X_BOOTTIME	(1)
     {{"_cnt"},         0, 0, 0, 0},
#define X_CNT		(2)
     {{"_cp_time"},     0, 0, 0, 0},
#define X_CP_TIME	(3)
     {{"_dk_seek"},     0, 0, 0, 0},
#define X_DK_SEEK	(4)
     {{"_hostname"},    0, 0, 0, 0},
#define X_HOSTNAME	(5)
     {{"_hostnamelen"}, 0, 0, 0, 0},
#define X_HOSTNAMELEN	(6)
     {{"_ifnet"},       0, 0, 0, 0},
#define X_IFNET		(7)
     {{"_vm_stat"},     0, 0, 0, 0},
#define X_VM_STAT	(8)
     {{""},             0, 0, 0, 0}
};

static char needvar[sizeof(namelist) / sizeof(*namelist)];

static char *viewTitles[] = {
    "1.0 Second Load Average:  %b",
    "Percent CPU Time Used in User Mode:  %b",
    "Percent CPU Time Used in System Mode:  %b",
    "Percent CPU Time Idle:  %b",
    "Percent CPU Time:  User  %b, System  %d, Idle  %w",
    "Number of Context Switches:  %b",
    "Number of System Calls:  %b",
    "Number of Calls to Trap:  %b",
    "Number of Device Interrupts:  %b",
    "Number of Address Faults:  %b",
    "Percent Cache Lookups:  Unused  %b, Successful  %d, Failed  %w",
    "Number of Wired Pages:  %b",
    "Number of Active Pages:  %b",
    "Number of Inactive Pages:  %b",
    "Number of Free Pages:  %b",
    "Number of Memory Pages:  Wired  %b, Active  %d, Inactive  %l, Free  %w",
    "Number of Pages Allocated:  %b",
    "Number of Pages Reclaimed:  %b",
    "Number of Pages Read In:  %b",
    "Number of Pages Written Out:  %b",
    "Number of Fixed Disk Transfers:  %b",
    "Number of Optical Disk Transfers:  %b",
    "Number of Disk Transfers:  Fixed  %b, Optical  %d",
    "Number of Ethernet Input Packets:  %b",
    "Number of Ethernet Output Packets:  %b",
    "Number of Ethernet Packets:  Input  %b, Output  %d",
    "Number of Ethernet Input Errors:  %b",
    "Number of Ethernet Output Errors:  %b",
    "Number of Ethernet Errors:  Input  %b, Output  %d",
    "Number of Ethernet Collisions:  %b"
};

static char *iconTitles[] = {
    "Load Average", "%% User CPU", "%% System CPU", "%% Idle CPU", "%% Total CPU", "Context Switches",
    "System Calls", "Calls to Trap", "Device Interrupts", "Address Faults", "%% Cache Hits", "Wired Pages",
    "Active Pages", "Inactive Pages", "Free Pages", "Total Pages", "Pages Allocated", "Pages Reclaimed",
    "Pages Read", "Pages Written", "Fixed Disk I/O", "Optical Disk I/O", "Disk Transfers", "Input Packets",
    "Output Packets", "Total Packets", "Input Errors", "Output Errors", "Total Errors", "Collisions"
};

static float ceilings[] = {
     -1.0, 100.0, 100.0, 100.0, 100.0,  -1.0,
     -1.0,  -1.0,  -1.0,  -1.0, 100.0,  -1.0,
     -1.0,  -1.0,  -1.0,  -1.0,  -1.0,  -1.0,
     -1.0,  -1.0,  -1.0,  -1.0,  -1.0,  -1.0,
     -1.0,  -1.0,  -1.0,  -1.0,  -1.0,  -1.0
};

static int kmem;

extern int   nlist(const char *, struct nlist *);
extern int   open(const char *, int, ...);
extern off_t lseek(int, off_t, int);
extern int   read(int, void *, int);
extern int   gettimeofday(struct timeval *, struct timezone *);
extern int   gethostname(char *name, int namelen);

@implementation Monitor

/* Factory Methods */

+ initialize
{
    static NXDefaultsVector defaults = {
	{"NXAutoLaunch", "NO"},
	{"ViewSelected", "Total CPU Time,Total Pages,Total Disk Transfers,Ethernet Total Packets"},
	{"UpdateTime",   "1.00"},
	{"IconSelected", "Load Average"},
	{"Display",      "Icon"},
	{NULL,	       NULL}
    };

    NXRegisterDefaults(APPLICATION, defaults);

    if (nlist("/mach", namelist)){
	fprintf(stderr, "Monitor: cannot nlist /mach\n");
	exit(EXIT_FAILURE);
    }
    bzero(needvar, sizeof(needvar));

    if ((kmem = open("/dev/kmem", O_RDONLY)) < 0){
	perror("Monitor: cannot open /dev/kmem");
	exit(EXIT_FAILURE);
    }
    return NULL;
}

- init
{
    int i;
    [super init];

    showview = NO;
    for (i = 0; i < TOTAL_SELECTIONS; i++){
	display[i] = NO;
	subview[i] = NULL;
    }

    showicon = NO_DISPLAY;
    iconview = NULL;

    updatetime = 1.0;

	/* peek(namelist[X_HOSTNAME].n_value, hostname);
	   peek(namelist[X_HOSTNAMELEN].n_value, hostnamelen);
	   */
    gethostname(hostname, MAXHOSTNAMELEN);
    peek(namelist[X_BOOTTIME].n_value, boottime);

    tmentry = nil;
    lastime.tv_sec  = 0;
    lastime.tv_usec = 0;

    bzero(&oldstats, sizeof(oldstats));
    bzero(&newstats, sizeof(newstats));
    return self;
}

- awakeFromNib
{
    [performance setBecomeKeyOnlyIfNeeded:YES];
    [performance setFrameAutosaveName:"Statistics"];
    [[timeForm setEntryType: NX_POSFLOATTYPE] setFloatingPointFormat: NO left: 2 right: 2];
    return self;
}

/* Private Instance Methods */

- createCharts:(int) count
{
    NXRect frame;
    NXRect current;
    float  height;
    int    i;

    [[performance contentView] getFrame: &frame];
    current.origin.x   = 0.0;
    current.size.width = frame.size.width;
    for (i = 0; count; i++, count--){
	height = (int) (frame.size.height / count);
	frame.size.height -= height;

	current.origin.y    = frame.size.height;
	current.size.height = height;

	while (!display[i]){
	    i++;
	}

	if (i == TOTAL_CPU_TIME || i == CACHE_HIT_RATE){
	    subview[i] = [[StripChart allocFromZone:[self zone]] initFrame: &current
								 maximum: ceilings[i]
								 scale: 0.0
								 title: viewTitles[i]];
	}else{
	    if (i == LOAD_AVERAGE){
		subview[i] = [[StripChart allocFromZone:[self zone]] initFrame: &current
								     maximum: ceilings[i]
								     scale: LSCALE
								     title: viewTitles[i]];
	    }else{
		subview[i] = [[StripChart allocFromZone:[self zone]] initFrame: &current
								     maximum: ceilings[i]
								     scale: 0.0
								     title: viewTitles[i]];
	    }
	}
	[[performance contentView] addSubview: subview[i]];

	if (!showview)
	    [subview[i] setAutodisplay: NO];
    }

    [[performance contentView] update];
    return self;
}

static void plot_stats(StripChart *view, int type, Statistics *oldstats, Statistics *newstats, int count, float scale)
{
    float cp_total;
    float hit_rate;

    cp_total = (newstats->cp_time[CP_NICE] - oldstats->cp_time[CP_NICE]) * 0.01;

    switch (type){
    case LOAD_AVERAGE:
	[view plotStats: newstats->avenrun[0] count: count];
	break;

    case USER_CPU_TIME:
	[view plotStats: (newstats->cp_time[CP_USER] - oldstats->cp_time[CP_USER]) / cp_total count: count];
	break;

    case SYSTEM_CPU_TIME:
	[view plotStats: (newstats->cp_time[CP_SYS] - oldstats->cp_time[CP_SYS]) / cp_total count: count];
	break;

    case IDLE_CPU_TIME:
	[view plotStats: (newstats->cp_time[CP_IDLE] - oldstats->cp_time[CP_IDLE]) / cp_total count: count];
	break;

    case TOTAL_CPU_TIME:
	[view plotStats: (newstats->cp_time[CP_USER] - oldstats->cp_time[CP_USER]) / cp_total :
	      (newstats->cp_time[CP_SYS]  - oldstats->cp_time[CP_SYS])  / cp_total :
	      (newstats->cp_time[CP_IDLE] - oldstats->cp_time[CP_IDLE]) / cp_total
	      count: count];
	break;

    case CONTEXT_SWITCHES:
	[view plotStats: (newstats->cnt.v_swtch - oldstats->cnt.v_swtch) * scale count: count];
	break;

    case SYSTEM_CALLS:
	[view plotStats: (newstats->cnt.v_syscall - oldstats->cnt.v_syscall) * scale count: count];
	break;

    case CALLS_TO_TRAP:
	[view plotStats: (newstats->cnt.v_trap - oldstats->cnt.v_trap) * scale count: count];
	break;

    case DEVICE_INTERRUPTS:
	[view plotStats: (newstats->cnt.v_intr - oldstats->cnt.v_intr) * scale count: count];
	break;

    case ADDRESS_FAULTS:
	[view plotStats: (newstats->vm_stat.faults - oldstats->vm_stat.faults) * scale count: count];
	break;

    case CACHE_HIT_RATE:
	if ((hit_rate = newstats->vm_stat.lookups - oldstats->vm_stat.lookups) != 0.0){
	    hit_rate = 100.0 * (float) (newstats->vm_stat.hits - oldstats->vm_stat.hits) / hit_rate;
	    [view plotStats: 0.0: hit_rate: 100.0 - hit_rate count: count];
	}else
	    [view plotStats: 100.0: 0.0: 0.0 count: count];
	break;

    case WIRED_PAGES:
	[view plotStats: newstats->vm_stat.wire_count count: count];
	break;

    case ACTIVE_PAGES:
	[view plotStats: newstats->vm_stat.active_count count: count];
	break;

    case INACTIVE_PAGES:
	[view plotStats: newstats->vm_stat.inactive_count count: count];
	break;

    case FREE_PAGES:
	[view plotStats: newstats->vm_stat.free_count count: count];
	break;

    case TOTAL_PAGES:
	[view plotStats: newstats->vm_stat.wire_count: newstats->vm_stat.active_count: newstats->vm_stat.inactive_count: newstats->vm_stat.free_count count: count];
	break;

    case PAGES_ALLOCATED:
	[view plotStats: (newstats->vm_stat.zero_fill_count - oldstats->vm_stat.zero_fill_count) * scale count: count];
	break;

    case PAGES_RECLAIMED:
	[view plotStats: (newstats->vm_stat.reactivations - oldstats->vm_stat.reactivations) * scale count: count];
	break;

    case PAGES_READ_IN:
	[view plotStats: (newstats->vm_stat.pageins - oldstats->vm_stat.pageins) * scale count: count];
	break;

    case PAGES_WRITTEN_OUT:
	[view plotStats: (newstats->vm_stat.pageouts - oldstats->vm_stat.pageouts) * scale count: count];
	break;

    case FIXED_DISK_TRANSFERS:
	[view plotStats: (newstats->dk_seek[2] - oldstats->dk_seek[2]) * scale count: count];
	break;

    case OPTICAL_DISK_TRANSFERS:
	[view plotStats: (newstats->dk_seek[0] - oldstats->dk_seek[0]) * scale count: count];
	break;

    case TOTAL_DISK_TRANSFERS:
	[view plotStats: (newstats->dk_seek[2] - oldstats->dk_seek[2]) * scale :
	      (newstats->dk_seek[0] - oldstats->dk_seek[0]) * scale
	      count: count];
	break;

    case ETHERNET_INPUT_PACKETS:
	[view plotStats: (newstats->ifnet.if_ipackets - oldstats->ifnet.if_ipackets) * scale count: count];
	break;

    case ETHERNET_OUTPUT_PACKETS:
	[view plotStats: (newstats->ifnet.if_opackets - oldstats->ifnet.if_opackets) * scale count: count];
	break;

    case ETHERNET_TOTAL_PACKETS:
	[view plotStats: (newstats->ifnet.if_ipackets - oldstats->ifnet.if_ipackets) * scale :
	      (newstats->ifnet.if_opackets - oldstats->ifnet.if_opackets) * scale
	      count: count];
	break;

    case ETHERNET_INPUT_ERRORS:
	[view plotStats: (newstats->ifnet.if_ierrors - oldstats->ifnet.if_ierrors) * scale count: count];
	break;

    case ETHERNET_OUTPUT_ERRORS:
	[view plotStats: (newstats->ifnet.if_oerrors - oldstats->ifnet.if_oerrors) * scale count: count];
	break;

    case ETHERNET_TOTAL_ERRORS:
	[view plotStats: (newstats->ifnet.if_ierrors - oldstats->ifnet.if_ierrors) * scale :
	      (newstats->ifnet.if_oerrors - oldstats->ifnet.if_oerrors) * scale
	      count: count];
	break;

    case ETHERNET_COLLISIONS:
	[view plotStats: (newstats->ifnet.if_collisions - oldstats->ifnet.if_collisions) * scale count: count];
    }
}

- _updateStats:sender
{
    struct timeval   curtime;
    struct ifnet    *ifnetp;
    struct ifnet     ifnet;
    char	     title[128];
    struct timeval   timenow;
    int		     value;
    float	     elapsed;
    int		     count;
    int		     i;

    gettimeofday(&curtime, NULL);

    peek(namelist[X_AVENRUN].n_value, newstats.avenrun);

    if (needvar[X_CNT])
	peek(namelist[X_CNT].n_value, newstats.cnt);

    if (needvar[X_CP_TIME]){
	peek(namelist[X_CP_TIME].n_value, newstats.cp_time);
	newstats.cp_time[CP_USER] += newstats.cp_time[CP_NICE];
	newstats.cp_time[CP_NICE]  = newstats.cp_time[CP_USER] + newstats.cp_time[CP_SYS] + newstats.cp_time[CP_IDLE];
    }

    if (needvar[X_DK_SEEK])
	peek(namelist[X_DK_SEEK].n_value, newstats.dk_seek);

    if (needvar[X_IFNET]){
	newstats.ifnet.if_ipackets   = 0;
	newstats.ifnet.if_ierrors    = 0;
	newstats.ifnet.if_opackets   = 0;
	newstats.ifnet.if_oerrors    = 0;
	newstats.ifnet.if_collisions = 0;
	peek(namelist[X_IFNET].n_value, ifnetp);
	while(ifnetp){
	    peek(ifnetp, ifnet);
	    newstats.ifnet.if_ipackets   += ifnet.if_ipackets;
	    newstats.ifnet.if_ierrors    += ifnet.if_ierrors;
	    newstats.ifnet.if_opackets   += ifnet.if_opackets;
	    newstats.ifnet.if_oerrors    += ifnet.if_oerrors;
	    newstats.ifnet.if_collisions += ifnet.if_collisions;
	    ifnetp = ifnet.if_next;
	}
    }

    if (needvar[X_VM_STAT]){
	vm_statistics(task_self(), &newstats.vm_stat);
	    /* peek(namelist[X_VM_STAT].n_value, newstats.vm_stat); */
    }
    sprintf(title, "%s: up", hostname);
    timenow.tv_sec = curtime.tv_sec - boottime.tv_sec;
    if ((timenow.tv_usec = curtime.tv_usec - boottime.tv_usec) > 500000){
	timenow.tv_sec++;
    }else{
	if (timenow.tv_usec < -500000){
	    timenow.tv_sec--;
	}
    }
    if (timenow.tv_sec >= SECONDS_PER_WEEK){
	value = timenow.tv_sec / SECONDS_PER_WEEK;
	timenow.tv_sec -= value * SECONDS_PER_WEEK;
	sprintf(title, "%s %d week%s", title, value, (value > 1) ? "s" : "");
    }
    if (timenow.tv_sec >= SECONDS_PER_DAY){
	value = timenow.tv_sec / SECONDS_PER_DAY;
	timenow.tv_sec -= value * SECONDS_PER_DAY;
	sprintf(title, "%s %d day%s", title, value, (value > 1) ? "s" : "");
    }
    value = timenow.tv_sec / SECONDS_PER_HOUR;
    timenow.tv_sec -= value * SECONDS_PER_HOUR;
    sprintf(title, "%s %2d:%02ld:%02ld  load averages:%6.2f,%6.2f,%6.2f",
	    title, value, timenow.tv_sec / SECONDS_PER_MINUTE, timenow.tv_sec % SECONDS_PER_MINUTE,
	    lscale(newstats.avenrun[0]), lscale(newstats.avenrun[1]), lscale(newstats.avenrun[2]));
    [performance setTitle: title];

    elapsed = curtime.tv_sec - lastime.tv_sec + (curtime.tv_usec - lastime.tv_usec) * 0.000001;
    lastime = curtime;

    count = elapsed / updatetime + 0.5;

    for (i = 0; i < TOTAL_SELECTIONS; i++){
	if (display[i]){
	    plot_stats(subview[i], i, &oldstats, &newstats, count, updatetime / elapsed);
	}
    }

    if (showicon != NO_DISPLAY){
	plot_stats(iconview, showicon, &oldstats, &newstats, count, updatetime / elapsed);
	[miniWindow flushWindow];
    }

	/* bcopy(newstats.avenrun, oldstats.avenrun, sizeof(oldstats.avenrun)); */
    oldstats.cnt = newstats.cnt;
    bcopy(newstats.cp_time, oldstats.cp_time, sizeof(oldstats.cp_time));
    bcopy(newstats.dk_seek, oldstats.dk_seek, sizeof(oldstats.dk_seek));
    oldstats.ifnet = newstats.ifnet;
    oldstats.vm_stat = newstats.vm_stat;
    return self;
}

- writeDefaults:(int) defaults
{
    static char		   update[8];
    static NXDefaultsVector viewdefs = {
	{"ViewSelected", NULL},
	{"UpdateTime",   update},
	{NULL,	         NULL}
    };

    int    i;
    int    length;

    switch (defaults){
    case VIEW_DEFAULTS:
	for (length = i = 0; i < TOTAL_SELECTIONS; i++)
	    if (display[i])
		length += strlen([[viewSelect findCellWithTag: i] title]) + 1;
	if (length){
	    *(viewdefs[0].value = NXZoneMalloc([self zone],length)) = '\0';
	    for (i = 0; i < TOTAL_SELECTIONS; i++)
		if (display[i])
		    if (*viewdefs[0].value)
			sprintf(viewdefs[0].value, "%s,%s", viewdefs[0].value, [[viewSelect findCellWithTag: i] title]);
		    else
			strcpy(viewdefs[0].value, [[viewSelect findCellWithTag: i] title]);
	}else
	    viewdefs[0].value = "None";
	sprintf(update, "%.2f", updatetime);
	NXWriteDefaults(APPLICATION, viewdefs);
	if (length)
	    free(viewdefs[0].value);
	break;

    case ICON_DEFAULTS:
	if (showicon != NO_DISPLAY)
	    NXWriteDefault(APPLICATION, "IconSelected", [[viewSelect findCellWithTag: showicon] title]);
	else
	    NXWriteDefault(APPLICATION, "IconSelected", "None");
	break;

    case DISPLAY_DEFAULTS:
	if (showview && showicon != NO_DISPLAY)
	    NXWriteDefault(APPLICATION, "Display", "View,Icon");
	else
	    if (showview)
		NXWriteDefault(APPLICATION, "Display", "View");
	    else
		if (showicon != NO_DISPLAY)
		    NXWriteDefault(APPLICATION, "Display", "Icon");
		else
		    NXWriteDefault(APPLICATION, "Display", "None");
    }
    return self;
}

/* Public Instance Methods */

- free
{
    int i;

    [tmentry free];

    [NXApp setDelegate: NULL];
    [performance setDelegate: NULL];

    for (i = 0; i < TOTAL_SELECTIONS; i++)
	if (display[i])
	    [[subview[i] removeFromSuperview] free];

    if (iconview)
	[[iconview removeFromSuperview] free];

    [super free];
    return self;
}

static void set_needed(int stat)
{
    switch (stat){
    case CONTEXT_SWITCHES:
    case CALLS_TO_TRAP:
    case SYSTEM_CALLS:
    case DEVICE_INTERRUPTS:
	needvar[X_CNT] = YES;
	break;

    case USER_CPU_TIME:
    case SYSTEM_CPU_TIME:
    case IDLE_CPU_TIME:
    case TOTAL_CPU_TIME:
	needvar[X_CP_TIME] = YES;
	break;

    case FIXED_DISK_TRANSFERS:
    case OPTICAL_DISK_TRANSFERS:
    case TOTAL_DISK_TRANSFERS:
	needvar[X_DK_SEEK] = YES;
	break;

    case ETHERNET_INPUT_PACKETS:
    case ETHERNET_OUTPUT_PACKETS:
    case ETHERNET_TOTAL_PACKETS:
    case ETHERNET_INPUT_ERRORS:
    case ETHERNET_OUTPUT_ERRORS:
    case ETHERNET_TOTAL_ERRORS:
    case ETHERNET_COLLISIONS:
	needvar[X_IFNET] = YES;
	break;

    case FREE_PAGES:
    case ACTIVE_PAGES:
    case INACTIVE_PAGES:
    case WIRED_PAGES:
    case TOTAL_PAGES:
    case PAGES_ALLOCATED:
    case PAGES_RECLAIMED:
    case PAGES_READ_IN:
    case PAGES_WRITTEN_OUT:
    case ADDRESS_FAULTS:
    case CACHE_HIT_RATE:
	needvar[X_VM_STAT] = YES;
    }
}

static void init_stats(Statistics *stats, struct timeval *lastime)
{
    struct ifnet *ifnetp;
    struct ifnet  ifnet;

    gettimeofday(lastime, NULL);

    peek(namelist[X_AVENRUN].n_value, stats->avenrun);
    peek(namelist[X_CNT].n_value, stats->cnt);
    peek(namelist[X_CP_TIME].n_value, stats->cp_time);
    stats->cp_time[CP_USER] += stats->cp_time[CP_NICE];
    stats->cp_time[CP_NICE]  = stats->cp_time[CP_USER] + stats->cp_time[CP_SYS] + stats->cp_time[CP_IDLE];
    peek(namelist[X_DK_SEEK].n_value, stats->dk_seek);
    stats->ifnet.if_ipackets   = 0;
    stats->ifnet.if_ierrors    = 0;
    stats->ifnet.if_opackets   = 0;
    stats->ifnet.if_oerrors    = 0;
    stats->ifnet.if_collisions = 0;
    peek(namelist[X_IFNET].n_value, ifnetp);
    while (ifnetp){
	peek(ifnetp, ifnet);
	stats->ifnet.if_ipackets   += ifnet.if_ipackets;
	stats->ifnet.if_ierrors    += ifnet.if_ierrors;
	stats->ifnet.if_opackets   += ifnet.if_opackets;
	stats->ifnet.if_oerrors    += ifnet.if_oerrors;
	stats->ifnet.if_collisions += ifnet.if_collisions;
	ifnetp = ifnet.if_next;
    }
	/* peek(namelist[X_VM_STAT].n_value, stats->vm_stat); */
    vm_statistics(task_self(), &(stats->vm_stat));
}

- appDidInit:sender
{
    View       *content;
    int	       count;
    const char *option;
    const char *optend;
    int	       length;
    const char *title;
    int	       i;


    content = [(miniWindow = [NXApp appIcon]) contentView];
    count = 0;
    if (option = NXGetDefaultValue(APPLICATION, "ViewSelected"))
	while (*option){
	    for (optend = option; *optend && *optend != ','; optend++)
		;
	    length = optend - option;

	    for (i = 0; i < TOTAL_SELECTIONS; i++)
		if (strlen(title = [[viewSelect findCellWithTag: i] title]) == length && !strncmp(title, option, length)){
		    set_needed(i);
		    display[i] = YES;

		    count++;
		    break;
		}

	    if (*(option = optend))
		option++;
	}

    if (count)
	[self createCharts: count];

    if (option = NXGetDefaultValue(APPLICATION, "IconSelected"))
	for (i = 0; i < TOTAL_SELECTIONS; i++)
	    if (!strcmp(option, [[viewSelect findCellWithTag: i] title])){
		set_needed(i);
		showicon = i;
		break;
	    }

    if (option = NXGetDefaultValue(APPLICATION, "Display"))
	while (*option){
	    for (optend = option; *optend && *optend != ','; optend++)
		;

	    if (optend - option == 4)
		if (!strncmp("View", option, 4))
		    [self showView: sender];
		else
		    if (!strncmp("Icon", option, 4))
			[self showIcon: sender];

	    if (*(option = optend))
		option++;
	}

    updatetime = atof(NXGetDefaultValue(APPLICATION, "UpdateTime"));

    if (!strcmp(NXGetDefaultValue(APPLICATION, "NXAutoLaunch"), "YES"))
	[NXApp deactivateSelf];

    init_stats(&oldstats, &lastime);

    tmentry = [[Animator allocFromZone:[self zone]] initChronon:updatetime
						    adaptation:2.00
						    target:self
						    action:@selector(_updateStats:)
						    autoStart:YES
						    eventMask:0];
    return self;
}

- appDidBecomeActive:sender
{
    if (showview)
	[performance orderFront: sender];
    return self;
}

- windowWillClose:sender
{
    int i;

    showview = NO;

    for (i = 0; i < TOTAL_SELECTIONS; i++)
	if (display[i])
	    [subview[i] setAutodisplay: NO];

    [self writeDefaults: DISPLAY_DEFAULTS];
    return self;
}

- windowDidResize:sender
{
    int count;
    int i;

    for (count = i = 0; i < TOTAL_SELECTIONS; i++)
	if (display[i])
	    {
		[[subview[i] removeFromSuperview] free];
		subview[i] = NULL;
		count++;
	    }

    if (count)
	[self createCharts: count];
    return self;
}

- selectView:sender
{
    int i;

    for (i = 0; i < TOTAL_SELECTIONS; i++)
	[[viewSelect findCellWithTag: i] setIntValue: display[i]];
    [timeForm   setFloatValue: updatetime];
    [timeSlider setFloatValue: updatetime];

    [selection makeKeyAndOrderFront: sender];
    return self;
}

- endSelect:sender
{
    int count;
    int i;

    [selection orderOut: sender];

    if (sender == okButton){
	[tmentry free];

	for (i = 0; i < TOTAL_SELECTIONS; i++)
	    if (display[i]){
		[[subview[i] removeFromSuperview] free];
		subview[i] = NULL;
	    }

	bzero(needvar, sizeof(needvar));
	for (count = i = 0; i < TOTAL_SELECTIONS; i++)
	    if (display[i] = [[viewSelect findCellWithTag: i] intValue]){
		set_needed(i);
		count++;
	    }
	if (showicon != NO_DISPLAY)
	    set_needed(showicon);

	if (count)
	    [self createCharts: count];

	if (showicon != NO_DISPLAY)
	    [self showIcon: sender];

	updatetime = [timeForm floatValue];

	init_stats(&oldstats, &lastime);

	tmentry = [[Animator allocFromZone:[self zone]] initChronon:updatetime
							adaptation:2.00
							target:self
							action:@selector(_updateStats:)
							autoStart:YES
							eventMask:0];

	[self writeDefaults: VIEW_DEFAULTS];
    }
    return self;
}

- selectIcon:sender
{
    NXRect frame;
    int	  lasticon;
    View  *icon;
    int	  i;

    bzero(needvar, sizeof(needvar));
    for (i = 0; i < TOTAL_SELECTIONS; i++)
	if (display[i])
	    set_needed(i);

    lasticon = showicon;
    if ((showicon = [[sender selectedCell] tag]) != NO_DISPLAY){
	set_needed(showicon);
	[self showIcon: sender];

	if (showicon != lasticon)
	    [self writeDefaults: ICON_DEFAULTS];
	if (lasticon == NO_DISPLAY)
	    [self writeDefaults: DISPLAY_DEFAULTS];
    }else
	if (iconview){
	    NXPoint point = {0.0, 0.0};

	    [[iconview removeFromSuperview] free];

	    [(icon = [miniWindow contentView]) getBounds: &frame];
	    [icon lockFocus];
	    [[NXImage findImageNamed:"NXAppTile"] composite:NX_SOVER toPoint:&point];
	    point.x = point.y = 8.0;
	    [[NXImage findImageNamed:"app"] composite:NX_SOVER toPoint:&point];
	    [icon unlockFocus];
	    [miniWindow flushWindow];

	    iconview = NULL;

	    [self writeDefaults: ICON_DEFAULTS];
	    [self writeDefaults: DISPLAY_DEFAULTS];
	}
    return self;
}

- showView:sender
{
    int i;

    [performance orderFront: sender];

    showview = YES;

    for (i = 0; i < TOTAL_SELECTIONS; i++)
	if (display[i])
	    [subview[i] setAutodisplay: YES];

    [self writeDefaults: DISPLAY_DEFAULTS];
    return self;
}

- showIcon:sender
{
    NXRect      frame;

    if (iconview)
	[[iconview removeFromSuperview] free];

    [[miniWindow contentView] getBounds: &frame];
    if (frame.size.width == 64.0 && frame.size.height == 64.0){
	frame.origin.x    += 3.0;
	frame.origin.y    += 3.0;
	frame.size.width  -= 6.0;
	frame.size.height -= 6.0;
    }

    if (showicon == TOTAL_CPU_TIME || showicon == CACHE_HIT_RATE)
	iconview = [[StripChart allocFromZone:[self zone]] initFrame: &frame
							   maximum: ceilings[showicon]
							   scale: 0.0
							   title: iconTitles[showicon]];
    else
	if (showicon == LOAD_AVERAGE)
	    iconview = [[StripChart allocFromZone:[self zone]] initFrame: &frame
							       maximum: ceilings[showicon]
							       scale: LSCALE
							       title: iconTitles[showicon]];
	else
	    iconview = [[StripChart allocFromZone:[self zone]] initFrame: &frame
							       maximum: ceilings[showicon]
							       scale: 0.0
							       title: iconTitles[showicon]];
    [[miniWindow contentView] addSubview: iconview];

    [iconview display];
    return self;
}

@end
