/** Use JWS to modify MyApplet12.java (previouslyAnimatorApplet4.java) 
 ** by experimenting with different values of squareSize, and changing 
 ** the applet to have some more interesting behavior, e.g. randomly 
 ** change the squareSize after every n (experiment with different values 
 ** of n) frames using % (the mod operation). Also, edit MyApplet12.html 
 ** in the project directory, both to run the MyApplet12.class file you 
 ** create, and so that everything looks nice and works properly.      **/

/** USE EXISTING JAVA CLASSES **/

import java.awt.*;
import java.applet.Applet;

/** EXTEND THE JAVA APPLET CLASS AND IMPLEMENT THE RUNNABLE INTERFACE **/

public class MyApplet12 extends Applet implements Runnable
   {int frameNumber = -1;            
    int fps = 18;                      // Define the default fps (frames per second)
    int delay;                         // as a global, in order to initialize in init()
    Thread animatorThread;             // and control the frame-to-frame delay in run().
    boolean frozen = true;
    Font font=new Font("TimesRoman",Font.BOLD,26);
    String framestr;
    int squareSize = 40;                 // Define globals for the checkerboard square
    boolean createTopSquareNextFrame = false; // size, and the frame-to-frame top square. 
    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 str1, str2;                     
        str1 = getParameter("fps");        // Possibly, get HTML string specifying
        try                                // the fps.
         {if (str1 != null)
           {fps = Integer.parseInt(str1);} // If possible, convert HTML fps to an
          }                                // integer.
        catch (Exception e) {}             // In case the try caused something strange.
        str2 = getParameter("squareSize"); // Possibly, get HTML string specifying 
        try                                // the squareSize.
         {if (str2 != null) 
           {squareSize = Integer.parseInt(str2);} // If possible, convert HTML squareSize
          }                                       // to an integer.
        catch (Exception e) {}             // In case the try caused something strange.
        }

    /** CLICK TO START OR STOP THE APPLET **/

    public boolean mouseDown(Event e, int x, int y)
       {if (frozen)
         {frozen = false;
          start();}
        else
         {frozen = true;
          animatorThread = null;           // Instead of calling stop() here, since
          }                                // stop() now nullifies our preImage context,
        return true;                       // nullify only the animation thread in case
        }                                  // we want to build on our current preImage.

    /** 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.
        int delayLong = (fps > 0) ? (10000/fps): 1000;   // Convert fps to milisecond 
        int delayShort = (fps > 0) ? (1000/fps): 100;    // delay time between frames. 
        
        while (Thread.currentThread() == animatorThread) // The animation loop.
           {frameNumber++;                               // Advance the frame number.
            framestr = "Pretty Cool Huh!! " + frameNumber;           // Create the frame string.                             
            repaint();                                   // Display the frame.
            if (frameNumber != 0)                        // Start with a longer delay
             {delay = delayShort;}                       // time for Frame 0, in order 
            else                                         // to avoid timing problems
             {delay = delayLong;}                        // when the thread starts up.      
            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
     {if (!frozen) 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();               // before painting the screen.    
      boolean createTopSquare;
      boolean fillSquare;
      int frameShift;
      int squareWidth, squareHeight;
      int x = 0, y = 0;
      int extendRow = 0;
           
      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
      preImageGraphics.setFont(font);                  // background color (red),
      preImageGraphics.setColor(Color.green);          // and then create the next
      preImageGraphics.drawString(framestr,155,39);    // offscreen preImage.   
      
      /* Create the current checkerboard for the current frame. This is done by 
         starting with the red background and then moving down the columns of the
         checkerboard (starting with the leftmost column), deciding whether or 
         not to paint a black square on top of the red background which creates
         the checkerboard effect. Frame 0 begins by skipping a red square at the
         top of the leftmost column, then paints a black one below it, skips a
         red square below it, paints a black one below it, etc. Frame 1 begins
         its leftmost column with a black square one pixel wide, frame 2 with
         a black square 2 pixels wide, etc. These frames are then displayed in
         sequence, creating the checkerboard moving to the right effect.       */
     
      /* Set up the current frame and remember appropriately for the next frame */
      
      createTopSquare = createTopSquareNextFrame; // Create top square: true/false?
      frameShift = frameNumber % squareSize; // Compute the frameShift, which is 
                                             // a number from 0 to squareSize-1.
      if (frameShift == 0)                   // frameShift equals the remainder
       {squareWidth = squareSize;            // after dividing frameNumber by
        createTopSquareNextFrame = !createTopSquare; // squareSize. So, % is 
        }                                            // the mod operation.
      else                                           // If frameShift isn't zero (mod),
       {squareWidth = frameShift;                    // start at the top of the leftmost
        createTopSquareNextFrame = createTopSquare;  // column with a partial square--
        }                                            // a rectangle of width frameShift
                                                     // (pixels).
                                                    
      fillSquare = createTopSquare;          // Fill or don't fill the leftmost                     
                                             // top (partial) square of the current                                                 
                                             // frame, depending on the value of the
                                             // global variable createTopSquare.
                                             
      /* Begin the checkerboard for the current frame */        
      
      while (x < d.width)                    // Begin drawing the first row, but 
       {int extendColumn = 0;                // proceed down each column from top
        while (y < d.height)                 // to bottom, starting from the left.
         {extendColumn += squareSize;   
          if (extendColumn <= d.height)      // Test column extension.
           {squareHeight = squareSize;}
          else                               // If a full square won't fit at the
           {squareHeight = d.height - y;}    // bottom of the column, then reduce
          if (fillSquare)                    // its height.
           {preImageGraphics.fillRect(x, y, squareWidth, squareHeight);
            fillSquare = false;
            } 
          else 
           {fillSquare = true;}
          y += squareHeight;                 // Increment y. 
          }                                  // End of while y loop.
        x += squareWidth;                          // Increment x.                
        y = 0;                                     // Start at the top of the next
        extendRow += squareWidth;                  // column.
        if (extendRow <= d.width)                  // Test row extension.  
         {squareWidth = squareSize;}
        else                                       // If a full square won't fit at the
         {squareWidth = d.width - x;}              // far right, then reduce its width. 
        createTopSquare = !createTopSquare;        // Set up to do the opposite at the
        fillSquare = createTopSquare;              // top of the next column.
        }                                          // End of while x loop.              
                                                   // Offscreen preImage now completed.                                                                                   
       g.drawImage(preImage,0,0,this);             // Paint preImage on the screen.
       }

    /** STOP THE ANIMATION THREAD **/

    public void stop()
     {animatorThread = null;     // The user either clicked to stop or left the page,
      preImageGraphics = null;   // so wipe out the current thread and preImage context.
      preImage = null;
      } 
    }