// FixedPoint Applet
// Concept by Bill Ziemer
// Programming by Brendon Cheves & Bill Ziemer
// e-mail: brendon@csulb.edu  wziemer@csulb.edu

// Copyright 1996 by California State University, Long Beach
// expr package Copyright 1996 by Darius Bacon

// Created 10/22/96
// Last update 10/22/96


// java packages
import java.applet.*;
import java.awt.*;
import java.lang.Math;


// our packages
import expr.*;			// f(x) parser by Darius Bacon
import cool_utils.*;	// cool utilities we made


public class FixedPoint extends Applet
{
	static final boolean DEBUG = false;

	double first = 0, last = 1;
	
	double[] spiderPoints;
	
	
	Color current_color = Color.black;

	Button helpButton, aboutButton, plusdx, minusdx, okBtn;

	Label sliderLabel, pOldLabel, pNewLabel, realFixedPointLabel, errorLabel;

	int nLimit, npoints = 0;

	double	theSum, theIntegral, realFixedPoint,  approxErr, error;
   		
    CardLayout
    	theCards;
    	
    String	theTopStyle = "Constant";
    
    Canvas	theCanvas;
    
    Panel	theDrawingPanel, sliderPanel, theFeedbackPanel, okBtnPanel,
    		mainCd, aboutCd, helpCd, theAboutPanel, theHelpPanel, dxPanel;
    
    Rectangle	dxWidth;
    	
    Image offscreenImage;

   	Graphics offscreenGraphics, offscreenInstance;

	MultiLineLabel aboutLabel;
	
	Dimension d;
	
	// EZGridLayout manager stuff
	EZGridSettings gridSettings;
	EZGridLayout EZGL;

	// Our function class, based on the Java Polygon class
	ZFunction theFunction;

	
    public void init()
    {
     	resize(640,420);
   	   	
   	   	error = 1E-10;
   	   	
  	   	theFunction = new ZFunction();
   	   	
    	// create a card layout
    	theCards = new CardLayout(10,10);
		this.setLayout(theCards);
 		
 		// create the main card
 		mainCd = new Panel();
 		this.add("main",mainCd);
 		mainCd.setLayout(new BorderLayout(10,10));
 		
 		// create the about card
 		aboutCd = new Panel();
 		this.add("about",aboutCd);
  		aboutCd.setLayout(new BorderLayout(10,10));
		
 		// create the help card
 		helpCd = new Panel();
 		this.add("help",helpCd);
  		helpCd.setLayout(new BorderLayout(10,10));
  

    	// create the drawing panel and add it to the main cd
   		theDrawingPanel = new Panel();
		mainCd.add("Center",theDrawingPanel);
 		theDrawingPanel.setLayout(new BorderLayout(10,10));

		  		  	
	  	// create the EZGridLayout
  		EZGL = new EZGridLayout(12,2);
	
		
		// create the feedback panel and add it to the main cd
 		theFeedbackPanel = new Panel();
		mainCd.add("South",theFeedbackPanel);
 		theFeedbackPanel.setLayout(EZGL);
 		 
 		minusdx = new Button("-");
		gridSettings = new EZGridSettings(minusdx,1,1,1,1);
		theFeedbackPanel.add(minusdx);
 		EZGL.addLayoutInfo(gridSettings);


  		sliderLabel = new Label();
		sliderLabel.setText("n = " + String.valueOf(npoints+1));
		gridSettings = new EZGridSettings(sliderLabel,2,1,1,1);
		theFeedbackPanel.add(sliderLabel);
		EZGL.addLayoutInfo(gridSettings);
		 		
 		plusdx = new Button("+");
		gridSettings = new EZGridSettings(plusdx,3,1,1,1);
		theFeedbackPanel.add(plusdx);
		EZGL.addLayoutInfo(gridSettings);
				 				
		pOldLabel = new Label();
		gridSettings = new EZGridSettings(pOldLabel,4,1,3,1);
		theFeedbackPanel.add(pOldLabel);
		EZGL.addLayoutInfo(gridSettings);

		pNewLabel = new Label();
		gridSettings = new EZGridSettings(pNewLabel,7,1,3,1);
		theFeedbackPanel.add(pNewLabel);
		EZGL.addLayoutInfo(gridSettings);

		realFixedPointLabel = new Label();
		gridSettings = new EZGridSettings(realFixedPointLabel,10,1,3,1);
		theFeedbackPanel.add(realFixedPointLabel);
		EZGL.addLayoutInfo(gridSettings);
		
		errorLabel = new Label();
		gridSettings = new EZGridSettings(errorLabel,7,2,3,1);
		theFeedbackPanel.add(errorLabel);
		EZGL.addLayoutInfo(gridSettings);
		
		helpButton = new Button("Help");
		gridSettings = new EZGridSettings(helpButton,1,2,1,1);
		theFeedbackPanel.add(helpButton);
		EZGL.addLayoutInfo(gridSettings);

		aboutButton = new Button("About...");
		gridSettings = new EZGridSettings(aboutButton,2,2,1,1);
		theFeedbackPanel.add(aboutButton);
		EZGL.addLayoutInfo(gridSettings);
		
		theFeedbackPanel.resize(preferredSize());
		
		// create the about panel and add it to the about cd
		theAboutPanel = new Panel();
		aboutCd.add("Center",theAboutPanel);
		theAboutPanel.setLayout(new FlowLayout());
		
		// create the OK button panel and add it to the about cd
		okBtnPanel = new Panel();
		aboutCd.add("South",okBtnPanel);
		
		// add the stuff to the about panel
		Font theFont = new Font("TimesRoman", Font.BOLD, 18);
		aboutLabel = new MultiLineLabel(
			"Riemann Sum Applet\nCopyright 1996 by California State University, Long Beach\nWritten by Bill Ziemer and Brendon Cheves\nFunction Parsing Copyright 1996 by Darius Bacon",
			10, 10, MultiLineLabel.CENTER);
		aboutLabel.setFont(theFont);
		theAboutPanel.add(aboutLabel);
		
		// add the OK button
		okBtn = new Button("OK");
		okBtnPanel.add(okBtn);

		// initialize the offscreen stuff
		d = theDrawingPanel.size();

		offscreenImage = this.createImage(600,500);
		offscreenGraphics = (Graphics)offscreenImage.getGraphics();
		if (offscreenGraphics == null) System.out.println("Error: Null offscreen graphics");
		
		// set up the initial screen
		theFunction.parse("(1-x^2) / 2");
		spiderPoints = new double[1];
		npoints = 0;
		nLimit = 0;
		spiderPoints[0] = 0.2;
		realFixedPoint = theFunction.fixedPoint(0.2,error);
		first = theFunction.xMin;
		last = theFunction.xMax;
		
		// finally, show the main card
		theCards.show(this,"main");
    }
    
    
    public void paint(Graphics g)
    {
    	// who cares about g, we know where we want to draw
		offscreenGraphics.clearRect(0,0,600,500);
		offscreenGraphics.setColor(Color.white);
		offscreenGraphics.fillRect(0,0,600,500);
		
		theFunction.plot((Graphics)offscreenGraphics);
		
		drawWeb(offscreenGraphics);
		
		drawAxis(offscreenGraphics);
 		drawLimits(offscreenGraphics);
		drawLabels(offscreenGraphics);
		
		// force the offscreen buffer to draw into the drawing panel
		theDrawingPanel.getGraphics().drawImage(offscreenImage,0,0,theDrawingPanel);   		
	}
     
    
	// Override update() for double buffering
	public void update(Graphics g)
	{
		paint(g);
	}


	public Insets insets()
	{
		return new Insets(10,10,10,10);
	}

 	
 	// handle GUI events
    public boolean action(Event e, Object arg)
	{
		
       	if(e.target == minusdx)
      	{
       		if(npoints > 0)
      		{
      			npoints--;
      			paint(theDrawingPanel.getGraphics());

      		}
      		return true;
      	}
      	else if(e.target == plusdx)
      	{
      		double[] tmp;
      		npoints++;
			
			if(npoints > nLimit)
			{
			    tmp = new double[npoints+1];
			    System.arraycopy(spiderPoints, 0, tmp, 0, npoints);
			    spiderPoints = tmp;
			    nLimit = npoints;
				spiderPoints[npoints] = theFunction.value(spiderPoints[npoints-1]);
				System.out.println("p(" + (npoints) + ") = " + spiderPoints[npoints]);
 			}
     		paint(theDrawingPanel.getGraphics());
      		
      		return true;
      	}
 
      	else if(e.target == aboutButton)
      	{
			theCards.show(this,"about");
      		return true;
      	}
      	else if(e.target == okBtn)
      	{
 			theCards.show(this,"main");
     		return true;
      	}

 		else return super.action(e,arg);	
	}

	
	// draw the axis
	public void drawAxis(Graphics g)
	{
 	  	g.setColor(Color.black);
		
		// x axis
        g.drawLine(0,theFunction.yRealToPixel(first),580,theFunction.yRealToPixel(first));	
        g.drawString("X",585,theFunction.yRealToPixel(first) + 4);

        // y axis
        g.drawLine(40,20,40,500);       
        g.drawString("Y",38,17);
        
        //draw y = x
        g.drawLine(theFunction.xRealToPixel(first),theFunction.yRealToPixel(first),
                   theFunction.xRealToPixel(last),theFunction.yRealToPixel(last));
        g.drawString("y = x", theFunction.xRealToPixel(last), theFunction.yRealToPixel(last));
 	}


	// draw all the necessary labels
	public void drawLabels(Graphics g)
	{
		g.setColor(Color.black);
		
	 	sliderLabel.setText("n = " + String.valueOf(npoints));
		pOldLabel.setText("p("+(npoints)+") = " + String.valueOf( spiderPoints[npoints] - ((1E6 * spiderPoints[npoints]) % 1) / 1E6));
		pNewLabel.setText("p("+(npoints+1)+") = " + String.valueOf( theFunction.value(spiderPoints[npoints]) - ((1E6 * theFunction.value(spiderPoints[npoints])) % 1) / 1E6));

		realFixedPointLabel.setText("Real Fixed Point = " + String.valueOf(realFixedPoint - ((1E6 * realFixedPoint) % 1) / 1E6));
		errorLabel.setText("Error = " + String.valueOf( realFixedPoint - theFunction.value(spiderPoints[npoints]) ));
	}

	
	// draw limits in the proper places
	public void drawLimits(Graphics g)
	{
      	g.setColor(Color.red);
		
		// x limit
		g.drawString(String.valueOf(last),(int)theFunction.XSCALE + 44,theFunction.yRealToPixel(first) + 14);
		g.drawLine((int)theFunction.XSCALE + 40,theFunction.yRealToPixel(first) - 2,(int)theFunction.XSCALE + 40,theFunction.yRealToPixel(first) + 2);
		
		// y limit
		theFunction.xvar.set_value(last);
		g.drawString(String.valueOf(theFunction.expr.value() - ((1E2 * theFunction.expr.value()) %1) / 1E2),5,theFunction.yRealToPixel(theFunction.expr.value()) + 3);
		g.drawLine(38,theFunction.yRealToPixel(theFunction.expr.value()),42,theFunction.yRealToPixel(theFunction.expr.value()));
		
		// fixed point
		drawDashedLine(theFunction.xRealToPixel(realFixedPoint), theFunction.yRealToPixel(realFixedPoint), theFunction.xRealToPixel(realFixedPoint), theFunction.yRealToPixel(first),g);
		
  	}

 	    
	public void drawWeb(Graphics g)
	{
		int x1,x2,y1,y2;
		//draw the history of iterations in green
		g.setColor(Color.green);
		for(int i=0; i < npoints; i++)
		{
			x1 = theFunction.xRealToPixel(spiderPoints[i]);
			x2 = theFunction.xRealToPixel(spiderPoints[i+1]);
			y1 = theFunction.yRealToPixel(first);
			y2 = theFunction.yRealToPixel(spiderPoints[i+1]);
			
			g.drawLine( x1, y1, x1, y2);
						
			g.drawLine(	x1, y2, x2, y2);
						
			g.drawLine(	x2, y2, x2, y1);
					
		}
		
		// draw the current iteration in red 
		x1 = theFunction.xRealToPixel(spiderPoints[npoints]);
		x2 = theFunction.xRealToPixel(theFunction.value(spiderPoints[npoints]));
		y1 = theFunction.yRealToPixel(first);
		y2 = theFunction.yRealToPixel(theFunction.value(spiderPoints[npoints]));
		g.setColor(Color.red);
		if( (first < spiderPoints[npoints]) && (spiderPoints[npoints] < last))
		{
			drawArrowheadLine(x1, y1, x1, y2,g);
			drawArrowheadLine(x1, y2, x2, y2,g);
			drawArrowheadLine(x2, y2, x2, y1,g);
			
						
			g.drawString("p(" + (npoints) + ")",x1-5,y1 + 14);
			g.drawString("p(" + (npoints+1) + ")",x2-5,y1 + 28);
			g.drawString("f(p(" + (npoints) + "))", 0, y2+5 );
			g.drawLine(38,theFunction.yRealToPixel(theFunction.expr.value()),42,theFunction.yRealToPixel(theFunction.expr.value()));
		}
				
	}
	
	// Return info for an about box browser option
	public String getAppletInfo()
	{
		return "Fixed Point Java Applet by Bill Ziemer & Brendon Cheves";
	}

	
	// Called from JavaScript only
	public void drawFunction(String f, String g)
	{
		theFunction.parse(f);
		npoints = 0;
		nLimit = 0;
		Float gWrapInFloat = new Float(g);
		spiderPoints[0] = gWrapInFloat.doubleValue();
		realFixedPoint = theFunction.fixedPoint(gWrapInFloat.doubleValue(),error);
		first = theFunction.xMin;
		last = theFunction.xMax;
		System.out.println("p(0) = " + spiderPoints[0]);

		
		//paint(theDrawingPanel.getGraphics());
		repaint();
	}
	
	
	// Unitl we can get CoolGraphics.class working, got to put these cool utilities here
	public void drawArrowheadLine(int x1, int y1, int x2, int y2, Graphics g)
	{
		int a = 0;
		int arrowW = 26, arrowH = 26;
		int arrowX, arrowY;
		
		// draw the line first
		g.drawLine(x1, y1, x2, y2);
		
		// calculate arrowhead locations
		arrowX = x2 - (arrowW / 2);
		arrowY = y2 - (arrowH / 2);
		
		if(x1 == x2)
		{
			if (y2 > y1)
			{
				a = 270;
			}
			else
			{
				a = 90;
			}
		}
		else
		{
			a = (int)Math.atan((double)(y2 - y1) / (double)(x2 - x1));	// -90 to 90
			if(x2 < x1)
			{
				a = 180 - a;
			}
		}

		// draw the arrowhead
		g.fillArc(arrowX, arrowY, arrowW, arrowH, a + 165, 30);
	}

	public void drawDashedLine(int x1, int y1, int x2, int y2, int dashLength, Graphics g)
	{
		dashedLine(x1, y1, x2, y2, dashLength,g);
	}
	
	public void drawDashedLine(int x1, int y1, int x2, int y2, Graphics g)
	{
		dashedLine(x1, y1, x2, y2, 4,g);
	}

	private void dashedLine(int x1, int y1, int x2, int y2, int dashLength, Graphics g)
	{
		float m;
		int i, tmp;
		
		if(x2 < x1)
		{
			tmp = x1;
			x1 = x2;
			x2 = tmp;
			
			tmp = y1;
			y1 = y2;
			y2 = tmp;
		}
		
		if(x2 != x1)
		{
			m = (y2 - y1) / (x2 - x1);
			
			for(i = x1; i < x2; i += (2 * dashLength))
			{
				g.drawLine( i, (int)((m * (i - x1)) + y1),  (int)(i + Math.sqrt(dashLength / (1 + (m*m)))), (int)((m * ((i + Math.sqrt(dashLength / (1 + (m*m)))) - x1)) + y1));
			}
			
			// guarantee last dash
			if( (i * 2 * dashLength + x1 < x2))
			{
				g.drawLine( i * 2 * dashLength + x1, (int)((m * (i * 2 * dashLength)) + y1),  x2, y2);
			}
				
				
		}
		else	// vertical
		{
			if(y2 < y1)
			{
				tmp = y1;
				y1 = y2;
				y2 = tmp;
			}
			
			for(i = y1; i < y2; i += (2 * dashLength))
			{
				g.drawLine( x1, i, x1, i + dashLength);
			}
			
			// guarantee last dash	
			if( (i * 2 * dashLength + y1 < y2))
			{
				g.drawLine( x1, i * 2 * dashLength + y1,  x1, y2);
			}
		}
	}


}


