/*
 * @(#)Juggling.java	1.0 95/05/01 Chris Seguin
 * E-mail:  seguin@uiuc.edu
 *
 * Copyright (c) 1995 University of Illinois (UIUC)
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. 
 *
 * 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 awt.*;
import browser.*;
import net.www.html.*;

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

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

    /**
     * Load the images, from dir. The images are assumed to be
     * named T1.gif, T2.gif...
     * Once all images are loaded the applet is resized to the
     * maximum width and height.
     */
    JugglingImages(URL context, String dir, Juggling parent,
        int maxWidth, int maxHeight) {

	imgs = new Image[40];
	for (int i = 1; i < imgs.length; i++) {
	    Image im = parent.getImage(dir + "/T" + i + ".gif");

	    if (im == null) {
		break;
	        }

	    imgs[nimgs++] = im;
	    if (im.width > maxWidth) {
		maxWidth = im.width;
	        }
	    if (im.height > maxHeight) {
		maxHeight = im.height;
	        }
	}
	parent.resize(maxWidth, maxHeight);
    }
}

/**
 * BallPaths class. This is a container for a paths
 * of juggling balls
 *
 * @author 	Chris Seguin
 * @version 	1.0, 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.0, 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.0, 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.0, 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);

    //System.out.println ("Move (" + nX + ", " + nY + ")  ===> " + 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.0, 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.0, 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);

    //System.out.println ("Move (" + nX + ", " + nY + ")  ===> " + 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.0, May 1, 1995
 */
public class Juggling extends 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 speed at which the icon changes
     */
    int nSpeed;

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

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

    /**
     * Loop variable - used everywhere
     */
    int ndx = 0;

    /**
     * Initialize the applet. Get attributes.
     */
    public void init() {
	String at = getAttribute("img");
	dir = (at != null) ? at : "doc:/demo/images/duke";
	at = getAttribute("speed");
	nSpeed = (at!= null) ? Integer.valueOf (at).intValue() : 20;
	at = getAttribute("height");
	nHeight = (at!= null) ? Integer.valueOf (at).intValue() : 100;
	at = getAttribute("width");
	nWidth = (at!= null) ? Integer.valueOf (at).intValue() : 160;
	at = getAttribute("balls");
	nBalls = (at!= null) ? Integer.valueOf (at).intValue() : 3;

        pjbPaths = new CascadeBallPaths ();
        pjbBalls[0] = new JugglingBall ( 0, 0, pjbPaths);
        pjbBalls[1] = new JugglingBall (40, 2, pjbPaths);
        pjbBalls[2] = new JugglingBall (20, 4, pjbPaths);

        phHandPaths = new HandPath (nBalls);
        phLeft = new Hand (5, phHandPaths, true);
        phRight = new Hand (35, phHandPaths, false);
    }

    /**
     * Run the image loop. This methods is called by class Thread.
     * @see java.lang.Thread
     */
    public void run() {
	Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
	jbiImages = new JugglingImages(documentURL, dir, this, nWidth, nHeight);

	while (width > 0 && height > 0 && kicker != null) {
            ndx = 0;
	    for (ndx = 0; ndx < nBalls; ndx++) {
                (pjbBalls[ndx]).Move();
	        }
       
            phLeft.Move();
            phRight.Move();

            repaint();
   	    Thread.sleep(nSpeed);
            }
        }

    /**
     * Paint the current frame.
     */
    public void paint(Graphics g) {
	if ((jbiImages != null) && (jbiImages.imgs != null)) {
            ndx = 0;
            for (ndx = 0; ndx < nBalls; ndx++) {
	        g.drawImage(jbiImages.imgs[pjbBalls[ndx].Color()], 
                    (pjbBalls[ndx]).XLoc(), (pjbBalls[ndx]).YLoc());
                }

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

    /**
     * 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;
    }
}
