package tropo.applet.stars;


import tropo.applet.*;
import java.awt.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.awt.image.*;
import java.applet.*;

/**
 * 
 * Copyright (c) 1997-1998 Tropo. All Rights Reserved.
 *
 * <p>
 *
 *
 * Starry, starry night applet.
 *
 * <p>
 * The basic idea is that we (typically) load a background image, and
 * then as the mouse moves around little fading stars are drawn near the
 * mouse.  When the user clicks the mouse we draw a "starburst" and then
 * go to a specified URL.
 *
 * <p>
 *
 * These are the configurable parameters that can be specified in
 * the HTML file e.g.
 *
 * <pre><code>
 * &lt;<font color=blue>applet</font> code="tropo.applet.stars.Starry.class" codebase="/techno/java/lib" width=400 height=400&gt;
 *    &lt;<font color=blue>param</font> name=bg value="#000000"&gt;
 *    &lt;<font color=blue>param</font> name=range value="15"&gt;
 *    &lt;<font color=blue>param</font> name=sleep value="75"&gt;
 * &lt;/<font color=blue>applet</font>&gt;
 * </code></pre>
 *  
 *
 * <table>
 *
 * <tr bgcolor="#DDDDDD">
 *  <th>
 *		Parameter
 *  </th>
 *  <th>
 *		Description
 *  </th>
 *  <th>
 *		Default
 *  </th>  
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>imageName</b></code>
 *  </td>
 *  <td>
 *		URL of the background image to load.
 *  </td>
 *  <td>
 *		None.
 *  </td> 
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>bg</b></code>
 *  </td>
 *  <td>
 *		Background color.  Takes an "HTML" format (#RRGGBB in hex)
 *		or java hex (0xRRGGBB) specification.
 *  </td>
 *  <td>
 *		Black.
 *  </td> 
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>starCenter</b></code>
 *  </td>
 *  <td>
 *		Color of center of the stars.
 *  </td>
 *  <td>
 *		White.
 *  </td> 
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>starOutside</b></code>
 *  </td>
 *  <td>
 *		Color of the outside of the stars.
 *  </td>
 *  <td>
 *		Blueish ( red=0, green=130, blue=255).
 *  </td> 
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>range</b></code>
 *  </td>
 *  <td>
 *		Random delta for x and y placemement of stars.
 *		The higher this is the more the stars spread out.
 *  </td>
 *  <td>
 *		6
 *  </td> 
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>sleep</b></code>
 *  </td>
 *  <td>
 *		Amount (in ms) to sleep inbetween frames.
 *  </td>
 *  <td>
 *		100ms
 *  </td> 
 * </tr> 
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>twinkle</b></code>
 *  </td>
 *  <td>
 *		If set to <code>1</code> then stars will move around slightly
 *		as they fade to give a slight twinkling effect.
 *		Set to 1/true/on to turn on.
 *  </td>
 *  <td>
 *		off
 *  </td> 
 * </tr>
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>spinOff</b></code>
 *  </td>
 *  <td>
 *		If the mouse is <em>still</em> should stars will appear?
 *		Set to 1/true/yes to turn on.
 *  </td>
 *  <td>
 *		off
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>delta</b></code>
 *  </td>
 *  <td>
 *		Don't let stars appear any more frequently than this
 *		when the mouse is moving.
 *  </td>
 *  <td>
 *		50ms
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>defSize</b></code>
 *  </td>
 *  <td>
 *		Default star size.
 *  </td>
 *  <td>
 *		6
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>maxStars</b></code>
 *  </td>
 *  <td>
 *		The maximum number of stars there can ever be.
 *  </td>
 *  <td>
 *		100
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>starburstSize</b></code>
 *  </td>
 *  <td>
 *		In a "starburst" (when the user clicks) how many stars appear?
 *  </td>
 *  <td>
 *		50
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>starburstTicks</b></code>
 *  </td>
 *  <td>
 *		How long does a starburst last?
 *  </td>
 *  <td>
 *		10
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>extra</b></code>
 *  </td>
 *  <td>
 *		How many extra stars to generate as the mouse moves.
 *  </td>
 *  <td>
 *		5
 *  </td> 
 * </tr>  
 *
 *
 * <!------------------------------------------------------------>
 * <tr>
 *  <td bgcolor="FFEEFF">
 *		<code><b>goto</b></code>
 *  </td>
 *  <td>
 *		The URL to go to when they click.
 *  </td>
 *  <td>
 *		None.
 *  </td> 
 * </tr>  
 *
 *
 *   
 * </table>
 * 
 */
public final class Starry
    extends TRBufferedApplet
    implements Runnable
{
	/**
	 *
	 */
	public Starry()
	{
		// keep this public so the browser can new us
	}
	
	/**
	 *
	 */
	public void init()
	{
		//o.println( "init");
		System.out.println( "Starry Starry Night, Copyright (c) 1998 Tropo");
	}

	/**
	 *
	 */
	private void parseParameters()
	{
		// get bg image
		String iName = getParameter( "imageName");
		if ( iName != null)
		{
			bgImage = getImage( getCodeBase(), iName);
			tracker.addImage( bgImage, BG_IMAGE_ID);
		}

		// get bg color
		String p;
		if ( ( p = getParameter( "bg")) != null)
		{
			background = new Color( grok( p));
			//o.println( "background=" + background);
		}

		// get color of center of star
		if ( ( p = getParameter( "starCenter")) != null)
		{
			starCenter = new Color( grok( p));
			//o.println( "p=" + p + " grok=" + grok(p) +
			//" starCenter="+ starCenter); 
		}

		// get color of outside of star
		if ( ( p = getParameter( "starOutside")) != null)
		{
			starOutside = new Color( grok( p));
			//o.println( "starOutside="+ starOutside);
		}

		// range of random perturbations
		if ( ( p = getParameter( "range")) != null)
		{
			RANGE = Integer.decode( p).intValue();
		}
		// sleep inbetween frames
		if ( ( p = getParameter( "sleep")) != null)
		{
			SLEEP = Integer.decode( p).intValue();
		}
		if ( ( p = getParameter( "twinkle")) != null)
		{
			twinkle = p.equals( "1") || p.equalsIgnoreCase( "true") ||
				p.equalsIgnoreCase( "yes"); 
		}
		if ( ( p = getParameter( "spinOff")) != null)
		{
			spinOff = (p.equals( "1") || p.equalsIgnoreCase( "true") ||
					   p.equalsIgnoreCase( "yes"));
		}				
		// time window for consc...
		if ( ( p = getParameter( "delta")) != null)
		{
			LAST = Integer.decode( p).intValue();
		}
		// default star size
		if ( ( p = getParameter( "defSize")) != null)
		{
			DEF_SIZE = Integer.decode( p).intValue();
		}
		// how many total
		if ( ( p = getParameter( "maxStars")) != null)
		{
			MAX_STARS = Integer.decode( p).intValue();
			if ( MAX_STARS <= 0)
				MAX_STARS = 1;

		}
		// size of starburst
		if ( ( p = getParameter( "starburstSize")) != null)
		{
			STARBURST_SIZE = Integer.decode( p).intValue();
			if ( STARBURST_SIZE <= 0)
				STARBURST_SIZE = 1;
		}
		
		if ( STARBURST_SIZE > MAX_STARS)
			STARBURST_SIZE = MAX_STARS;
		// size of starburst
		if ( ( p = getParameter( "starburstTicks")) != null)
		{
			STARBURST_TICKS = Integer.decode( p).intValue();
			if ( STARBURST_TICKS <= 0)
				STARBURST_TICKS = 1;
		}
		// extra as mouse moves
		if ( ( p = getParameter( "extra")) != null)
		{
			EXTRA = Integer.decode( p).intValue();
		}
		

		nextURL = getParameter( "goto");
		//o.println( "next="+ nextURL);

		// hmm there's some problem w/ init/start/stop and the applet
		// lifecycle from different pages
		if ( starX == null || starX.length != MAX_STARS)
		{
			starX = new int[ MAX_STARS];
			starY = new int[ MAX_STARS];
			starSize = new int[ MAX_STARS];
		}
	}
	
	/**
	 * Start by kicking off bg thread.
	 */
	public void start()
	{
		//o.println( "start");
		parseParameters();
		goon = true;
		if ( myThread == null)
		{
			myThread = new Thread( this);
			myThread.setName( "Starry");
			myThread.start();
		}
		else
		{
			myThread.resume();
		}
	}
	/**
	 *
	 */
	public void stop()
	{
		//o.println( "stop");
		showStatus( "stopping");
		goon = false;
		myThread = null;
		if ( myThread != null && myThread.isAlive())
		{
			myThread.suspend();
		} 
	}

	/**
	 * Try to gracefully clean up.
	 */
	public void destroy()
	{
		invalidate(); // get rid if offscreen
		if ( myThread != null)
		{
			myThread.stop();
			myThread = null;
		}
		super.destroy();
	}

	/**
	 * Return parameter doc - but how is this exposed to the user?
	 */
	public String[][] getParameterInfo()
	{
		return PARAMETER_INFO;
	}

	/**
	 * Convert a string to a color/int.
	 */
	private static int grok( String s)
	{
		if ( s.startsWith( "#"))
			return hex( s.substring( 1));
		if ( s.startsWith( "0x"))
			return hex( s.substring( 1));
		return 0;
	}

	/**
	 * Parse as hex.
	 */
	private static int hex( String s)
	{
		s = s.toLowerCase();
		int len = s.length();
		int res = 0;
		for ( int i = 0 ; i < len; i++)
		{
			char c = s.charAt( i);
			res = (res << 4) | hex( c);
		}
		return res;
	}
	
	/**
	 * Parse a char as hex.
	 */
	private static int hex( char c)
	{
		switch( c)
		{
		case '0': return 0;
		case '1': return 1;
		case '2': return 2;
		case '3': return 3;
		case '4': return 4;
		case '5': return 5;
		case '6': return 6;
		case '7': return 7;
		case '8': return 8;
		case '9': return 9;
			
		case 'a': return 10;
		case 'b': return 11;
		case 'c': return 12;
		case 'd': return 13;
		case 'e': return 14;
		case 'f': return 15; 
		}
		return 0;
	}

	/**
	 * Thread to do star animation.
	 */
	public void run()
	{
		//o.println( "run " + MAX_STARS + "/" + starSize.length);
		// need bg image first
		if ( bgImage != null)
		{
			try
			{
				//showStatus( "waiting for image");
				tracker.waitForID( BG_IMAGE_ID);
				/*
				showStatus( "done waiting for image, it is " +
							bgImage.getWidth( null) + " by " +
							bgImage.getHeight( null)
							);
							*/
			}
			catch( InterruptedException ie)
			{
				System.err.println( "* ouch " + ie);
			}
			repaint();
			waitForPaint();
		}

		// animate
		while ( goon )
		{
			// shrink stars
			synchronized( this)
			{
				for ( int i = 0; i < MAX_STARS; i++)
					starSize[ i]--;
			}
			long now = System.currentTimeMillis();
			if ( spinOff &&
				 lastMove > 0 &&
				 (now - lastMove) > SPIN_LAST)
			{
				lastMove = now;
				add1( mx, my, false, -2);
			}
			// paint
			repaint();
			waitForPaint(); // don't overwhelm
			// delay
			try
			{
				Thread.sleep( SLEEP);
			}
			catch( InterruptedException ie)
			{
			}

			if ( --starBurst == 0 && nextURL != null)
			{
				try
				{
					getAppletContext().showDocument( new URL( getDocumentBase(), nextURL));
				}
				catch( MalformedURLException me)
				{
					String msg = "nextURL=" + nextURL + " me=" + me;
					showStatus( msg);
				}
			}
			else if ( starBurst > 0)
			{
				//o.println( "sb " + starBurst + " " + System.currentTimeMillis());
			}
		}
	}

	/**
	 *
	 */
	private synchronized void waitForPaint()
	{
		while ( ! painted)
		{
			try
			{
				wait();
			}
			catch( InterruptedException ie)
			{
			}
		}
		painted = false;
	}

	/**
	 *
	 */
	private synchronized void justPainted()
	{
		painted = true;
		notify();
	}
	


	/**
	 *
	 */
	public String getAppletInfo()
	{
		return "Starry Starry Night 0.02a";
	}

	/**
	 * Draw the screen.
	 */
	protected void draw( Graphics g)
	{
		Dimension d = size();
		width = d.width;
		height = d.height;

		// fill bg


		if ( bgImage == null)
		{
			g.setColor( background);
			g.fillRect( 0, 0, width, height);
		}
		else
		{
			int bgImageWidth = bgImage.getWidth( null);
			int bgImageHeight = bgImage.getHeight( null);
			if ( bgImageWidth < width ||
				 bgImageHeight < height)
			{
				g.setColor( background);
				g.fillRect( 0, 0, width, height);				
			}
			int dx = (width/2) - (bgImageWidth/2);
			int dy = (height/2) - (bgImageHeight/2);
			g.drawImage( bgImage, dx, dy, null);
		}

		// draw all stars
		for ( int i = 0; i < MAX_STARS; i++)
			drawStar( g, starX[ i], starY[ i], starSize[ i]);
		justPainted();
	}

	/**
	 * Draw one star.
	 */
	private void drawStar( Graphics g, int x, int y, int size)
	{
		if ( size <= 0) // alive?
			return;

		if ( twinkle && rnd( 100) < 50)
		{
			x += rnd( 3) - 1;
			y += rnd( 3) - 1;
		}

		switch( size)
		{
		case 1:
			g.setColor( starCenter);
			g.drawLine( x, y, x, y);
			g.setColor( starOutside);
			g.drawLine( x-1, y, x-1, y);
			g.drawLine( x+1, y, x+1, y);
			g.drawLine( x, y-1, x, y-1);
			g.drawLine( x, y+1, x, y+1);
			break;
		case 2:
			g.setColor( starCenter);
			g.drawLine( x-1, y, x+1, y);
			g.drawLine( x, y-1, x, y+1);			
			g.setColor( starOutside);
			g.drawLine( x-2, y, x-2, y);
			g.drawLine( x+2, y, x+2, y);
			g.drawLine( x, y-2, x, y-2);
			g.drawLine( x, y+2, x, y+2);
			break;
		case 3:
			g.setColor( starCenter);
			g.drawLine( x-1, y, x+1, y);
			g.drawLine( x, y-1, x, y+1);			
			g.setColor( starOutside);
			g.drawLine( x-3, y, x-3, y);
			g.drawLine( x+3, y, x+3, y);
			g.drawLine( x, y-3, x, y-3);
			g.drawLine( x, y+3, x, y+3);
			break;
		case 4:
			g.setColor( starCenter);
			g.fillRect( x-1, y-1, 3, 3);
			g.drawLine( x-3, y, x+3, y);
			g.drawLine( x, y-3, x, y+3); 
			g.setColor( starOutside);
			g.drawLine( x-5, y, x-4, y);
			g.drawLine( x+5, y, x+4, y);
			g.drawLine( x, y-4, x, y-5);
			g.drawLine( x, y+4, x, y+5);
			break;
		case 5:
			g.setColor( starCenter);
			g.fillRect( x-1, y-1, 3, 3);
			g.drawLine( x-3, y, x+3, y);
			g.drawLine( x, y-3, x, y+3); 
			g.setColor( starOutside);
			g.drawLine( x-7, y, x-4, y);
			g.drawLine( x+7, y, x+4, y);
			g.drawLine( x, y-4, x, y-7);
			g.drawLine( x, y+4, x, y+7);
			break;
		case 6:
			g.setColor( starCenter);
			g.fillRect( x-1, y-1, 3, 3);
			g.drawLine( x-4, y, x+4, y);
			g.drawLine( x, y-4, x, y+4);
			g.drawLine( x-2, y-2, x-2, y-2);
			g.drawLine( x+2, y+2, x+2, y+2);
			g.drawLine( x-2, y+2, x-2, y+2);
			g.drawLine( x+2, y-2, x+2, y-2);			
			g.setColor( starOutside);
			g.drawLine( x-9, y, x-5, y);
			g.drawLine( x+9, y, x+5, y);
			g.drawLine( x, y-5, x, y-9);
			g.drawLine( x, y+5, x, y+9);
			break;
		default:
			int extra = size - 5;
			g.setColor( starCenter);
			g.fillRect( x-1, y-1, 3, 3);
			g.drawLine( x-5, y, x+5, y);
			g.drawLine( x, y-5, x, y+5);
			g.drawLine( x-2, y-2, x-2, y-2);
			g.drawLine( x+2, y+2, x+2, y+2);
			g.drawLine( x-2, y+2, x-2, y+2);
			g.drawLine( x+2, y-2, x+2, y-2);			
			g.setColor( starOutside);
			g.drawLine( x-10-extra, y, x-6, y);
			g.drawLine( x+10+extra, y, x+6, y);
			g.drawLine( x, y-6, x, y-10-extra);
			g.drawLine( x, y+6, x, y+10+extra);
			break; 									
		}

		/*
		// do center as a jagged square kind of thing
		g.setColor( starCenter);
		for ( int i = 0; i < size; i++)
		{
			int x1 = x - (size-1) + i;
			int x2 = x + (size-1) - i;
			int y1 = y - i;
			int y2 = y + i;

			g.drawLine( x1, y1, x2, y1);
			g.drawLine( x1, y2, x2, y2);
		}

		// 4 tips
		g.setColor( starOutside);
		g.drawLine( x - size - size, y, x - size-1, y);
		g.drawLine( x + size + size, y, x + size+1, y);
		g.drawLine( x, y - size - size, x, y - size - 1);
		g.drawLine( x, y + size + size, x, y + size + 1);
		*/
	}

	/**
	 * Add one star.
	 */
	private void add1( int x, int y, boolean down)
	{
		add1( x, y, down, 0);
	}
	/**
	 * Add one star.
	 */
	private void add1( int x, int y, boolean down, int adjust)
	{
		// don't add too frequently
		long now = System.currentTimeMillis();
		if ( (now - last) < LAST)
			return;
		last = now;
		int ox = x;
		int oy = y;

		// find an empty slot
		int indx = findFree();
		if ( indx >= 0)
		{
			// perturb
			x = rnd( ox - RANGE, (2*RANGE+1));
			y = rnd( oy - RANGE, (2*RANGE+1)); 
			starX[ indx] = x;
			starY[ indx] = y;
			if ( adjust == 0)
			{
				starSize[ indx] = DEF_SIZE + adjust;
				return;
			}
			else
			{
				starSize[ indx] = DEF_SIZE + (down ? 3 : 0);				
			}
		}

		// add a few more
		int extra = rnd( EXTRA);
		for ( int i = 0; i < extra; i++)
		{
			if ( (indx = findFree()) >= 0)
			{
				x = rnd( ox - RANGE, (2*RANGE+1));
				y = rnd( oy - RANGE, (2*RANGE+1));
				starX[ indx] = x;
				starY[ indx] = y;
				// extra ones are smaller
				starSize[ indx] = DEF_SIZE - 2;
			}
		}
		repaint();
	}

	/**
	 *
	 */
	private void mouse( int x, int y)
	{
		mx = x;
		my = y;
		lastMove = System.currentTimeMillis();
	}
	
	/**
	 * The mouse moved.
	 */
	public boolean mouseMove( Event evt, int x, int y)
	{
		mouse( x, y);
		add1( x, y, false);
		return true;
	}
	
	/**
	 * The mouse entered
	 */
	public boolean mouseEnter( Event evt, int x, int y)
	{
		mouse( x, y);		
		return super.mouseEnter( evt, x, y);
	}
	/**
	 * The mouse leave
	 */
	public boolean mouseExit( Event evt, int x, int y)
	{
		lastMove = 0;
		return true;
	}		
	/**
	 * The mouse dragged.
	 */
	public boolean mouseDrag( Event evt, int x, int y)
	{
		mouse( x, y);				
		add1( x, y, false);
		return true;
	}
	
	/**
	 * They clicked.
	 */
	public boolean mouseDown( Event evt, int x, int y)
	{
		// mouse( x, y); 
		add1( x, y, true);
		// do a big starburst
		starBurst = STARBURST_TICKS;
		//o.println( "starburst...");
		for ( int i = 0; i < STARBURST_SIZE; i++)
		{
			int indx = findFree();
			if ( indx < 0)
				break;

			starX[ indx] = rnd( width);
			starY[ indx] = rnd( height);
			starSize[ indx] = DEF_SIZE + rnd( 7) - 3;
		}
		return true;
	}	

	/**
	 * Find a free slot.
	 */
	private int findFree()
	{
		for ( int i = 0; i < MAX_STARS; i++)
		{
			if ( starSize[ i] <= 0)
				return i;
		}
		return -1;
	}
	
	

	
	/**
     * @return the next random integer
     */
    private static int rnd()
    {
        return Math.abs( rgen.nextInt());
    }

	/**
     * @return a random number mod 'mod'
     */
    private static int rnd( int mod)
    {
        return rnd() % mod;
    }

	/**
	 *
	 */
	private static boolean pc( int foo)
    {
        return rnd( 100) < foo;
    }

    /**
     * base + rnd( range)
     */
    private static int rnd( int base, int range)
    {
        return base + rnd( range);
    }

	private Image bgImage;
	private int mx;
	private int my;
	private int width = -1;
	private int height = -1;
	private MediaTracker tracker = new MediaTracker( this);
	private static final int BG_IMAGE_ID = 0;

	// "range"
	private static int RANGE = 6;

	private static int STARBURST_SIZE	= 50;
	private static int MAX_STARS	= 100;	
	private int[] starX = new int[ MAX_STARS];
	private int[] starY = new int[ MAX_STARS];
	private int[] starSize = new int[ MAX_STARS]; 
	
	private boolean mouseIsPressed;

	// "sleep"
	private static int SLEEP = 100;

	private long last;

	// "delta"
	private static long LAST = 50; // time delay for consec
	private static long SPIN_LAST = 100; // time delay for consec	
	private Color background = Color.black;
	private Color starCenter = Color.white;
	private Color starOutside = new Color( 0, 130, 255);
    private static final Random rgen = new Random( System.currentTimeMillis());

	// "defSize"
	private static int DEF_SIZE = 6;
	private boolean goon;
	private Thread myThread;
	private String nextURL;
	private int starBurst;

	private static int STARBURST_TICKS = 10;
	
	private static final String[][] PARAMETER_INFO =
	{
		{ "imageName",	"String",		"Name of background image"},
		{ "bg",			"Color",		"Background color"},
		{ "starCenter",	"Color",		"Color of center of star"},
		{ "starOutside","Color",		"Color of outside of star"},
		{ "goto",		"URL",			"Page to go to when they click"},
		{ "range",		"Int",			"Variation range"},
		{ "sleep",		"Int",			"Amount to sleep between frames, in ms"},
		{ "delta",		"Int",			"Time window for const stars during mouse motion"},
		{ "defSize",		"Int",		"Default star size"},
		{ "twinkle",		"1/0",		"Twinkle in place"},
		{ "maxStars",		"Int",		""},
		{ "starburstSize",	"Int",		""},
		{ "starburstTicks",	"Int",		""},
		{ "extra",			"Int",		""},
		{ "spinOff",		"1/0",		""}		
	};

	private boolean painted;

	private boolean twinkle;
	private boolean spinOff;	// still mouse generates them too
	private int EXTRA = 5;

	private long lastMove;

	//private static final PrintStream o = System.out;
}
