/* Plot.m  by Jonas Karlsson, July 1990 
 *
 * Copyright (C)  1991  The Board of Trustees of
 * The Leland Stanford Junior University. All Rights Reserved.
 */
 
#import "Plot.h"

const char Plot_h_rcsid[] = PLOT_H_ID;
const char Plot_m_rcsid[] = "$Id: Plot.m,v 1.20 1992/04/23 17:15:57 pfkeb Rel $";

#import <appkit/Panel.h>
#import <objc/List.h>
#import <objc/Storage.h>

#import "HGraphicView.h"
#import "HTuple.h"

#import <appkit/nextstd.h>
#import <math.h>
#import <float.h>
#import <dpsclient/wraps.h>

#define INDEX_VERSION 2
#define CURRENT_VERSION INDEX_VERSION

@implementation Plot

static unsigned int plotNum = 0;

static NXAtom namePlot() 
{
    char buffer[256];
    
    sprintf( buffer, "HippoPlot.%d", plotNum++);
    return NXUniqueString(buffer);
}
    
static void setMarginRect( NXRect *margins, NXRect *bounds, int grid )
{    
    NXSetRect (margins, 
	0.18 * bounds->size.width,
	0.18 * bounds->size.height,
	bounds->size.width * (1.0 - .26),
	bounds->size.height * (1.0 - .26));
    if ( margins->size.width/grid > 3.001 ) {
        margins->origin.x = bounds->origin.x
	              + MAX( grid*floor( margins->origin.x/grid ), grid );
	margins->size.width = grid*floor( margins->size.width/grid );
    } else {
        margins->origin.x += bounds->origin.x;
    }
    if ( margins->size.height/grid > 3.001) {
        margins->origin.y = bounds->origin.y
	               + MAX( grid*floor( margins->origin.y/grid ), grid );
	margins->size.height = grid*floor( margins->size.height/grid );
    } else {
        margins->origin.y += bounds->origin.y;
    }
    
}
+ initialize
{
    [self setVersion:CURRENT_VERSION];
    return self;
}
- init
{
    [super init ];
    uniqueName = namePlot();
    return self;
}
- (const char *)name
{
    return uniqueName;
}
- getDispType:(graphtype_t *) type
{
     *type = h_getDispType(disp);
     return self;
}

- setDispType:(graphtype_t *) type
{
    graphtype_t         oldtype;
    const char         *string;
    int                 i;

    oldtype = h_getDispType(disp);
    h_setDispType(disp, *type);
    if ((oldtype == HISTOGRAM) && (*type != HISTOGRAM)) {
	i = h_getBinding(disp, XAXIS);
	h_bind(disp, YAXIS, i);
	string = h_getNtLabel([hTuple ntuple], i);
	[self NameAxisY:string];
    }
    if ((oldtype != HISTOGRAM) && (*type == HISTOGRAM)) {
	h_setDrawType(disp, BOX);
	[self NameAxisY:"Entries per bin"];
    }
    return self;
}

- setGraphicView:anObject
{
    graphicView = anObject;
    return self;
}
- graphicView
{
    return graphicView;
}
- setHTuple: ht withDisplay:(display) d1
{
    NXRect      margins;
    int		grid;

    disp = d1;
    [self replaceTupleWith:ht];
    h_getDrawRect(disp, (rectangle *) &bounds);
    grid = [graphicView gridSpacing];
    setMarginRect(&margins, &bounds, grid );
    h_setMarginRect(disp, (rectangle *) & margins);
    return self;
}
- setRefFlag:(BOOL) flag
{
    h_setNtByRef( disp, flag, NULL );
    return self;
}
- setFixBinsFlag:(BOOL)flag
{
    fixBinsFlag = flag;
    h_setFixedBins( disp, fixBinsFlag );
    return self;
}
- setRefFilename:(const char *)filename
{
	if (!filename) return self;    /* if filename is not legal, return */
    if ( !reffilename ) {
        NX_ZONEMALLOC( [self zone], reffilename, char, strlen(filename)+1 );
    } else {
        NX_ZONEREALLOC( [self zone], reffilename, char, strlen(filename)+1 );
    }
    strcpy( reffilename, filename );
    return self;
}
- changeRefFileNameIfValid:(const char *)filename
{
    if ( !hTuple ) {
        return self;
    }
    if ( [hTuple isFakeFilename] ) {
	if ( !reffilename ) {
	    NX_ZONEMALLOC( [self zone], reffilename, char,
	                   strlen(filename)+1 );
	} else {
	    NX_ZONEREALLOC( [self zone], reffilename, char,
	                    strlen(filename)+1 );
	}
	strcpy( reffilename, filename );
	[hTuple setFilename:filename];
    }
    return self;
}
- replaceTupleWith: ht
{
    ntuple	tuple;
    
    hTuple = ht;
    tuple = (hTuple != nil)  ? [hTuple ntuple] : NULL ;
    h_bindNtuple(disp, tuple );
    if ( !tuple ) return self;
    h_setTitle(disp, h_getNtTitle(tuple) );
    [self setRefFilename:[hTuple filename]];
    [self setRefFlag:[hTuple isRef]];
    ntindex = [hTuple index];
    return self;
}
- replace:oldTuple with:newTuple
{
    if ( oldTuple == hTuple ) {
	[self replaceTupleWith:newTuple];
    }
    return self;
} 
- closeTuple
{
    ntuple	tuple;
    
    refFlag = h_getNtByRef(disp);
    fixBinsFlag = h_getFixedBins(disp);
    h_setFixedBins(disp, YES);
    hTuple = nil;
    tuple = NULL;
    h_bindNtuple(disp, tuple );
    return self;
}
- bindReference
{
    if ( reffilename == NULL ) return self;
    if ( !hTuple ) {
	hTuple = [ graphicView hTupleForFile:reffilename index:ntindex];
    }
    if ( disp == NULL ) return self;
    [self replaceTupleWith:hTuple];
    if ( !hTuple ) return self;
    
    h_setFixedBins( disp, fixBinsFlag );
    return self;
}

- bindCuts
{
    id			copyStor;
    id			plotList;
    id			plot;
    cutStorElem		*cut;
    dependStorElem	*depend;
    func_id		cut_func;
    unsigned		i, j;
    BOOL		isBound;
    
    if ( cutPlotStor ) {
	copyStor = [cutPlotStor copy ];
	[cutPlotStor empty];
	while ( (cut_func = h_nextCut(disp, NULL) ) ) {
	    h_deleteCut(disp, cut_func);
	}
	isBound = NO;
	i = [copyStor count];
	while ( i-- ) {
	    cut = [copyStor elementAt: i];
	    if ( cut->plot ) {
		[self addCutPlot:cut->plot];
		isBound = YES;
	    } else {
		plotList = [graphicView plotList];
		j = [plotList count];
		while ( j-- ) {
		    plot = [plotList objectAt:j];
		    if ( cut->name == [plot name] ) {
			[self addCutPlot:plot];
			isBound = YES;
			break;
		    }
		}
	    }
	}
	if ( !isBound ) {
	    h_setFixedBins( disp, YES );
	}
	[copyStor free];
    }
    if ( dependStor ) {
        copyStor = [dependStor copy];
	[ dependStor empty];
	i = [copyStor count];
	while ( i-- ) {
	    depend = [copyStor elementAt:i];
	    if ( depend->plot ) {
	        plotList = [depend->plot plotList];
		j = [plotList indexOf:self];
		if ( j == NX_NOT_IN_LIST ) {
		    [depend->plot addCutPlot:self];
		} else {
		    [dependStor addElement:depend];
		}
	    } else {
	        plotList = [graphicView plotList];
		j = [plotList count];
		while ( j-- ) {
		    plot = [plotList objectAt:j];
		    if ( depend->name == [plot name] ) {
		        [plot addCutPlot:self];
			break;
		    }
		}
	    }
	}
	[graphicView addCut:self];
    }
    return self;
}

- hTuple
{
    return hTuple;
}
- (ntuple) ntuple
{
    return [hTuple ntuple];
}
- addHTupleToList:tlist
{
    if ( hTuple ) {
	[tlist addObjectIfAbsent:hTuple];
    }
    return self;
}

- addPlotToList:list
{
    [list addObjectIfAbsent:self];
    return self;
}
- (display) histDisplay
{
    return disp;
}

- setTitle:(char *) title
{
  h_setTitle(disp, title);

  return self;
}
- (char *) title
{
    return ((char *) h_getTitle( disp ) );
}
- NameAxisX:(const char *)AxisName
{
    h_setAxisLabel(disp, XAXIS, AxisName);
    return self;
}
- (const char *) axisLabelX
{
    return h_getAxisLabel(disp, XAXIS );
}
- (const char *) axisLabelY
{
    return h_getAxisLabel(disp, YAXIS );
}
- NameAxisY:(const char *)AxisName
{
    h_setAxisLabel(disp, YAXIS, AxisName);
    return self;
}
- NameAxisW:(const char *)AxisName
{
    h_setAxisLabel(disp, WEIGHT, AxisName);
    return self;
}

- NameAxisXE:(const char *)AxisName
{
    h_setAxisLabel(disp, XERROR, AxisName);
    return self;
}

- NameAxisYE:(const char *)AxisName
{
    h_setAxisLabel(disp, YERROR, AxisName);
    return self;
}

- bindAxisX:(int *)dataDim
{
    h_bind(disp, XAXIS, *dataDim );
    h_setAxisLabel(disp, XAXIS, h_getNtLabel([hTuple ntuple],*dataDim) );
    return self;
}
- bindAxisY:(int *)dataDim
{
    h_bind(disp, YAXIS, *dataDim );
    h_setAxisLabel(disp, YAXIS, h_getNtLabel([hTuple ntuple],*dataDim) );
    return self;
}
- bindAxisW:(int *)dataDim
{
    h_bind(disp, WEIGHT, *dataDim );
    return self;
}

- bindAxisXE:(int *)dataDim
{
    h_bind(disp, XERROR, *dataDim );
    return self;
}

- bindAxisYE:(int *)dataDim
{
    h_bind(disp, YERROR, *dataDim );
    return self;
}

- setLogScaleX:(int *)yesOrNo
{	
	h_setLogAxis(disp,XAXIS,*yesOrNo);
	return self;
}
- (BOOL) isLogScaleX
{
    BOOL	val;
    
    val = h_getLogAxis(disp, XAXIS);
    return val;
}
- setLogScaleY:(int *)yesOrNo
{	
	h_setLogAxis(disp,YAXIS,*yesOrNo);
	return self;
}
- (BOOL) isLogScaleY
{
    BOOL	val;
    
    val = h_getLogAxis(disp, YAXIS);
    return val;
}
- setTitlesFlag:(int *)yesOrNo
{
    h_setDrawTitles( disp, *yesOrNo);
    return self;
}
- (int) titlesFlag
{
    return h_getDrawTitles(disp);
}

- setAxesFlag:(int *)yesOrNo
{
    h_setDrawAxes( disp, *yesOrNo );
    return self;
}
- (int) axesFlag
{
    return h_getDrawAxes( disp );
}
- setAutoScaleX:(int *)yesOrNo
{	
     h_setAutoScale(disp,XAXIS,*yesOrNo);
     return self;
}
- setAutoScaleY:(int *)yesOrNo
{	
     h_setAutoScale(disp,YAXIS,*yesOrNo);
     return self;
}

- setCutHistFlag: (BOOL) yesOrNo
{
	cutHistFlag = yesOrNo;
	return self;
}

- (BOOL) isCutHist
{
	return cutHistFlag;
}

- setRangeForAxisX:(NXPoint *)p
{
    h_setRange(disp, XAXIS, p->x, p->y );
    return self;
}
- getRangeForAxisX:(NXPoint *)p
{
    if ( h_getAutoScale( disp, XAXIS ) ) {
        h_setDirty( disp );
	h_bin( disp ); /* in case it hasn't been displayed */
    }
    h_getRange(disp, XAXIS, &p->x, &p->y );
    return self;
}
- setRangeForAxisY:(NXPoint *)p
{
    h_setRange(disp, YAXIS, p->x, p->y );
    return self;
}
- getRangeForAxisY:(NXPoint *)p
{
    if ( h_getAutoScale(disp,YAXIS) ) {
        if ( h_getDispType(disp) == HISTOGRAM ) {
	    h_setDirty( disp );
	    h_bin( disp ); /* in case it hasn't been displayed */
	    h_getBinExtreme(disp, &p->x, &p->y );
	} else {
	    h_autoScale(disp);
	    h_getRange(disp, YAXIS, &p->x, &p->y );
	}
    } else {
	h_getRange(disp, YAXIS, &p->x, &p->y );
    }
    return self;
}
- getRangeForAxis:(binding_t) axis low:(float *) xl high:(float *) xh
{
    
    if ( h_getAutoScale( disp, axis ) && (axis == YAXIS) ) {
        if ( h_getDispType(disp) == HISTOGRAM ) {
	    h_setDirty( disp );
	    h_bin( disp ); /* in case it hasn't been displayed */
	    h_getBinExtreme(disp, xl, xh );
	} else {
	    h_autoScale(disp);
	    h_getRange(disp, YAXIS, xl, xh );
	}
    } else {
	h_getRange(disp, axis, xl, xh );
    }
    return self;
}
- setRangesFrom:plot
{
    NXPoint	range;
    graphtype_t	plot_t;
    float	plot_w, my_w;
    int		flag = 0;

    if ( plot == self ) {
        return self;
    }
    [plot getRangeForAxisX:&range];
    [self setRangeForAxisX:&range];
    [self setAutoScaleX:&flag];
    [plot getRangeForAxisY:&range];
    if ( h_getDispType(disp) == HISTOGRAM ) {
        [plot getDispType:&plot_t];
	if ( plot_t == HISTOGRAM ) {
	    plot_w = [plot widthForAxis:XAXIS];
	    my_w = h_getBinWidth(disp, XAXIS);
	    range.x = range.x*(my_w/plot_w);
	    range.y = range.y*(my_w/plot_w);
	    [self setRangeForAxisY:&range];
	    [self setAutoScaleY:&flag];
	}
    } else {
	[self setRangeForAxisY:&range];
	[self setAutoScaleY:&flag];
    }
    return self;
}
- getRangeForAxisY:(NXPoint *)range normalizedTo:plot
{
    graphtype_t	plot_t;
    float	plot_w, my_w;
    
    [self getRangeForAxisY:range];
    
    [plot getDispType:&plot_t];
    if ( h_getDispType(disp) != HISTOGRAM || 
                      plot_t != HISTOGRAM    ) {
        return self;
    }
    plot_w = [plot widthForAxis:XAXIS];
    my_w = h_getBinWidth(disp, XAXIS);
    range->x *= (plot_w/my_w);
    range->y *= (plot_w/my_w);
    return self;
}
- (float)widthForAxis:(binding_t) axis
{
    return h_getBinWidth(disp, axis);
}
- setNumBinsForAxisX:(int *) n
{
   h_setBinNum(disp, XAXIS, *n );
   return self;
}

- setNumBinsForAxisY:(int *) n
{
   h_setBinNum(disp, YAXIS, *n );
   return self;
}

- (int)numBinsForAxis:(binding_t) axis
{
    return h_getBinNum(disp, axis);
}
- setDrawType:(drawtype_t *) type
{
    h_setDrawType(disp, *type);
    return self;
}
- getDrawType:(drawtype_t *) type
{
	*type = h_getDrawType(disp);
	return self;
}
- setColorType:(int *)onOff
{
     int i;
     
     if ( *onOff )
	  h_orDrawType(disp, COLOR);
     else
     {
	  i = h_getDrawType(disp);
	  i = i & (~COLOR);
	  h_setDrawType(disp, i);
     }
     return self;
}
- draw
{
    	NXRect margins;
        int   grid;
		
    	if (bounds.size.width < 1.0 || bounds.size.height < 1.0)
		return self;

    	if (!NXEqualColor([self fillColor], NX_COLORCLEAR)) {
		[self setFillColor];
		NXRectFill(&bounds);
    	}
   	 if (!NXEqualColor([self lineColor], NX_COLORCLEAR)) {
		[self setLineColor];
    	}
   	if ( (NXDrawingStatus == NX_PRINTING) && (linewidth < 0.15) ) {
    		PSsetlinewidth(0.15); 	/* in case going to Linotronic (R) */
    	}
    
    	h_setDrawRect (disp, (rectangle *) &bounds );
	if ( graphicView ) { 
	    grid = [graphicView gridSpacing];
	} else {
	    grid = 1;
	}
    	setMarginRect( &margins, &bounds, grid );
    	h_setMarginRect (disp, (rectangle *) &margins);

 /*
 * do shading in case of cut plot. Must do after plotting, since h_plot
 *  sets up margins, autoscales, etc.
 * h_shade will keep shading inside bounds of plot.
 */
	if ( cutHistFlag && (disp != NULL) && (cutParms.cutFunc != NULL) )
	{
	     switch (cutParms.cutCode)
	     {
	     case 0:
		  h_shade(disp, -FLT_MAX, cutParms.cutValue1);
		  break;
	     case 1:
		  h_shade(disp, cutParms.cutValue1, FLT_MAX);
		  break;
	     case 2:
		  h_shade(disp, cutParms.cutValue1, cutParms.cutValue2);
		  break;
	     case 3:
		  h_shade(disp, -FLT_MAX, cutParms.cutValue1);
		  h_shade(disp, cutParms.cutValue2, FLT_MAX);
		  break;
	     }		  
	}

 /*
  * draw plot last, so shading doesn't cover it.
  */
    	h_plot(disp);

    	return self;
}
- print
{
    h_print(disp);
    return self;
}

- setBounds:(const NXRect *)aRect
{
    NXRect margins;
    int		grid;
    
    bounds = *aRect;
    h_setDrawRect(disp, (rectangle *)  &bounds);
    grid = [graphicView gridSpacing];
    setMarginRect( &margins, &bounds, grid );	
    h_setMarginRect (disp, (rectangle *) &margins);
    return self;
}


- sizeToNaturalAspectRatio
{
    NXRect	defaultBox;
    
    [graphicView calcDefaultPlotSize:&defaultBox];
    bounds.origin.y += ( NX_HEIGHT(&bounds) - NX_HEIGHT(&defaultBox) );
    bounds.size.height = NX_HEIGHT(&defaultBox);
    bounds.size.width = NX_WIDTH(&defaultBox);
    [self setBounds:&bounds];
    return self;
}
/* Methods supporting managing cutPlots to this plot */
- addCutPlot: cutPlot
{
    cutStorElem		newcut, *cut;
    dependStorElem	*depend;
    cutParmType         parms;
    int			irc;
    unsigned		i, count;
    
    if ( self == cutPlot ) {
        return self;
    }
    if ( [cutPlot ntuple] != [hTuple ntuple] ) {
        irc = NXRunAlertPanel( "Open",
			       "Attempt to apply cut from different ntuple",
	                       "OK", NULL, NULL);
        return self;
    }
    if ( !cutPlotStor ) {
        cutPlotStor = [[Storage allocFromZone:[self zone]] initCount:0
    			        elementSize:sizeof(cutStorElem)
			        description:"@%*"];
    }
    
 /* Check if this cutPlot has already been added */
    count = [cutPlotStor count];
    for ( i = 0; i < count; i++ ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    return self;
	}
    }
    
 /* Add cutPlot to this plot's list of cuts */
    [cutPlot addCutDepend:self ];
    [cutPlot getCutParms:&parms];
    newcut.plot = cutPlot;
    newcut.name = [cutPlot name];
    newcut.function = h_addCut(disp, parms.cutFunc, parms.varIndex, 
		               parms.cutValue1, parms.cutValue2);
    [cutPlotStor addElement:&newcut];

 /* If this plot is a cutting Plot, then add cutPlot to those plots */
    if ( !dependStor ) {
        return self;
    }
    i = [dependStor count];
    if ( !i ) {
        return self;
    }
    while ( i-- ) {
        depend = [dependStor elementAt:i];
	[depend->plot addCutPlot:cutPlot];
    }
    return self;
}
- changeCutPlot:cutPlot
{
    cutStorElem		*cut;
    cutParmType         parms;
    int			i, count;
    
    count = [cutPlotStor count];
    for ( i = 0; i < count; i ++ ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    [cutPlot getCutParms:&parms];
	    h_changeCut(disp, cut->function,
	                parms.cutValue1, parms.cutValue2);
	    break;
	}
    }
    return self;
}
- removeCutPlot:cutPlot
{
    cutStorElem		*cut;
    int			i, count;
    
    
    count = [cutPlotStor count];
    for ( i = 0; i < count; i ++ ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    [cutPlot removeCutDepend:self];
            h_deleteCut( disp, cut->function );
	    [cutPlotStor removeAt:i];
	    break;
	}
    }
    return self;
}
- removeAllCuts
{
    cutStorElem		*cut;
    int			i, count;
    
    
    count = [cutPlotStor count];
    for ( i = 0; i < count; i ++ ) {
        cut = [cutPlotStor elementAt:i];
	[cut->plot removeCutDepend:self];
	h_deleteCut( disp, cut->function );
    }
    [cutPlotStor empty];
    return self;
}
- cutList
{
    cutStorElem		*cut;
    unsigned	i, count;
    
    count = [cutPlotStor count];
    if ( !count ) {
        return nil;
    }
    
    if ( cutPlotList == nil ) {
        cutPlotList = [[List allocFromZone:[self zone]] initCount:0];
    } else {
        [cutPlotList empty];
    }
    for ( i = 0; i < count; i++ ) {
        cut = [cutPlotStor elementAt:i];
	[cutPlotList addObject:cut->plot];
    }
    return cutPlotList;
}
- (BOOL) hasCut
{
    return ( [cutPlotStor count] ? YES : NO );
}
/* Methods used to support plot being used for display for a cut function */
- setCutParms:(cutParmType *) parms
{
    cutParms.cutFunc   = NXCopyStringBuffer( parms->cutFunc );
    cutParms.varIndex  = parms->varIndex;
    cutParms.cutValue1 = parms->cutValue1;
    cutParms.cutValue2 = parms->cutValue2;
    cutParms.cutCode   = parms->cutCode;
    if ( cutParms.cutCode < 2 ) {
        cutParms.blkSize = 2;
    } else {
        cutParms.blkSize = 3;
    }
    
    return self;
}
- getCutParms:(cutParmType *) parms
{
    parms->cutFunc   = cutParms.cutFunc;
    parms->varIndex  = cutParms.varIndex;
    parms->cutValue1 = cutParms.cutValue1;
    parms->cutValue2 = cutParms.cutValue2;
    parms->cutCode   = cutParms.cutCode;
    parms->blkSize   = cutParms.blkSize;

    return self;
}    
- addCutDepend:sender
{
    dependStorElem	new, *old;
    unsigned int	i;
    
    if ( !dependStor ) {
        dependStor = [[Storage allocFromZone:[self zone]] initCount:0
    			             elementSize:sizeof(dependStorElem)
			             description:"@%"];
    }
    i = [dependStor count];
    while ( i-- ) {
        old = [dependStor elementAt:i];
	if ( old->plot == sender ) {
	    return self;
	}
    }
    new.plot = sender;
    new.name = [sender name];
    [dependStor addElement:&new];
    return self;
}
- removeCutDepend:sender
{
    dependStorElem	*old;
    unsigned int	i;
    
    i = [dependStor count];
    while ( i-- ) {
        old = [dependStor elementAt:i];
	if ( old->plot == sender ) {
	    [dependStor removeAt:i];
	    return self;
	}
    }
    return self;
}
- (unsigned) dependCount
{
    if ( !dependStor ) {
        return 0;
    }
    return [dependStor count];
}
- dependList;
{
    dependStorElem	*old;
    unsigned int	i;
    
    if ( !dependList ) {
        dependList = [[List allocFromZone:[self zone]] initCount:0];
    }
    [dependList empty];
    i = [dependStor count];
    while ( i-- ) {
        old = [dependStor elementAt:i];
	[dependList addObject:old->plot];
    }
    return dependList;
}
- (BOOL) isCutPlot
{
    return cutHistFlag;
}
/* Methods supporting Archiving and de-Archiving */
- write:(NXTypedStream *) ts
{
    cutStorElem		*cut;
    dependStorElem	*old;
    display             dlist[] = {NULL, NULL};
    char		*data;
    int			len, lenref;
    int			i;

    [super write:ts];

 /* change some flags for storage to stream */
    refFlag = h_getNtByRef(disp);
    fixBinsFlag = h_getFixedBins(disp);
    h_setFixedBins(disp, YES);
    h_setNtByRef(disp, YES, reffilename );
    lenref = strlen(reffilename);

 /* write display to buffer */
    len = h_dispSize( disp );
    NX_ZONEMALLOC( [self zone], data, char, len );
    dlist[0] = disp;
    if ( h_writeMem( data, len, dlist, NULL ) ) {
        printf( "Could not write buffer\n" );
	return self;
    }

 /* restore display in case we are doing copy */
    h_setNtByRef( disp, refFlag, NULL );
    h_setFixedBins( disp, fixBinsFlag );
        
 /* Now archive Plot object to typed stream */
 
    NXWriteTypes( ts, "ccii",    &refFlag, &fixBinsFlag, &len, &lenref );
    NXWriteType(  ts, "*",       &reffilename );
    if ( [Plot version] >= INDEX_VERSION ) {
        NXWriteType( ts, "i", &ntindex );
        NXWriteObjectReference(ts, hTuple);
    }
    NXWriteArray( ts, "c",       len, data );
    NXWriteType(  ts, "c",       &cutHistFlag);
    if ( cutHistFlag ) {
	len = strlen( cutParms.cutFunc );
        NXWriteType( ts, "i",       &len );
	NXWriteType( ts, "{*iffi}", &cutParms ); 
	NXWriteType( ts, "i",       &cutNumber);
    }
    
    i = [cutPlotStor count];
    NXWriteType( ts, "i", &i );
    while ( i-- ) {
        cut = [cutPlotStor elementAt:i];
	NXWriteObjectReference(ts, cut->plot );
	if ( [Plot version] >= INDEX_VERSION ) {
	    cut->name = [cut->plot name];
	    NXWriteType( ts, "%", &cut->name );
	}
    }
    if ( dependStor ) {
        i = [dependStor count];
    } else {
        i = 0;
    }
    NXWriteType( ts, "i", &i);
    while ( i-- ) {
        old = [dependStor elementAt:i];
	NXWriteObjectReference( ts, old->plot);
	if ( [Plot version] >= INDEX_VERSION ) {
	    old->name = [old->plot name];
	    NXWriteType( ts, "%", &old->name );
	}
    }
    
    free( data );    
    
    return self;
}
- read:(NXTypedStream *) ts
{
    cutStorElem		cut;
    dependStorElem	new;
    ntuple		*ntlist;
    display		*dlist;
    char		*data;
    int			len, lenref;
    int			i, count;
    
    [super read:ts];
    uniqueName = namePlot();
    NXReadTypes( ts, "ccii",    &refFlag, &fixBinsFlag, &len, &lenref);
    NX_ZONEMALLOC( [self zone], data, char, len );
    NX_ZONEMALLOC( [self zone], reffilename, char, lenref+1 );
    NXReadType(  ts, "*",       &reffilename);
    if ( NXTypedStreamClassVersion(ts, "Plot") >= INDEX_VERSION ) {
        NXReadType( ts, "i", &ntindex );
        hTuple = NXReadObject( ts );
    } else {
        ntindex = 0;
	hTuple = nil;
    }
    NXReadArray( ts, "c",       len, data );
    h_readMem( data, len,  &dlist, &ntlist );
    NX_FREE( data );
    disp = dlist[0];
    NXReadType(  ts, "c",      &cutHistFlag );
    if ( cutHistFlag ) {
        NXReadType( ts, "i",       &len );
	NX_ZONEMALLOC( [self zone], cutParms.cutFunc, char, len+1 );
	NXReadType( ts, "{*iffi}", &cutParms ); 
	NXReadType( ts, "i",       &cutNumber);
    }
    NXReadType(  ts, "i",       &count );
    if ( count ) {
        cutPlotStor = [[Storage allocFromZone:[self zone]] initCount:0
    			             elementSize:sizeof(cutStorElem)
			             description:"@%*"];
    }
    for ( i = 0; i < count; i++ ) {
        cut.plot = NXReadObject( ts );
	if ( NXTypedStreamClassVersion(ts, "Plot") >= INDEX_VERSION ) {
	    NXReadType( ts, "%", &cut.name );
	} else {
	    cut.name = NXUniqueString("HippoPlot.NULL" );
	}
	if ( cut.plot ) {
	    cut.name = [cut.plot name];
	}
	[cutPlotStor addElement:&cut.plot];
    }
    NXReadType( ts, "i", &count );
    if ( count ) {
        dependStor = [[Storage allocFromZone:[self zone]] initCount:0
    			             elementSize:sizeof(dependStorElem)
			             description:"@%"];
    }
    for ( i = 0; i < count; i++ ) {
        new.plot = NXReadObject( ts );
	if ( NXTypedStreamClassVersion(ts, "Plot") >= INDEX_VERSION ) {
	     NXReadType( ts, "%", &new.name );
	} else {
	    new.name = NXUniqueString("HippoPlot.NULL" );
	}
	if ( new.plot ) {
	    new.name = [new.plot name];
	}
        [dependStor addElement:&new];
    }
    return self;
}
- free
{
  /* disp should be attached, but it may not be */
    if ( disp != NULL ) h_freeDisp(disp);
    NX_FREE(reffilename);
    NX_FREE(cutParms.cutFunc);
    return [super free];
}
@end
