/* plotCut.m  by Paul Kunz	November 1992
 * Category extension to Plot class dealing with Cuts
 *
 * Copyright (C)  1991  The Board of Trustees of
 * The Leland Stanford Junior University. All Rights Reserved.
 */

#import "Draw.subproj/draw.h"
#import "Plot.h"

const char plotCut_m_rcsid[] = "$Id: plotCut.m,v 2.11.2.4 1994/08/17 18:57:49 pfkeb Exp $";

#import "HGraphicView.h"

@implementation Plot(Cut)

/* private methods not declared in interface file */
- (cutParmType*) getCutParms
{
     return &cutParms;
}

/* public methods */

- initCutWithTuple:ht Column:(int)i
{
     [self initPlotWithTuple: ht Type: HISTOGRAM ];
     [self setCutHistFlag:YES];
     [self initCutParmsForColumn:i];
     return self;
}

- bindCuts
{
    id			copyStor;
    id			plotList;
    id			plot;
    cutStorElem		*cut;
    dependStorElem	*depend;
    func_id		cut_func;
    unsigned		i, j;
    unsigned		notFound;
    BOOL		isBound;
    
  /* We can enter here in three ways:
   *	- dearchiving a document
   *	- pasting into open document
   *	- pasting into temporary document for pasteboard request
   * In the last two, the cut plot needs to be found before binding.
   * In the last case, the cut plot will not be found, so we just return
   */
   
    if ( cutPlotStor ) {
	plotList = [graphicView plotList];
        i = [cutPlotStor count];
	notFound = i;
	while ( i-- ) {
	    cut = [cutPlotStor elementAt:i];
	    if ( cut->plot == nil ) {
		j = [plotList count];
		while ( j-- ) {
		    plot = [plotList objectAt:j];
		    if ( cut->name == [plot name] ) {
		        cut->plot = plot;
			notFound--;
			break;
		    }
		}
	    } else {
	        notFound--;
	    }
	}
	if ( notFound ) return self;
    }
    
  /* Now proceed to bind the display to cuts, since we know there are there */
    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 ) {
		if ([self addCutPlot:cut->plot]) {
		    break;
		}
		if ( cut->invertFlag ) {
		    [self invertCutPlot:cut->plot];
		}
		isBound = YES;
	    }
	}
	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 cutList] addObjectIfAbsent:self];
    }
    return self;
}

- setCutHistFlag: (BOOL) yesOrNo
{
     if (graphicView)
     {
	  if (yesOrNo && ! cutHistFlag) 
	  {
	       [[graphicView cutList] addObject:self];
	  }
	  else if (!yesOrNo && cutHistFlag)
	  {
	       [[graphicView cutList] removeObject:self];
	  }
     }
     
     cutHistFlag = yesOrNo;
     return self;
}

- initCutParmsForColumn: (int)i
{
     float range;
     ntuple ant;
     
     cutParms.cutCode = 2;
     cutParms.cutFunc = NXCopyStringBuffer( "h_cut_outside" );
     cutParms.varIndex = i;
     /*
      * set the cut parameters to 'no-cut' starting values 
      */
     ant = [self ntuple];
     cutParms.cutValue1 = *(h_getNtLow(ant) + cutParms.varIndex);
     cutParms.cutValue2 = *(h_getNtHigh(ant) + cutParms.varIndex);
     range = cutParms.cutValue2 - cutParms.cutValue1;
     cutParms.cutValue2 += 0.05*range;
     cutParms.cutValue1 -= 0.05*range;
     
     if (cutParms.cutCode < 2)
	  cutParms.blkSize = 2;
     else
	  cutParms.blkSize = 3;

     h_bind(disp, XAXIS, cutParms.varIndex );
     return self;
}

/* Methods supporting managing cutPlots to this plot */
- (int) addCutPlot: cutPlot
{
    cutStorElem		newcut, *cut;
    dependStorElem	*depend;
    cutParmType *parms;
    unsigned		i;
    
    if ( self == cutPlot ) {
        return 1;
    }

    if ( [cutPlot hTuple] != hTuple ) {
        NXRunAlertPanel( "Open",
			 "Attempt to apply cut from different ntuple",
	                 "OK", NULL, NULL);
        return 1;
    }
    if ( !cutPlotStor ) {
        cutPlotStor = [[Storage allocFromZone:[self zone]] initCount:0
    			        elementSize:sizeof(cutStorElem)
			        description:"@%*"];
    }
    
 /* Check if this cutPlot has already been added */
    i = [cutPlotStor count];
    while ( i-- ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    return 1;
	}
    }
    
 /* Add cutPlot to this plot's list of cuts */
    [cutPlot addCutDepend:self ];
    parms = [cutPlot getCutParms];
    newcut.plot = cutPlot;
    newcut.name = [cutPlot name];
    newcut.function = h_addCut(disp, parms->cutFunc, parms->varIndex, 
		               parms->cutValue1, parms->cutValue2);
    newcut.invertFlag = NO;
    [cutPlotStor addElement:&newcut];

 /* If this plot is a cutting Plot, then add cutPlot to those plots */
    if ( !dependStor ) {
        return 0;
    }
    i = [dependStor count];
    while ( i-- ) {
        depend = [dependStor elementAt:i];
	[depend->plot addCutPlot:cutPlot];
    }
    return 0;
}
- changeCutPlot:cutPlot
{
    cutStorElem		*cut;
    int			i, count;
    float low, high;
    
    count = [cutPlotStor count];
    [cutPlot getCutLow:&low High:&high];
    for ( i = 0; i < count; i ++ ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    h_changeCut(disp, cut->function, low, high );
	    break;
	}
    }
    isFitted = NO;
    return self;
}
- invertCutPlot:cutPlot
{
    cutStorElem		newcut, *cut = NULL;
    cutParmType         *parms;
    int			i, index, count;
    
    index = -1;
    count = [cutPlotStor count];
    for ( i = 0; i < count; i++ ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    index = i;
	    break;
	}
    }
    if ( index == -1 ) {
        return self;
    }
    newcut.plot = cut->plot;
    newcut.name = cut->name;
    parms = [cutPlot getCutParms];
    if ( !cut->invertFlag ) {
	newcut.function = h_addCut(disp, "h_cut_in_incl",
	    parms->varIndex, parms->cutValue1, parms->cutValue2);
	newcut.invertFlag = YES;
    } else {
	newcut.function = h_addCut(disp, "h_cut_outside",
	    parms->varIndex, parms->cutValue1, parms->cutValue2);
	newcut.invertFlag = NO;
    }
    h_deleteCut(disp, cut->function );
    [cutPlotStor replaceElementAt:index with:&newcut ];
    return self;
}
- (BOOL) isInvertedCut:cutPlot
{
    cutStorElem		*cut;
    int			i;
    
    i = [cutPlotStor count];
    while ( i-- ) {
        cut = [cutPlotStor elementAt:i];
	if ( cut->plot == cutPlot ) {
	    return cut->invertFlag;
	}
    }
    return NO;
}
- 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 removeElementAt:i];
	    break;
	}
    }
    return self;
}
- removeAllCuts
{
    cutStorElem		*cut;
    int			i, count;
    func_id		cut_func;
    
    count = [cutPlotStor count];
    for ( i = 0; i < count; i ++ ) {
        cut = [cutPlotStor elementAt:i];
	[cut->plot removeCutDepend:self];
    }
    while ( (cut_func = h_nextCut(disp, NULL) ) ) {
	    h_deleteCut(disp, cut_func);
    }
    [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 */
- setCutLow:(float)low High:(float)high
{
    cutParms.cutValue1 = low;
    cutParms.cutValue2 = high;
    return self;
}
- getCutLow:(float *)low High:(float *)high
{
    *low = cutParms.cutValue1;
    *high = cutParms.cutValue2;
    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 removeElementAt: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;
}

- (int) getCutColumn
{
    return cutParms.varIndex;
}

@end
