/* InspectCut.m		by Mike Gravina		November 1991
 * Controls the cuts on plots
 *
 * Copyright (C)  1991  The Board of Trustees of
 * The Leland Stanford Junior University.  All Rights Reserved.
 */ 

#import "HGraphicView.h"
#import "InspectCut.h"

const char InspectCut_h_rcsid[] = INSPECTCUT_H_ID;
const char InspectCut_m_rcsid[] = "$Id: InspectCut.m,v 2.41.2.3 1994/02/08 20:29:21 rensing Exp $";

#import "FineSlider.h"
#import "HDrawApp.h"
#import "InspectTuple.h"
#import "NewInspector.h"

#define SCALE_FACTOR 5.0

#define CUT_HI 		0
#define CUT_LO 		1

#define	SHOW_ALL	0
#define SHOW_PLOT	1

 
@interface InspectCut(Private)

- (const char *) calcCutText:(Plot *)cut;
 /* 
  * Formats the string to be placed in cut browser cell.
  */
  
- checkValues;
 /*
  * checks the range of the cut and runs alert panel if low > hig
  */
- internalUpdateView;
 /*
  * Forced update even if selected plot hasn't changed.   This
  * method differs from updateView in that it is used internally
  * when cuts are added or removed to a plot.
  */
  
- reDrawPlot: (SEL) operation inList:aList;
 /*
  * Sends message operation to each Plot object in List aList and
  * redraws them.
  */
  
- removeCut:plot;
 /*
  * Removes the cut plot from any plot that depends on it.   Method
  * used internally.
  */
  
- setSliders:(BOOL)flag;
 /*
  * Sets initial range and valudes of sliders and forms if flag is YES,
  * otherwise disables them
  */
	
- setShowAllState:(BOOL) state;
 /*
  * Sets the state of the cut browser to show all cuts if state is YES,
  * or show cuts on selected plot if not.
  */
  
- updateForm;
 /*
  * Updates the contents of the cut high and low form entries.
  */
- setHTuple: atuple;
 /*
  * Sets the tuple with already prepared display held by this object
  */

@end

@implementation InspectCut

- initInspFor:aDraw
{
    NXBundle	*bundle;
    char	buffer[MAXPATHLEN+1];
    
    [super initInspFor:aDraw];
    
    bundle = [NXBundle bundleForClass:[self class]];
    if ( [bundle getPath:buffer forResource:"InspectCut" ofType:"nib"] ) {
    	[NXApp loadNibFile:buffer owner:self
                 withNames:NO  fromZone:[self zone]];
    }
    [self setSliders:NO];
    [self updateForm];
    [loSlider setScaleFactor:SCALE_FACTOR];
    [hiSlider setScaleFactor:SCALE_FACTOR];
    hTuple = NULL;
    tupleCutList = [[List alloc] initCount:0];
    [theInspector addView:[contentBox contentView]
    		 withName:"Cut Options" withSupervisor:self];
    return self;
}


- newValueInForm:sender
{
    cutLow = [cutValueForm floatValueAt:CUT_LO];
    cutHigh = [cutValueForm floatValueAt:CUT_HI];
    [self checkValues];
    [cutPlot setCutLow:cutLow High:cutHigh];

    [self setSliders:YES];
    [self reDrawPlot:@selector(changeCutPlot:) inList:[cutPlot dependList]];
    return self;
}

- newValueInSlider2:sender
{
    float               newValue, diff;

    newValue = [loSlider floatValue];
    if (newValue == cutLow)
	return self;
    diff = newValue - cutLow;
    cutLow = newValue;
    [cutValueForm setFloatValue:cutLow at:CUT_LO];

    if ([zpSwitch state] == YES) {
	cutHigh += diff;
	[cutValueForm setFloatValue:cutHigh at:CUT_HI];
	[hiSlider setFloatValue:cutHigh];
    }
    [cutPlot setCutLow:cutLow High:cutHigh];
    [self reDrawPlot:@selector(changeCutPlot:) inList:[cutPlot dependList]];
    return self;
}

- newValueInSlider1:sender
{
    float               newValue, diff;

    newValue = [hiSlider floatValue];
    if (newValue == cutHigh)
	return self;
    diff = newValue - cutHigh;
    cutHigh = newValue;
    [cutValueForm setFloatValue:cutHigh at:CUT_HI];

    if ([zpSwitch state] == YES) {
	cutLow -= diff;
	[cutValueForm setFloatValue:cutLow at:CUT_LO];
	[loSlider setFloatValue:cutLow];
    }
    [cutPlot setCutLow:cutLow High:cutHigh];
    [self reDrawPlot:@selector(changeCutPlot:) inList:[cutPlot dependList]];
    return self;
}
- newCut:sender
{
    int col;
    
    if (!hTuple) {
        return self;
    }

    col = [tpBrowserCells selectedRow];
    
    cutPlot = [[Plot alloc] initCutWithTuple: hTuple Column:col];

    [graphicView addPlot:cutPlot andSelect:NO];
    [graphicView graphicsPerformNOP:cutPlot];
    [self setSliders:YES];
    if ( selectedPlot ) {
        [buttonMatrix setEnabled:YES];
	[graphicView graphicsPerform:@selector(addCutPlot:)
				with:cutPlot andDraw:YES];
	[graphicView graphicsPerformNOP:cutPlot];
    } else {
        [self setShowAllState:YES];
    }
    [cutBrowser setAutodisplay:NO];
    [self internalUpdateView];
    [cutBrowser setAutodisplay:YES];
    [cutBrowser display];
    NXPing();
    [[[graphicView window] flushWindow] makeKeyWindow];
    return self;
}

/*
- deleteCut:plot
{
    if ( selectedPlot == plot ) {
       selectedPlot = nil;
    }
    [self setSliders:NO];
    [self internalUpdateView];
    return self;
}
- addCut:plot
{
    [docCutList addObjectIfAbsent:plot];
    return self;
}
*/
- add:sender
{
    Cell	*aCell;
    Matrix	*matrix;
    List	*cellList;
    Plot	*cut;
    int		i, count;
    
    matrix = [cutBrowser matrixInColumn:0];
    cellList = [matrix cellList];
    count = [cellList count];
    for ( i = 0; i < count; i++ ) {
        aCell = [cellList objectAt:i];
	if ( [aCell isHighlighted] ) {
	    cut = [cutList objectAt:i];
	    [graphicView graphicsPerform:@selector(addCutPlot:)
				    with:cut andDraw:YES];
	    [graphicView graphicsPerformNOP:cut];
	}
    }
    [[[graphicView window] flushWindow] makeKeyWindow];
    return self;
}
- invert:sender
{
    NXBrowserCell	*aCell;
    Matrix		*matrix;
    List		*cellList;
    Plot		*cut;
    const char		*cutText;
    int			i, count;
    
    matrix = [cutBrowser matrixInColumn:0];
    cellList = [matrix cellList];
    count = [cellList count];
    for ( i = 0; i < count; i++ ) {
        aCell = [cellList objectAt:i];
	if ( [aCell isHighlighted] ) {
	    cut = [cutList objectAt:i];
	    [graphicView graphicsPerform:@selector(invertCutPlot:)
				    with:cut andDraw:YES];
	    cutText = [self calcCutText:cut];
	    [aCell setStringValue:cutText];
	}
    }
    lastPlot = nil;
    [cutBrowser display];
    [[[graphicView window] flushWindow] makeKeyWindow];
    return self;
}    
- remove:sender
{
    NXBrowserCell	*aCell;
    Matrix		*matrix;
    List		*cellList;
    Plot		*cut;
    int			i, count;
    
    matrix = [cutBrowser matrixInColumn:0];
    cellList = [matrix cellList];
    count = [cellList count];
    for ( i = 0; i < count; i++ ) {
        aCell = [cellList objectAt:i];
	if ( [aCell isHighlighted] ) {
	    cut = [cutList objectAt:i];
	    [graphicView graphicsPerform:@selector(removeCutPlot:) 
				with:cut andDraw:YES];
	}
    }
    [[[graphicView window] flushWindow] makeKeyWindow];
    [self internalUpdateView];
    return self;
}
- replace:sender
{
    List		*applyList;
    NXBrowserCell	*aCell;
    const char		*cutText;
    int			i;
    
    if ( !cutPlot ) {
        return self;
    }
    applyList = [[cutPlot dependList] copy];
    [self removeCut:cutPlot];
    [cutPlot initCutParmsForColumn: [tpBrowserCells selectedRow]];
    [self reDrawPlot:@selector(addCutPlot:) inList: applyList];
    [applyList free];
    [graphicView graphicsPerformNOP:cutPlot];
    [[[graphicView window] flushWindow] makeKeyWindow];
    i = [cutList indexOf:cutPlot];
    if ( i != NX_NOT_IN_LIST ) {
        aCell = [[cutBrowser matrixInColumn:0] cellAt:i :0];
	cutText = [self calcCutText:cutPlot];
	[aCell setStringValue:cutText];
	[cutBrowser display];
    }
    [self setSliders:YES];
    [self updateForm];
    return self;
}
- showCutsRadio:sender
{
    unsigned int	showtype;
    
    showtype = [showCutsRadio selectedCol];
    switch (showtype) {
        case SHOW_ALL:
	    [cutBrowser setTitle:"All Available Cuts on Selected Tuple"
	          ofColumn:0];
	    break;
	case SHOW_PLOT:
	    [cutBrowser setTitle:"Cuts on Selected Plot" ofColumn:0];
	    break;
	default:
	    ;
    }
    [self internalUpdateView];
    return self;
}
- mouseMoved:(const NXPoint *)offset in:sender withKey:(int)flag
{
    	double hiMaxValue, hiMinValue;
	double loMaxValue, loMinValue;
	float	tempValue;
	float	diff;
	int		index;
	unsigned int	state;
	
	state = [showCutsRadio selectedCol];
	if ( state == SHOW_PLOT ) {
		[self setShowAllState:YES];
		[self internalUpdateView];
    	}
	/** select the current cut */
	index = [cutList indexOf:sender];
	[[cutBrowser matrixInColumn:0] selectCellAt:index :0];
	[self selectCut:self];
	
	/* update high slider */
	hiMaxValue = [hiSlider mmaxValue];
	hiMinValue = [hiSlider mminValue];
	diff = hiMaxValue - hiMinValue;
	
	tempValue = [hiSlider floatValue];
	tempValue += offset->x * diff/100;
	if (tempValue > hiMaxValue) 
		tempValue = hiMaxValue;
	if (tempValue < hiMinValue) 
		tempValue = hiMinValue;
	[hiSlider setFloatValue:tempValue];
	
	loMaxValue = [loSlider mmaxValue];
	loMinValue = [loSlider mminValue];
	diff = loMaxValue - loMinValue;
	tempValue = [loSlider floatValue];
	
	/* if command key was pressed, move the cut */
	if (flag & NX_COMMANDMASK)
		tempValue += offset->x * diff/100;
	else	/* increase or decrease the cut area */
		tempValue -= offset->x * diff/100;

	if (tempValue > hiMaxValue) 
		tempValue = hiMaxValue;
	if (tempValue < hiMinValue) 
		tempValue = hiMinValue;
	[loSlider setFloatValue:tempValue];
	
	[self newValueInSlider1:hiSlider];
	[self newValueInSlider2:loSlider];
	return self;
}
/* Methods that update the view */
- updateView
{
    if ( lastPlot == firstPlot  && !comingForward ) {
        return self;
    }
    [self internalUpdateView];
    lastPlot = firstPlot;
    return self;
}

- updateEmptySelection
{
    InspectTuple	*inspector;
    
    lastPlot = nil;
    inspector = [hDraw inspectTuple];
    [self setHTuple:[inspector currentHTuple]];
    [self internalUpdateView];
    return self;
}
- selectCut:sender
{
    List	*selCellList = [[List alloc] init];
    int		i, j;
    
    cutBrowserCells = [cutBrowser matrixInColumn:0];
    [cutBrowserCells getSelectedCells:selCellList];
    if ( !cutBrowserCells || 
	 ( [selCellList count] > 1 )     ) {
        cutPlot = nil;
	[self setSliders:NO];
	[self updateForm];
	return self;
    }
    [cutBrowserCells getRow:&i andCol:&j ofCell:[selCellList objectAt:0]];
    [cutBrowserCells scrollCellToVisible:i :0];
    cutPlot = [cutList objectAt:i];
    [cutPlot getCutLow:&cutLow High:&cutHigh];
    [self setSliders:YES];
    [self updateForm];
    [selCellList free];
    return self;
}
/* Delegate Methods for NXBrowsers */
- (int) browser: sender fillMatrix: matrix inColumn: (int) column
{
    Plot		*cut;
    NXBrowserCell	*aCell;
    int                 i, count = 0;
    const char          *cutText;
    ntuple ant;
    
    if (sender == tpBrowser) {
	[matrix setEmptySelectionEnabled:NO];
	if ( !hTuple ) {
	    return count;
	}
	ant = [hTuple ntuple];
	count = h_getNtDim(ant);
	for (i = 0; i < count; i++) {
	    [matrix insertRowAt:i];
	    aCell = [matrix cellAt:i :0];
	    [aCell setStringValue:h_getNtLabel(ant, i)];
	    [aCell setLeaf:YES];
	    [aCell setLoaded:YES];
	}
    } else if (sender == cutBrowser) {
    /*
     * load up a cell for each cut 
     */
	count = [cutList count];
	for (i = 0; i < count; i++) {
	    cut = [cutList objectAt:i];
	    [matrix insertRowAt:i];
	    aCell = [matrix cellAt:i :0];
	    cutText = [self calcCutText:cut];
	    [aCell setStringValue:cutText];
	    [aCell setLeaf:YES];
	    [aCell setLoaded:YES];
	}
    }
    return count;
}
@end

/* Private Methods */

@implementation InspectCut(Private)

- (const char *) calcCutText:(Plot *)cut
{
#define CUT_SYMBOL_LEN	13
#define CUT_TEXT_LEN	255
    static char 	cutText[CUT_TEXT_LEN+1];
    int index;
    
    index = [cut getCutColumn];
    strncpy(cutText, h_getNtLabel([cut ntuple], index), 
    		(CUT_TEXT_LEN - CUT_SYMBOL_LEN) );
    if ( [selectedPlot isInvertedCut:cut] ) {
	strcat( cutText, "  ---|   |---" );
    } else {
	strcat( cutText, "     |---|   " );
    }
    return cutText;
}
- checkValues
{
    List	*dependList;
    float	delta;
    ntuple ant = [hTuple ntuple];
    
    if ( cutLow > cutHigh ) {
	int index = [cutPlot getCutColumn];
        delta = ( h_getNtHigh(ant)[index] -
		  h_getNtLow(ant)[index] ) /100.;
	cutHigh = cutLow + delta;
	cutLow = cutLow - delta;
	dependList = [cutPlot dependList];
	[cutPlot setCutLow:cutLow High:cutHigh];
	[self setSliders:YES];
	[self reDrawPlot:@selector(changeCutPlot:) inList :dependList];
        NXRunAlertPanel( "Warning", 
	    "The low edge of the cut has become greater then the high edge.",
		 NULL, NULL, NULL );
    }
    return self;
} 

- internalUpdateView
{
    List		*oldCutList;
    List *docCutList;
    Plot		*cut;
    unsigned int	state;
    int			i, count;
    int 		oldCount;
    
    if ( selectedPlot ) {
	[self setHTuple:[selectedPlot hTuple]];
    }
    state = [showCutsRadio selectedCol];
    oldCutList = cutList;
    if (cutList == NULL)
	oldCount = 0;
    else 
    	oldCount = [cutList count];

    if ( state == SHOW_ALL ) {
	if ( selectedPlot ) {
	    docCutList = [graphicView cutList];
	    [tupleCutList empty];
	    count = [docCutList count];
	    for ( i = 0; i < count; i++ ) {
	        cut = [docCutList objectAt:i];
	        if ( hTuple == [cut hTuple] ) {
		    [tupleCutList addObject:cut];
		}
	    }
	    cutList = tupleCutList;
	 /*   oldCutList = nil; */
	} else {
	    cutList = nil;
	}
    } else {
        if ( selectedPlot ) {
	    cutList = [selectedPlot cutList];
	} else {
	     cutList = nil;
	}
    }
    if ( cutList != oldCutList 
         || (cutList != NULL && oldCount != [cutList count]) )
    {
        [cutBrowser reloadColumn:0];
	cutBrowserCells = [cutBrowser matrixInColumn:0];
	if ( cutList ) {
	    [buttonMatrix setEnabled:YES];
            i = [cutList indexOf:cutPlot];
	    if (i < 0) i = 0;
	    [cutBrowserCells selectCellAt:i :0];
	} else {
	    [buttonMatrix setEnabled:NO];
	}
    }
    if ( state == SHOW_ALL ) {
	if ( [selectedPlot isCutPlot] ) {
	    i = [cutList indexOf:selectedPlot];
	    [cutBrowserCells selectCellAt:i :0];
	}
    }
    [self selectCut:self];
    [cutBrowser setNeedsDisplay:YES];
    return self;
}
- reDrawPlot: (SEL) operation inList:aList
{
    [graphicView graphicsPerform:operation	/* redraw selected list */
                            with:cutPlot
			 andDraw:YES
			  inList:aList];
    [graphicView graphicsPerformNOP:cutPlot];

 /*
  * flush window buffer to screen 
  */
    [[[graphicView window] flushWindow] makeKeyWindow];

    return self;
}
- removeCut:plot
{
    List	*drawList;
    
    drawList = [[cutPlot dependList] copy];
    [self reDrawPlot:@selector(removeCutPlot:) inList:drawList];
    [drawList free];
    return self;
}
- setSliders:(BOOL)flag
{
    float	xlp, xhp;
    double	xl,  xh;
    
    if ( !flag ) {
        [hiSlider setEnabled:NO];
	[loSlider setEnabled:NO];
        return self;
    }
    [cutPlot getRangeForAxis:XAXIS low:&xlp high:&xhp];
    xl = (cutLow < xlp) ? cutLow : xlp ;
    xh = (cutHigh > xhp) ? cutHigh : xhp;
    [hiSlider setMinValue: xl];
    [hiSlider setMaxValue: xh];
    [loSlider setMinValue: xl];
    [loSlider setMaxValue: xh];
   
    [loSlider setFloatValue:cutLow];
    [loSlider setEnabled:YES];
    
    [hiSlider setFloatValue:cutHigh];
    [hiSlider setEnabled:YES];
    [zpSwitch setEnabled:YES];
    return self;
}
- setShowAllState:(BOOL) state
{
    unsigned int	curState;
    
    curState = [showCutsRadio selectedCol];
    if ( state && (curState == SHOW_PLOT) ) {
        [showCutsRadio selectCellAt:0 :SHOW_ALL];
	[self showCutsRadio:self];
    } else if ( !state && (curState == SHOW_ALL) ) {
        [showCutsRadio selectCellAt:0 :SHOW_PLOT];
	[self showCutsRadio:self];
    }
    return self;
}
- updateForm
{
    if ( !cutPlot ) {
	[cutValueForm setStringValue:"" at:CUT_LO];
	[cutValueForm setStringValue:"" at:CUT_HI];
	[cutValueForm setEnabled:NO];
        return self;
    }
    [cutPlot getCutLow:&cutLow High:&cutHigh];
    [self checkValues];
    [cutValueForm setFloatValue:cutLow at:CUT_LO];
    [cutValueForm setFloatValue:cutHigh at:CUT_HI];
    [cutValueForm setEnabled:YES];
    
    return self;
}

- setHTuple: atuple
{
    if (hTuple == atuple && !comingForward )
	return self;
    hTuple = atuple;
    [tpBrowser reloadColumn:0];
    tpBrowserCells = [tpBrowser matrixInColumn:0];
    return self;
}
@end
