/*      The FunctionPlotter 1D Internal Frame class 
    extends JInternalFrame 
*/ 
 
package tools.plottool;

import tools.*;
import mathgraphics.*;
import tools.plottool.*;
import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 
import javax.swing.event.*; 
import mathfunctions.*; 
import java.awt.geom.*;
import java.util.*;
 
 
/**
 * The class <code>PlotTool</code> is the core class that produce 
 * the function plotter for the MathTool suite of tools called the
 * MVT.  
 *
 * @author Peter Staab
 * @author Peter Fox
 * @author Eric Wright
 * @author for the Mathematical Visualization Toolkit
 * originally written in July 1999
 *
 * Note: Revisions by Peter Staab March 2000, and March 2001
 */
public class PlotTool extends GraphicsTool  
{ 

    // The input and option panels is where the user can 
    // add function input and set options such as color. 

  
    
    // Stores information about the function, its derivative and
    // its independent variable. 
    
    private Function function = null;
    private Function derivative = null;
    private Variable indVar = null;

    // The menu item to get the options. 
    
 
 
    
    // This is where the plotting of the functions is done.

    Line line = null;

    // input panel

   private PlotInput inputPanel = new PlotInput();
    private PlotOptions optionsPanel = new PlotOptions();
 
    /**
     * Creates a new <code>PlotTool</code> instance. By default it calls 
     * the GraphicsTool constructor which consists of the basics for
     * all of the Graphics Tools in the MVT packages.  
     *
     */
    public PlotTool() 
    { 
	super("Function Plotter");
	super.setInputPanel(inputPanel);
	super.setOptionsPanel(optionsPanel);

	setHelpFile("FunctionPlotter/plot1d.html","Function Plotter Help");
	
	
	// Add the Variable update Listener

	inputPanel.indVariableBox.getTextBox().
	    addCaretListener(new CaretListener(){
		    public void caretUpdate(CaretEvent e){

      			inputPanel.functionBox
			    .setLabel("f("+inputPanel.indVariableBox);
		    }
		});
		    

	//
	

	// Add a listener to the plot button
	
	inputPanel.plotButton.addActionListener(new PlotButtonListener()); 

	inputPanel.clearButton.addActionListener(new ClearButtonListener());
    } 
    
    
    private void createPoints(){
	

	double xmin = toolPanel.getPlotPanel().getXMin();
	double xmax = toolPanel.getPlotPanel().getXMax();
	double x,y;

	final double EPS = 1e-5;
	
	int numPoints = toolPanel.getPlotPanel().getWidth();
	
	LinkedList ptVect = new LinkedList();
	
	int ptsAdded;
	
	double xPrevious, xNext, xCurrent;
	double yPrevious, yNext, yCurrent;
	double newxPoint1, newyPoint1, newxPoint2, newyPoint2;
	
	double trueDerivative, approxDerivative;
	
	try {
	    
	    // Initially set numPoints elements into a Vector
	    
	    for(int i=0;i<=numPoints;i++){
		
		x=xmin+((float)(i))/
		    ((double)numPoints)*(xmax-xmin);
		
		y=function.evaluate(indVar,x);
		
		ptVect.add(new Point2D.Double(x,y));
	    }	

	    // Now fill the Vector adaptively
	    
	    while(true) {
		
		// ptsAdded is a flag to determine how many points have
		// been added to the Vector at each pass.
		
		ptsAdded=0;
		
		for(int i=1;i<ptVect.size()-1;i++){

		    // for interior points get the previous, current, and 
		    // next points.
		    
		    xPrevious =((Point2D.Double)ptVect.get(i-1)).x;
		    xCurrent=((Point2D.Double)ptVect.get(i)).x;
		    xNext =((Point2D.Double)ptVect.get(i+1)).x;
		    
		    yPrevious =((Point2D.Double)ptVect.get(i-1)).y;
		    yCurrent=((Point2D.Double)ptVect.get(i)).y;
		    yNext =((Point2D.Double)ptVect.get(i+1)).y;
		    
		    // calculate the true derivate at the current point
		    
		    trueDerivative
			=derivative.evaluate(indVar,xCurrent);
		    
		    // calculate the approximate derivative using
		    // centered differences.  
		    
		    approxDerivative=(yNext-yPrevious)/(xNext-xPrevious);
		    
		    
		    // check how close the true and approximate 
		    // derivatives are
		    
		    // also check to see that points are not added when
		    // they are already closer than a
		    // pixel width apart.
		    
		    if((Math.abs(trueDerivative-approxDerivative)>EPS)&&
		       ((xNext-xPrevious)>(xmax-xmin)/((double)numPoints))){
			
			// If the more points are needed, calculate
			// a new point 1/2-way between the current and 
			// previous point and add it to the ptVector
			
			newxPoint1=0.5*(xCurrent+xPrevious);
			newyPoint1=function.evaluate(indVar,newxPoint1);
			ptVect.add(i,new Point2D
				   .Double(newxPoint1,newyPoint1));
			
			// Do the same with the current and next point.
			
			newxPoint2=0.5*(xCurrent+xNext);
			newyPoint2=function.evaluate(indVar,
						     newxPoint2);
			ptVect.add(i+2,new Point2D
				   .Double(newxPoint2,newyPoint2));
			
			// This jumps to the next appropriate point;
			
			i+=4;

			// And increase the ptsAdded variable to 
			// indicated that points have been added. 
			
			ptsAdded+=2;
		    }
		}

		// if no points have been added during this pass, 
		// break out of the while loop.
		
		if(ptsAdded==0)
		    break;

	    } 

	    // convert the point vector to a point array
	    
	    Point2D.Double [] pts 
		= new Point2D.Double[ptVect.size()];
	    
	    for(int i=0;i<ptVect.size();i++){
		pts[i] = (Point2D.Double) ptVect.get(i);
	    }
	    
	    // create a new line that consists of the points.
	    
	    this.line = new Line(pts,toolPanel.getPlotPanel(),
				 optionsPanel.linecolor.getColor());
	    
	} catch (VariableUndefinedException ex){
	    errorMessage("Error in function: " 
			 + inputPanel.functionBox.getEditBox()
			 +"\nYour function contains variables: "
			 + function.getVariables() +"\n"
			 + "The only operable variable is: "
			 + inputPanel.indVariableBox.getEditBox());
	
	}  catch(UnacceptableVariableException ex){
	    errorMessage(ex.getMessage());
	}
	
    }
    
    /* This function parses all of the options in the PlotOptions 
       pop-up menu. */

    protected boolean parseOptions() {

	// Decide if the range is to be default or user input
	
	if (optionsPanel.defaultRange.isSelected()){
	    toolPanel.getPlotPanel().defaultY();
	} else {
	    double maxY=1.0, minY=-1.0;
	    try {
		maxY = optionsPanel.maxRangeBox.getDoubleValue();
		minY = optionsPanel.minRangeBox.getDoubleValue();
	    } catch (SyntaxException ex){
		return false;
	    }

	    if (maxY<=minY) {
		toolPanel.getPlotPanel().defaultY();
		errorMessage("The max Range value must be larger \n"+
			     "than the min. Range value");
		optionsPanel.defaultRange.setSelected(true);

		return false;
	    }else{
		    
		toolPanel.getPlotPanel().setYMax(maxY);
		toolPanel.getPlotPanel().setYMin(minY);
	    }
	    
	}

	
	// Parse the axes labels

	toolPanel.getPlotPanel()
	    .setAxisLabels(optionsPanel.xAxisLabel.getEditBox(),
			   optionsPanel.yAxisLabel.getEditBox());	


	// Is the aspect ratio one to one selected?


	if(optionsPanel.aspectRatio.isSelected()){
	    toolPanel.getPlotPanel().oneToOne();}
 
	return true;
    }

    /* This function parses all of the input in the input panel */

    protected boolean parseInput() {
	
	try {
	    function = inputPanel.functionBox.getFunction();
	    indVar = inputPanel.indVariableBox.getVariable();

	    if ((!(function.getVariables()
		   .contains(inputPanel.indVariableBox.getVariable())))||
		(function.getNumVariables()!=1)){
		
		errorMessage("Error in function: " 
			     + inputPanel.functionBox.getEditBox()
			     +"\nYour function contains variables: "
			     + function.getVariables() +"\n"
			     + "The stated variable is: "
			     + inputPanel.indVariableBox.getEditBox());
		
		return false;
	    }
	} catch (SyntaxException ex){
	    return false;
	}
	
	try { 
	    indVar = new Variable(inputPanel.indVariableBox.getEditBox());
	    derivative = function.differentiate(indVar);
	} catch(UnacceptableVariableException ex){
	    return false;
	} catch(VariableUndefinedException ex){
	    return false;
	}
	
	try {

	    double xmin = inputPanel.minDomainBox.getDoubleValue();
	    double xmax = inputPanel.maxDomainBox.getDoubleValue();
	    toolPanel.getPlotPanel().setXMin(xmin);
	    toolPanel.getPlotPanel().setXMax(xmax);
	} catch (SyntaxException ex){
	    return false;
	}

	return true;
	
    }
    

    class PlotButtonListener implements ActionListener
    {
           public void actionPerformed(ActionEvent e)
	{
	    if(parseInput()){
		
		if (!(inputPanel.overlay.isSelected()))
		    toolPanel.getPlotPanel().clearGraphicsList();
	
		if(function!=null){
		    createPoints();}
		
		if(line != null){
		    toolPanel.getPlotPanel().addGraphicsObject(line);}
		
		if(parseOptions()){
		    toolPanel.getPlotPanel().repaint();
		}
	    }
	    
	}
    }
    
}