 /** This applet combines the code in Simple Applet 7
  ** and Animator Applet 3 in an interesting way. It                  
  ** begins with user interaction that switches over 
  ** to an animation.
  **                                                                      
  ** Written by John Pais, February 1998.          **/


/** USE EXISTING JAVA CLASSES **/

import java.awt.*;
import java.applet.Applet;

/** EXTEND THE JAVA APPLET CLASS AND IMPLEMENT THE RUNNABLE INTERFACE **/

public class AnimatorApplet8 extends Applet implements Runnable
   {final int maxnumlines = 10;                       // Define max number of lines,
    final int maxsize = maxnumlines + 5;              // max array size, 
    Point startpoint[] = new Point[maxsize];          // the array of start points,
    Point endpoint[] = new Point[maxsize];            // the array of end points, 
    Point anchorpoint, connectpoint;                  // the current anchor point and
    int entrynum = 0;                                 // connection point, and the 
						      // current array entry number,
						      // starting with 0 (instead of 1).
   
    Font permitfont = new Font("TimesRoman",Font.BOLD+Font.ITALIC,18);  
    FontMetrics permitfontm = getFontMetrics(permitfont);   
    String permitText[] = new String[maxsize+1];
    int xpermitText[] = new int[maxsize+1];
    String punish = "Now I must punish you";
    String forbidbegin = "I real";
    String forbidend = "y forbid you from drawing any more lines.";
    int tempentrynum = 0;
    String exclamation = "!"; 
    int fontPoint = 12;
    Font resistfont = new Font("TimesRoman",Font.BOLD,fontPoint);
    FontMetrics resistfontm = getFontMetrics(resistfont);
    int xresistfont;
    int yresistfont;  
    String resist = "RESISTANCE";
    
    int frameNumber = -1;              
    int delay;                         
    Thread animatorThread;
    boolean frozen = true;
    String framestr = "";
    boolean eraser = false;
    int linesleft = maxnumlines;
    
    Dimension preImageDim;               // Define preImage variables needed to
    Image preImage;                      // create the offscreen copy of the image
    Graphics preImageGraphics;           // we want to paint on the screen.

    /** INITIALIZE THE APPLET **/

    public void init()
       {setBackground(Color.blue);
	String str;
        int fps = 2;                       // Define the default fps (frames per second).
        str = getParameter("fps");         // Possibly, get HTML string specifying the
        try                                // fps.
         {if (str != null)
           {fps = Integer.parseInt(str);}  // If possible, convert HTML fps to an
          }                                // integer.
        catch (Exception e) {}             // In case the try caused something strange.
        delay = (fps > 0) ? (1000/fps): 100; // Convert fps to the milisecond delay
        }                                    // time between frames.

   
     /** HANDLE THE MOUSEDOWN EVENT **/    
    
     public boolean mouseDown(Event e, int x, int y)
	{if (entrynum < maxsize)                 
	  {anchorpoint = new Point(x,y);              // Initialize both points to
	   connectpoint = anchorpoint;                // the current mouse position.  
	   return true;}                              // Note that this will give us a
	 else                                         // way to prevent painting a single
	  {return false;}                             // point (see the mouseUp method).
	 }
	 
     /** HANDLE THE MOUSEDRAG EVENT **/    
    
     public boolean mouseDrag(Event e, int x, int y)
	{if (entrynum < maxsize)                      // A mouseDrag event occurs only
	  {connectpoint = new Point(x,y);             // when the mouse is moved. So,
	   repaint();                                 // we must have connectpoint != 
	   return true;}                              // anchorpoint, which ensures that
	 else                                         // a temporary (red) line will be 
	  {return false;}                             // painted (see the paint method).
	 }	 
	 	 
     /** HANDLE THE MOUSEUP EVENT **/    
    
     public boolean mouseUp(Event e, int x, int y)
	{if (entrynum < maxsize && connectpoint != anchorpoint) // Don't add a single point.
	  {addline();                                           // Add new points to the arrays.
	   if (!eraser)
	    {repaint();}                                        // Repaint everything, including 
	   else                                                 // the new line (all yellow).
	    {frozen = false;
	     start();
	     } 
	   return true;                                       
	   }
	 else                                         
	  {return false;}      
	 }
	 
	 
     /** THE ADDLINE METHOD **/   
     
     void addline()
	{startpoint[entrynum] = anchorpoint;          // Record the startpoint and endpoint
	 endpoint[entrynum] = connectpoint;           // of the new line.
	 entrynum++;                                  // Add 1 to the current entry number.
	 anchorpoint = null;                          // Nullifying these points prevents the
	 connectpoint = null;                         // line from being painted twice (see               
	 }                                            // the paint method).
	      
    /** GENERATE AND/OR START THE ANIMATION THREAD **/

    public void start()
       {if (frozen) { }                    // The animation is supposed to stay frozen.
        else                               // Otherwise, generate and/or start the
         {if (animatorThread == null)      // animation thread. Apparently, if run
           {animatorThread = new Thread(this);}  // breaks after catching an exception,
          animatorThread.start();                // the animatorThread though not null
          }                                      // will still need to be restarted.
        }

    /** CREATE AND DISPLAY FRAMES OF THE ANIMATION THREAD **/

    public void run()
       {Thread.currentThread().setPriority(Thread.MIN_PRIORITY); // Set the priority low.
        long startTime = System.currentTimeMillis();             // Remember start time.
        while (Thread.currentThread() == animatorThread)         // The animation loop.
           {frameNumber++;                               // Advance the frame number.
            framestr = "Frame " + frameNumber;           // Create the frame string.
            repaint();                                     // Display the frame.
            try                                          // Sleep for the delay period.
	     {startTime += delay;
	      Thread.sleep(Math.max(0,
                                    startTime-System.currentTimeMillis()));
	      }
            catch (InterruptedException e) {break;}     // In case the try caused
            }                                           // something strange, exit
         }                                               // the while loop.


    /** CREATE THE CURRENT FRAME OF THE ANIMATION THREAD **/

    public void paint(Graphics g)         // This technique of painting by calling
     {update(g);}                         // update, prevents the default (and hidden)            
					  // behavior of update, and instead allows
    public void update(Graphics g)        // the creation of an offscreen preImage 
     {Dimension d = size();               // preImage before painting the screen.           
      if ((preImageGraphics == null) ||              
	  (d.width != preImageDim.width) ||         // Compare the current applet
	  (d.height != preImageDim.height))         // viewing size to our preImage,
	   {preImageDim = d;                        // and if different, resize our 
	    preImage = createImage(d.width,d.height); // next preImage.
	    preImageGraphics = preImage.getGraphics();
	    }                                
       preImageGraphics.setColor(getBackground());      // Clear the previous preImage
       preImageGraphics.fillRect(0,0,d.width,d.height); // by filling with the current
                				        // background color (blue),
        						// and then create the next
      							// offscreen preImage.

      if (entrynum == 0)
       {permitText[entrynum] = "I will permit you to draw "+ maxnumlines + " lines.";}
      else if (entrynum < maxnumlines)
       {permitText[entrynum] = "I will permit you to draw "+ (maxnumlines-entrynum) + " more lines.";}
      else if (entrynum == maxnumlines) 
       {permitText[entrynum] = "I forbid you from drawing any more lines.";}
      else if (entrynum < maxsize)
       {if (entrynum != tempentrynum)
	 {forbidbegin += "l";
          permitText[entrynum] = forbidbegin + forbidend;
	  tempentrynum = entrynum;
	  }
	}  
      else
       {if (frameNumber < 30)
	// {punish += entrynum;}
	 {punish += exclamation;}
	permitText[entrynum] = punish;
	}
      xpermitText[entrynum] = (size().width - permitfontm.stringWidth(permitText[entrynum]))/2;
      preImageGraphics.setFont(permitfont);
      preImageGraphics.setColor(Color.red);
      preImageGraphics.drawString(permitText[entrynum],xpermitText[entrynum],15);
      preImageGraphics.setColor(Color.black);
      preImageGraphics.fillRect(0,20,500,10);
      if (frameNumber > maxnumlines)
       {Font resistfont=new Font("TimesRoman",Font.BOLD,fontPoint);
	FontMetrics resistfontm = getFontMetrics(resistfont);
	xresistfont = (size().width - resistfontm.stringWidth(resist))/2;
	yresistfont = (size().height - 30)/2 + 30;
	preImageGraphics.setFont(resistfont);
	preImageGraphics.setColor(Color.magenta);
	preImageGraphics.drawString(resist,xresistfont,yresistfont);
	fontPoint += 10;
	fontPoint %= 70;
	//if (resist != "FUTILE")
	// {fontPoint %= 70;}
	//else 
	// {fontPoint %= 100;}
	fontPoint = Math.max(fontPoint,12);
	if (resist == "RESISTANCE" && fontPoint == 12)
	 {resist = "IS";}
	else if (resist == "IS" && fontPoint == 12)
	 {resist = "FUTILE";}
        else if (resist == "FUTILE" && fontPoint == 12)
	 {resist = "RESISTANCE";}
	}
      if (entrynum < maxsize)
       {preImageGraphics.setColor(Color.yellow);
	for (int i = 0; i < entrynum; i++)                   // Note that nothing happens here, 
	  {if (i < maxnumlines)                              // until a mouseDown event occurs.  
	    {preImageGraphics.drawLine(startpoint[i].x, startpoint[i].y, // Repaint all lines from
					 endpoint[i].x, endpoint[i].y);} // 0 to maxnumlines-1.    
	   }
	if (connectpoint != anchorpoint)                     // Paint a temporary red line in 
	 {preImageGraphics.setColor(Color.red);              // response to a mouseDrag event.
	  preImageGraphics.drawLine(anchorpoint.x, anchorpoint.y,
		      connectpoint.x, connectpoint.y);}
	if (entrynum == maxsize-1) 
	 {eraser = true;} 
	}		      
       else if (entrynum == maxsize)
	{preImageGraphics.setColor(Color.yellow);
	 if (linesleft == maxnumlines)
	  {for (int i = 0; i < maxnumlines; i++)                              
	   preImageGraphics.drawLine(startpoint[i].x, startpoint[i].y,    
				       endpoint[i].x, endpoint[i].y);
	   linesleft -= 1;	     
	   }
	 else 		     
	  //Font font=new Font("TimesRoman",Font.BOLD,fontPoint);
	  //g.setFont(font);
          //g.setColor(Color.red);
          //g.drawString(framestr,120,110);     
	  {for (int i = 0; i < linesleft+1; i++)                              
	    {preImageGraphics.drawLine(startpoint[i].x, startpoint[i].y,    
			  endpoint[i].x, endpoint[i].y);
	     }
	   if (linesleft >= 0)
	    {preImageGraphics.setColor(Color.red);
	     preImageGraphics.drawLine(startpoint[linesleft].x, startpoint[linesleft].y,    
			endpoint[linesleft].x, endpoint[linesleft].y);	     
	     linesleft -= 1;
	     }   
	   }     
         }    
       g.drawImage(preImage,0,0,this);   
       }
    		 
    /** STOP THE ANIMATION THREAD **/

    public void stop()
       {animatorThread = null;}  // The user either clicked to stop or left the page,
    }                            // so wipe out the current thread.
