/*   Modified by Andrew Loewenstern (andrew@cubetech.com)   <drue>       */
/*   7/15/92 for 3.0 compatibility.  Mainly changed headers and such...  */

/*   Also changed by Garance Drosehn (gad@eclipse.its.rpi.edu)  */
/*   on June 18, 1995, mainly trying to get things to work a    */
/*   little better on NS/Intel (and probably HP and SPARC)      */

/*
  Original code:
  Copyright 1991 Scott Hess.  Permission to use, copy, modify, and
  distribute this software and its documentation for any purpose
  and without fee is hereby granted, provided that this copyright
  notice appear in all copies.  The copyright notice need not appear
  on binary-only distributions - just in source code.
  
  Scott Hess makes no representations about the suitability of this
  software for any purpose.  It is provided "as is" without express
  or implied warranty.
*/

/* Generated by Interface Builder */

#import "Monitor.h"
#import "AppDelegate.h"
#import "wraps.h"

const int elementsToDisplay = 55;	// we display the last <x> events
const int elementsToSave = 200;		// but keep around some extras...

extern int table(int,int,void *,int,int);	// We need to stick this in because the table call is undocumented.

/* Initial testing of an interesting possibility */
// #define DO_NETPNI 1
// #define DO_NETPPP 1

@implementation Monitor

- initFrame:(NXRect *)frameRect
{
    int tIndex, tableRes;
    /*************************************************************************
     *    Instantiate the NXImages needed for compositing onto the icon      *
     *************************************************************************/
    cpuTiff = [NXImage findImageNamed:"CPU.tiff"];
    vmTiff = [NXImage findImageNamed:"VM.tiff"];
    memTiff = [NXImage findImageNamed:"MEM.tiff"];
    diskTiff = [NXImage findImageNamed:"DISK.tiff"];
    net_en0Tiff = [NXImage findImageNamed: "NET.tiff" ];
#if defined(DO_NETPNI)
    net_pniTiff = [NXImage findImageNamed: "NET-pni.tiff" ];
#endif
#if defined(DO_NETPPP)
    net_pppTiff = [NXImage findImageNamed: "NET-ppp.tiff" ];
#endif
    networkTiff = net_en0Tiff;

    /*************************************************************************
     *    Instantiate the Storage Object needed to keep the statistics       *
     *************************************************************************/
    stats = [Storage alloc];
    [stats initCount:0 
           elementSize:sizeof(struct perfData) 
           description:PERFDATA_DESC];
    
    [super initFrame:frameRect];   

    infoType = -1;

    /*************************************************************************
     *    Need to find out what table() calls should be used on this system  *
     *************************************************************************/
    memset(&iostat, 0, sizeof(iostat));
    memset(&netstat, 0, sizeof(netstat));
    memset(&diskstat, 0, sizeof(diskstat));
    table(TBL_IOINFO, 0, &iostat, 1, sizeof(iostat));
    
    disk_Index = -1;
    for (tIndex = 0; tIndex<iostat.io_ndrive; tIndex++) {
	tableRes = table(TBL_DISKINFO, tIndex, &diskstat, 1, sizeof(diskstat));
	if (tableRes > 0) {
#ifdef DEBUG
	    printf("%s: found disk #%d = %s\n", [NXApp appName], tIndex,
		    diskstat.di_name);
#endif
	    if ( disk_Index<0 ) {
		    disk_Index = tIndex;
	    }
	}
    }

    /* Network interface is checked for in netstatAvailable, because
     * it can change dynamically (in some situations) */
    [self netstatAvailable];

    return self;
}

- drawSelf:(NXRect *)rects :(int)rectCount
{
unsigned int total;
struct perfData *old;
	
/*   Note: I should have just translated the coordinates instead of adding 8
 *   to everything, but I didn't think of it at the time. Change it later.
 */
    switch ( infoType )
        {
        case CPU_tag:
            drawCPU(current.system / 2 + 8,
                    ( current.user + current.system ) / 2 + 8,
                    ( current.user + current.system + current.nice) / 2 + 8);
            break;
        case MEMORY_tag:
            total = current.free + current.active + current.inactive + current.wired;
            drawMem(current.wired * 50 / total + 8,
                   ( current.wired + current.inactive ) * 50 / total + 8,
                   ( current.wired + current.inactive + current.active ) * 50 / total + 8);
            break;
        case DISK_tag:
            drawDisk(MIN(current.disk,50) + 8,
                     MIN(current.pagein + current.pageout,50) + 8,
                     MIN(current.pageout,50) + 8);
            break;
        case NETWORK_tag:
            if ( (old = (struct perfData *)[stats elementAt:[stats count]-2]) == NULL )
                drawNet(8,8,8,MIN(current.netin,50)+8,MIN(current.netout,50)+8,MIN(current.netcollision,50)+8);
            else
                drawNet(MIN(old->netin,50)+8,MIN(old->netout,50)+8,MIN(old->netcollision,50)+8,
                        MIN(current.netin,50)+8,MIN(current.netout,50)+8,MIN(current.netcollision,50)+8);
            break;
        }
    return self;
}


- flip:sender
{
static NXPoint aPoint = {2,0};
struct perfData *data;
struct perfData *old;
int i;
int type;
int count;
int total;

    if ( (type = [[sender selectedCell] tag]) == infoType )
        return self;   
    [self lockFocus];
    count = [stats count];
    switch ( type )
        {
        case CPU_tag:
            infoType = CPU_tag;
            [cpuTiff composite:NX_COPY toPoint:&aPoint];
            NXWriteDefault([NXApp appName],"InfoType","0");
            for ( i = MAX(count - elementsToDisplay,0) ; i < count ; i++ )
                {
                data = (struct perfData *)[stats elementAt:i];
                drawCPU(data->system / 2 + 8,
                        ( data->user + data->system ) / 2 + 8,
                        ( data->user + data->system + data->nice) / 2 + 8);
                }
            break;
        case MEMORY_tag:
            infoType = MEMORY_tag;
            [memTiff composite:NX_COPY toPoint:&aPoint];
            NXWriteDefault([NXApp appName],"InfoType","1");
            for ( i = MAX(count - elementsToDisplay,0) ; i < count ; i++ )
                {
                data = (struct perfData *)[stats elementAt:i];
                total = data->free + data->active + data->inactive + data->wired;                
                drawMem(data->wired * 50 / total + 8,
                       ( data->wired + data->inactive ) * 50 / total + 8,
                       ( data->wired + data->inactive + data->active ) * 50 / total + 8);
                }
            break;
        case DISK_tag:
            infoType = DISK_tag;
            [diskTiff composite:NX_COPY toPoint:&aPoint];
            NXWriteDefault([NXApp appName],"InfoType","2");
            for ( i = MAX(count - elementsToDisplay,0) ; i < count ; i++ )
                {
                data = (struct perfData *)[stats elementAt:i];
                drawDisk(MIN(data->disk,50) + 8,
                         MIN(data->pagein + data->pageout,50) + 8,
                         MIN(data->pageout,50) + 8);
                }
            break;
        case NETWORK_tag:
            infoType = NETWORK_tag;
            [networkTiff composite:NX_COPY toPoint:&aPoint];
            NXWriteDefault([NXApp appName],"InfoType","3");
            for ( i = MAX(count - elementsToDisplay,1) ; i < count ; i++ )
                {
                old = (struct perfData *)[stats elementAt:i-1];
                data = (struct perfData *)[stats elementAt:i];
                drawNet(MIN(old->netin,50)+8,
		        MIN(old->netout,50)+8,
			MIN(old->netcollision,50)+8,
                        MIN(data->netin,50)+8,
			MIN(data->netout,50)+8,
			MIN(data->netcollision,50)+8);
                }
            break;
        }
    [self unlockFocus];
    [window flushWindow];

    return self;
}

- step
{
long cp_time[CPUSTATES];
long total;
long temp;
int netstatScale;
vm_statistics_data_t vmstat;

    /*************************************************************************
     *    Get the data                                                       *
     *************************************************************************/
    memset(&tc, 0, sizeof(tc));
    table(TBL_CPUINFO, 0, &tc, 1, sizeof(tc));
    bcopy(tc.ci_cptime, cp_time, sizeof(cp_time));
    vm_statistics(task_self(),&vmstat);
    if (netInterface_Index>=0) {
	memset(&netstat, 0, sizeof(netstat));
	table(TBL_NETINFO, netInterface_Index, &netstat, 1, sizeof(netstat));
    }
    if (disk_Index>=0) {
	memset(&diskstat, 0, sizeof(diskstat));
	table(TBL_DISKINFO, disk_Index, &diskstat, 1, sizeof(diskstat));
    }

    /*************************************************************************
     *    Process the data into the proper form.                             *
     *************************************************************************/
    total = cp_time[CP_SYS] + cp_time[CP_USER] + cp_time[CP_NICE]
	  + cp_time[CP_IDLE];
    temp = total;
    
		if((total - oldTotal) > 0)  /* this was done to fix the div0 error.  It */
			total -= oldTotal;        /* works, but barely...  - drue  */
    oldTotal = temp;
    
    current.system = ( cp_time[CP_SYS] - oldTimes[CP_SYS] ) * 100 / total;
    current.user = ( cp_time[CP_USER] - oldTimes[CP_USER] ) * 100 / total;
    current.nice = ( cp_time[CP_NICE] - oldTimes[CP_NICE] ) * 100 / total;
    bcopy(cp_time,oldTimes,sizeof(cp_time));

    current.free = vmstat.free_count;
    current.active = vmstat.active_count;
    current.inactive = vmstat.inactive_count;
    current.wired = vmstat.wire_count;

    netstatScale = 1;
    if ( (networkTiff == net_pniTiff) || (networkTiff == net_pppTiff) ) {
	netstatScale = 5;
    }
    current.netin = ( netstat.ni_ipackets - oldin ) * netstatScale
	    / pollInterval;
    current.netout = ( netstat.ni_opackets - oldout ) * netstatScale
	    / pollInterval;
    current.netcollision = ( netstat.ni_collisions - oldcollision )
	    * netstatScale / pollInterval;
    oldin = netstat.ni_ipackets;
    oldout = netstat.ni_opackets;
    oldcollision = netstat.ni_collisions;

    current.disk = ( diskstat.di_xfer - olddisk) / pollInterval;
    current.pagein = ( vmstat.pageins - oldpagein ) / pollInterval;
    current.pageout = ( vmstat.pageouts - oldpageout ) / pollInterval;
    olddisk = diskstat.di_xfer;
    oldpagein = vmstat.pageins;
    oldpageout = vmstat.pageouts;

    [self display];

    if ( [stats count] > elementsToSave ) {
	[stats removeElementAt:0];
    }
    
    [stats addElement:&current];
    return self;
}   

- (BOOL)diskstatAvailable
{
    return (disk_Index >= 0);
}

/* this should probably be even more clever, checking to see what
 * network interfaces are currently up...  Note that this complicates
 * the history situation (previously stored events), as the network
 * interface can change on the fly.  That's one reason it's only an
 * compile-time option at the moment.
 */
- (BOOL)netstatAvailable
{
    int tIndex, tableRes;
    netInterface_Index = -1;

    /* note that the number of network interfaces can change while
     * this program is running (if the user loads up PNI slip, for
     * instance)
     */
    memset(&iostat, 0, sizeof(iostat));
    table(TBL_IOINFO, 0, &iostat, 1, sizeof(iostat));

#ifdef DO_NETPNI
    for (tIndex = 0; tIndex<iostat.io_nif; tIndex++) {
	memset(&netstat, 0, sizeof(netstat));
	tableRes = table(TBL_NETINFO, tIndex, &netstat, 1, sizeof(netstat));
	if (tableRes > 0) {
	    if (   (netInterface_Index<0)
		&& !strncmp(netstat.ni_name, "pni", 3 )) {
#  ifdef DEBUG
		printf("%s: using network interface #%d = %s\n",
			[NXApp appName], tIndex, netstat.ni_name);
#  endif
		networkTiff = net_pniTiff;
		netInterface_Index = tIndex;
	    }
	}
    }
    if (netInterface_Index >= 0) return YES;
#endif

#ifdef DO_NETPPP
    for (tIndex = 0; tIndex<iostat.io_nif; tIndex++) {
	memset(&netstat, 0, sizeof(netstat));
	tableRes = table(TBL_NETINFO, tIndex, &netstat, 1, sizeof(netstat));
	if (tableRes > 0) {
	    if (   (netInterface_Index<0)
		&& !strncmp(netstat.ni_name, "ppp", 3 )) {
#  ifdef DEBUG
		printf("%s: using network interface #%d = %s\n",
			[NXApp appName], tIndex, netstat.ni_name);
#  endif
		networkTiff = net_pppTiff;
		netInterface_Index = tIndex;
	    }
	}
    }
    if (netInterface_Index >= 0) return YES;
#endif

    for (tIndex = 0; tIndex<iostat.io_nif; tIndex++) {
	memset(&netstat, 0, sizeof(netstat));
	tableRes = table(TBL_NETINFO, tIndex, &netstat, 1, sizeof(netstat));
	if (tableRes > 0) {
	    if (   (netInterface_Index<0)
		&& !strncmp(netstat.ni_name, "en0", 3 )) {
#ifdef DEBUG
		printf("%s: using network interface #%d = %s\n",
			[NXApp appName], tIndex, netstat.ni_name);
#endif
		networkTiff = net_en0Tiff;
		netInterface_Index = tIndex;
	    }
	}
    }

    /* maybe should check for something else if there's no en0? */
    return (netInterface_Index >= 0);
}

@end
