/*
 * @(#)Juggling.java                          1.0f 95/05/01 Chris Seguin
 * E-mail:  seguin@uiuc.edu
 *
 * Copyright (c) 1995 University of Illinois (UIUC)
 *
 * I MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UIUC SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * To be fair, I used code created by James Gosling and extended it 
 * to juggle objects, rather than just animate a gif image.
 *
 */

import java.io.InputStream;
import java.awt.*;
import java.net.*;

/**
 * JugglingImages class. This is a container for a list
 * of images that are animated.
 *
 * @author      Chris Seguin
 * @version     1.0f, May 1, 1995
 */
class JugglingImages {
    /**
     * The images.
     */
    Image imgs[];

    /**
     * The number of images actually loaded.
     */
    int nImagesCount = 0;

    /**
     * Load the images, from dir. The images are assumed to be
     * named T1.gif, T2.gif...
     */
    JugglingImages(URL context, String dir, Juggling parent) {

        nImagesCount = 0;
        imgs = new Image[10];
        int nWidth = 0;

        for (int i = 1; i < imgs.length; i++) {

            Image im = parent.getImage(parent.getDocumentBase(), 
                                       dir + "/T" + i + ".gif");
            
            imgs[nImagesCount++] = im;
        }
    }
}

/**
 * BallPaths class. This is a container for a paths
 * of juggling balls
 *
 * @author      Chris Seguin
 * @version     1.0f, May 1, 1995
 */
class BallPaths {
    
    /**
     * Arrays containing the path of the balls
     */
    int pnX[] = {0};
    int pnY[] = {0};

    int nLength = 1;

    /**
     * LookupX - looks up the appropriate value of X
     */
    public int LookupX (int nIndex) 
    {
    if ((nIndex > nLength) || (nIndex < 0))
        return 0;

    return pnX[nIndex];
    }

    /**
     * LookupY - looks up the appropriate value of Y
     */
    public int LookupY (int nIndex) 
    {
    if ((nIndex > nLength) || (nIndex < 0))
        return 0;

    return pnY[nIndex];
    }

    /**
     * Length - the number of data points stored in the path
     */
    public int Length ()
    {
    return nLength;
    } 
}


/**
 * CascadeBallPaths class. This is a container for a paths
 * of juggling balls, the balls are moving in a standard 
 * cascade pattern
 *
 * @author      Chris Seguin
 * @version     1.0f, May 1, 1995
 */
class CascadeBallPaths extends BallPaths {
    
    /**
     * Arrays containing the path of the balls
     */
    int pnX[] = {     20,   24,   27,   31,   35, 
                      40,   45,   50,   55,   60, 
                      65,   70,   75,   80,   85, 
                      90,   95,  100,  105,  110, 
                     115,  120,  125,  130,  135, 
                     143,  144,  141,  138,  134, 
                     130,  126,  123,  119,  115, 
                     110,  105,  100,   95,   90, 
                      85,   80,   75,   70,   65, 
                      60,   55,   50,   45,   40, 
                      35,   30,   25,   20,   15, 
                       7,    6,    9,   12,   16
                     };

    int pnY[] = {     76,   78,   76,   70,   60, 
                      60,   50,   42,   34,   28, 
                      22,   18,   14,   12,   10, 
                      10,   10,   12,   14,   18, 
                      22,   28,   34,   42,   50, 
                      66,   68,   70,   72,   74, 
                      76,   78,   76,   70,   60, 
                      60,   50,   42,   34,   28, 
                      22,   18,   14,   12,   10, 
                      10,   10,   12,   14,   18, 
                      22,   28,   34,   42,   50, 
                      66,   68,   70,   72,   74
                     };

    /**
     *  The length of the arrays
     */
    int nLength = 60;

    /**
     * LookupX - looks up the appropriate value of X
     */
    public int LookupX (int nIndex) 
    {
    if ((nIndex >= nLength) || (nIndex < 0))
        return 0;

    return pnX[nIndex];
    }

    /**
     * LookupY - looks up the appropriate value of Y
     */
    public int LookupY (int nIndex) 
    {
    if ((nIndex >= nLength) || (nIndex < 0))
        return 0;

    return pnY[nIndex];
    }

    /**
     * Length - the number of data points stored in the path
     */
    public int Length ()
    {
    return nLength;
    } 
}


/**
 * ReverseCascadeBallPaths class. This is a container 
 * for a paths of juggling balls, the balls are moving 
 * in a reverse cascade pattern
 *
 * @author      Chris Seguin
 * @version     1.0f, May 1, 1995
 */
class ReverseCascadeBallPaths extends BallPaths {
    
    /**
     * Arrays containing the path of the balls
     */
    int pnX[] = {     12,    9,    6,    3,    0, 
                       0,    5,   10,   15,   20, 
                      25,   30,   35,   40,   45, 
                      50,   55,   60,   65,   70, 
                      75,   80,   85,   90,   95, 
                     100,  103,  106,  109,  112, 
                     115,  118,  121,  124,  127, 
                     130,  125,  120,  115,  110, 
                     105,  100,   95,   90,   85, 
                      80,   75,   70,   65,   60, 
                      55,   50,   45,   40,   35, 
                      27,   24,   21,   18,   15  };
    int pnY[] = {     60,   60,   60,   60,   60, 
                      60,   51,   42,   35,   28, 
                      23,   18,   15,   12,   11, 
                      10,   11,   12,   15,   18, 
                      23,   28,   35,   42,   51, 
                      60,   60,   60,   60,   60, 
                      60,   60,   60,   60,   60, 
                      60,   51,   42,   35,   28, 
                      23,   18,   15,   12,   11, 
                      10,   11,   12,   15,   18, 
                      23,   28,   35,   42,   51, 
                      60,   60,   60,   60,   60 };

    /**
     *  The length of the arrays
     */
    int nLength = 60;

    /**
     * LookupX - looks up the appropriate value of X
     */
    public int LookupX (int nIndex) 
    {
    if ((nIndex >= nLength) || (nIndex < 0))
        return 0;

    return pnX[nIndex];
    }

    /**
     * LookupY - looks up the appropriate value of Y
     */
    public int LookupY (int nIndex) 
    {
    if ((nIndex >= nLength) || (nIndex < 0))
        return 0;

    return pnY[nIndex];
    }

    /**
     * Length - the number of data points stored in the path
     */
    public int Length ()
    {
    return nLength;
    } 
}

/**
 * JugglingBall class. This is a juggling ball
 *
 * @author      Chris Seguin
 * @version     1.0f, May 1, 1995
 */
class JugglingBall {

    /**
     * The location on the ball's path
     */
    int nCycleSlot;

    /**
     * The color of the ball - specified by an index into the ball array
     */
    int nBallColor;

    /**
     * The current location of the ball
     */
    int nX;
    int nY;

    /**
     * The path to follow
     */
    BallPaths ptbpPath;

    /**
     * JugglingBall - creates a juggling ball
     */
    public JugglingBall (int nStartPos, int nStartColor, BallPaths ptbpThePath)
    {
    nCycleSlot = nStartPos;
    nBallColor = nStartColor;

    ptbpPath = ptbpThePath;

    nX = ptbpPath.LookupX(nStartPos);
    nY = ptbpPath.LookupY(nStartPos);
    }

    /**
     * Move - moves the ball to the next location
     */
    public void Move ()
    {
    nCycleSlot++;
    if ((nCycleSlot >= ptbpPath.Length ()) || (nCycleSlot <= 0)) {
        nCycleSlot = 0;
        }

    nX = ptbpPath.LookupX(nCycleSlot);
    nY = ptbpPath.LookupY(nCycleSlot);
    }

    /**
     * XLoc - returns the x location
     */
    public int XLoc ()
    {
    return nX;
    }

    /**
     * YLoc - returns the Y location
     */
    public int YLoc ()
    {
    return nY;
    }

    /**
     * Color - returns the color
     */
    public int Color ()
    {
    return nBallColor;
    }
}


/**
 * HandPath class. This is a container for the paths of the hands
 *
 * @author      Chris Seguin
 * @version     1.0f, May 3, 1995
 */
class HandPath {
    
    /**
     * Arrays containing the path of the hands
     */
    int pnLeftHandX[] = {
                       7,    6,    9,   12,   16, 
                      20,   24,   27,   31,   35, 
                      35,   31,   27,   24,   20, 
                      16,   12,    9,    6,    7
                     };

    int pnRightHandX[] = {
                     143,  144,  141,  138,  134, 
                     130,  126,  123,  119,  115, 
                     115,  119,  123,  126,  130, 
                     134,  138,  141,  144,  143
                     };

    int pnHandY[] = {
                      73,   75,   77,   79,   81, 
                      83,   85,   83,   77,   67, 
                      67,   57,   51,   49,   51, 
                      53,   55,   57,   59,   61
                     };


    /**
     *  The length of the arrays
     */
    int nLength = 60;
    int nBalls = 0;

    /**
     * HandPath - creates a hand path
     */
    public HandPath (int nStartBalls) 
    {
    nBalls = nStartBalls;
    }


    /**
     * LookupX - looks up the appropriate value of X
     */
    public int LookupX (int nIndex, boolean bLeft) 
    {
    if ((nIndex >= nLength) || (nIndex < 0))
        return 0;

    //  Limit the lookup to the range
    if (nIndex >= 20 * nBalls)
         nIndex = 19;

    while (nIndex >= 20)
         nIndex -= 20;

    //  Look up the value
    if (bLeft)
        return pnLeftHandX[nIndex];
    else
        return pnRightHandX[nIndex];
    }

    /**
     * LookupY - looks up the appropriate value of Y
     */
    public int LookupY (int nIndex) 
    {
    if ((nIndex >= nLength) || (nIndex < 0))
        return 0;

    //  Limit the lookup to the range
    if (nIndex >= 20 * nBalls)
         nIndex = 19;

    while (nIndex >= 20)
         nIndex -= 20;

    //  Look up the value
    return pnHandY[nIndex];
    }

    /**
     * Length - the number of data points stored in the path
     */
    public int Length ()
    {
    return nLength;
    } 
}


/**
 * Hand class. This is a hand 
 *
 * @author      Chris Seguin
 * @version     1.0f, May 3, 1995
 */
class Hand {

    /**
     * The location on the ball's path
     */
    int nCycleSlot;

    /**
     * Whether this is the left hand
     */
    boolean bLeft;

    /**
     * The current location of the ball
     */
    int nX;
    int nY;

    /**
     * The path to follow
     */
    HandPath phPath;

    /**
     * Hand - creates a hand
     */
    public Hand (int nStartPos, HandPath phThePath, boolean bStartLeft)
    {
    nCycleSlot = nStartPos;
    bLeft = bStartLeft;

    phPath = phThePath;

    nX = phPath.LookupX(nStartPos, bLeft);
    nY = phPath.LookupY(nStartPos);
    }

    /**
     * Move - moves the ball to the next location
     */
    public void Move ()
    {
    nCycleSlot++;
    if ((nCycleSlot >= phPath.Length ()) || (nCycleSlot <= 0)) {
        nCycleSlot = 0;
        }

    nX = phPath.LookupX(nCycleSlot, bLeft);
    nY = phPath.LookupY(nCycleSlot);
    }

    /**
     * XLoc - returns the x location
     */
    public int XLoc ()
    {
    return nX;
    }

    /**
     * YLoc - returns the Y location
     */
    public int YLoc ()
    {
    return nY;
    }
}


/**
 * A juggling demonstration program
 *
 * @author      Chris Seguin
 * @version     1.0f, May 1, 1995
 */
public class Juggling extends java.applet.Applet implements Runnable {

    /**
     * The path of the juggling balls
     */
    BallPaths pjbPaths;

    /**
     * The juggling balls
     */
    JugglingBall pjbBalls[] = {null, null, null};

    /**
     * The paths that the hands trace out
     */
    HandPath phHandPaths;
    /**
     * The hands
     */
    Hand phLeft;
    Hand phRight;

    /**
     * The directory or URL from which the images are loaded
     */
    String dir;

    /**
     * The images used.
     */
    JugglingImages jbiImages;

    /**
     * The thread animating the images.
     */
    Thread kicker = null;

    /**
     * The delay between animation frames
     */
    int nSpeed;

    /**
     * Shape of the window
     */
    int nHeight = 0;
    int nWidth = 0;

    /**
     * The number of balls in the demonstration
     */
    int nBalls = 0;

    /**
     * Parameter info.
     */
    public String[][] getParameterInfo() {
        String[][] info = {
            {"balls",  "int",  "the number of balls to animate"},
            {"speed",  "int",  "the speed the balls move at"},
            {"img",    "urls", "the directory where the images are located"},
        };
        return info;
    }

    /**
     * Initialize the applet.  Get attributes.
     */
    public void init() {
        //  Load the parameters from the HTML file

        String at = getParameter("img");
        dir = (at != null) ? at : "images";

        at = getParameter("speed");
        nSpeed = (at != null) ? Integer.valueOf (at).intValue() : 20;

        at = getParameter("height");
        nHeight = (at != null) ? Integer.valueOf (at).intValue() : 100;
        at = getParameter("width");
        nWidth = (at != null) ? Integer.valueOf (at).intValue() : 170;

        at = getParameter("balls");
        nBalls = (at != null) ? Integer.valueOf (at).intValue() : 3;

        //  Initialize the Ball variables
        pjbPaths = new CascadeBallPaths ();
        pjbBalls[0] = new JugglingBall ( 0, 0, pjbPaths);
        pjbBalls[1] = new JugglingBall (40, 2, pjbPaths);
        pjbBalls[2] = new JugglingBall (20, 4, pjbPaths);
        
        //  Initialize the hand variables
        phHandPaths = new HandPath (nBalls);
        phLeft = new Hand (5, phHandPaths, true);
        phRight = new Hand (35, phHandPaths, false);

        resize(nWidth, nHeight);
    }

    /**
     * Run the image loop. This methods is called by class Thread.
     * @see java.lang.Thread
     */
    public void run() {
        //  Create the thread
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

        //  Load the images
        jbiImages = new JugglingImages(getDocumentBase(), dir, this);
        
        //  Do the animation
        int ndx = 0;
        while (size().width > 0 && size().height > 0 && kicker != null) {
            for (ndx = 0; ndx < nBalls; ndx++) {
                (pjbBalls[ndx]).Move();
            }
            
            phLeft.Move();
            phRight.Move();
            
            repaint();
            try {Thread.sleep(nSpeed);} catch (InterruptedException e){}
        }
    }
    
    /**
     * Paint the current frame.
     */
    public void paint(Graphics g) {
        update (g);
    }
    public void update(Graphics g) {
        if ((jbiImages != null) && (jbiImages.imgs != null)) {
            //  Erase the background
            g.setColor(java.awt.Color.lightGray);
            g.fillRect(0, 0, nWidth, nHeight);

            int ndx = 0;
            for (ndx = 0; ndx < nBalls; ndx++) {
                if (jbiImages.imgs[pjbBalls[ndx].Color ()] == null) {
                    System.out.print ("ERROR::No Image ");
                    System.out.println (ndx);
                }

                g.drawImage(jbiImages.imgs[pjbBalls[ndx].Color ()], 
                      (pjbBalls[ndx]).XLoc(), (pjbBalls[ndx]).YLoc(), this);
            }
            
            //  Draw the hands
            g.drawImage(jbiImages.imgs[7], 
                        phLeft.XLoc(), phLeft.YLoc(), this);
            g.drawImage(jbiImages.imgs[7], 
                        phRight.XLoc(), phRight.YLoc(), this);
        }
    }

    /**
     * Start the applet by forking an animation thread.
     */
    public void start() {
        if (kicker == null) {
            kicker = new Thread(this);
            kicker.start();
        }
    }

    /**
     * Stop the applet. The thread will exit because kicker is set to null.
     */
    public void stop() {
        kicker = null;
    }
}

