/**
* @(#)DynamicBillboard.java
* @version 1.51 04/06/97
* @author Robert Temple (robertt@starwave.com)
*/

import java.awt.*;
import java.awt.image.ImageObserver;
import java.net.URL;

/**
*   Applet which displays an image on an HTML page.  The image changes to a
*   new image after a delay.  This change from one image to the next has
*   some kind of special effect associated with it.  See the BillTransition
*   class for more information on these special effects.  Images can have
*   URLs associated with them.
*
* USAGE NOTE:
*   The images this applet uses all must be the same size.  The size of the
*   applet must be the same size of these images.
*
*   HTML tag:
*   <applet code="DynamicBillboard" width="XXX" height="XXX">
*   <param name="delay" value="20000">
*   <param name="billboards" value="X">
*   <param name="bill0" value="XXX.gif,http://X.X.X/XXX.html">
*   <param name="bill1" value="XXX.gif,http://X.X.X/XXX.html">
*   <param name="bill2" value="XXX.gif,http://X.X.X/XXX.html">
*   <param name="bill3" value="XXX.gif,http://X.X.X/XXX.html">
*   <param name="transitions" value="4,ColumnTransition,FadeTransition,TearTransition,SmashTransition">
*   <param name="bgcolor" value="#FFFFFF">
*   <param name="target" value="_self">
*   </applet>
*
*   HTML tag NOTES
*     width - the width of the applet.  Must be the same size as the images
*     height - the height of the applet. Must be the same size as the images
*     delay - the time between images in milliseconds
*     billboards - the number of different BillBoards(images) that you will
*               use
*     bill# - the image followed by the linking URL for a given BillBoard,
*             starting with number 0.  this followed by a string that will appear on
*             status bar when the mouse is over the billboard.
*             The image and URL should be separated
*             by a comma, *NO SPACES*
*     bill#... - this parameter should appear 0 to one less then the value of
*             the "billboards" parameter.  Replace # with the BillBoards
*             number
*     transitions - the number of classes that will be used as transitions,
*             followed by the names of these classes, separated by commas
*             *NO SPACES*
*     bgcolor - OPTIONAL - the background color the applet will use.  The value is
*             similar to how one specifies the bgcolor tag in HTML.
*     target - OPTIONAL - the frame target the applet should goto when the mouse
*             is pressed over the applet.
*			  ## I hope that not too many people want different targets for different 
*             ## billboards
*
* DESIGN NOTES:
*   FAST CONTENT:
*   Java Applets take much longer to get to downloaded and displayed then
*   most other things appearing on a HTML page under Netscape 2.0b4.  I
*   believe that most web surfers do not have the patience to wait for most
*   applets to load.  Because of this I attempted to make this applet load
*   and get content to the screen as fast as possible.
*
*   To accomplish this, The applet does little processing before waiting for
*   the first image to be displayed on the screen.  This is unlike most other
*   Java applet.  Most load all images and classes before showing anything
*   but a gray box.
*
*   Later, the applet loads other images and classes only as they are needed.
*
*   EXCEPTIONS:
*   I really would like to put more exceptions in the code, but I did not
*   because I wanted the fastest possible loading of classes.  Hence the
*   smallest bytecode
*
*   PUBLIC DATA MEMBERS:
*   A lot of data members are made public even though in good OO-programming
*   they should not be.  This is done for the same reason as above, bytecode
*   size.
*
*   Making data members protected, and then creating a function that allows
*   read only access to this data increases the bytecode size.  Even when
*   the one line function is made final, and the code is compiled with
*   optimizations. :(
*/
public class DynamicBillboard extends java.applet.Applet implements Runnable {
	/**  Array which holds all of the billboards */
	BillData[] billboards;

	/** Index into the billboards array of the current billboard */
	int current_billboard;

	/** Index into the billboards array of the next billboard */
	int next_billboard;

	/** Array of Strings which hold the names of the different transition classes */
	String[] transition_classes;

	/** The main thread which drives the program */
	Thread thread = null;

	/** Current Image displayed on the screen */
	Image image = null;

	/** 
	* The delay time from the completion of one transition to the next transtion
	* Delay is initially set to -1.  This is used to check to see if the
	* finish init function has been called before.  It has if delay is
	* not -1
	*/
	long delay = -1;

	/** Flag the keep track if the mouse is currently located inside the applet */
	boolean mouse_inside_applet;


	/** The frame the applet will use as a target when going to a new HTML document */
	String link_target_frame;

	/**
	* Initializes the applet.  Performs minimal work to get the applet to the
	* screen ASAP.  The next method, finishInit completes the initialization
	*/
	public void init() {
		// Check to see if the user wanted a certain background color
		String s = getParameter("bgcolor");
		if(s != null) {
			Color color = new Color(Integer.parseInt(s.substring(1), 16));
			setBackground(color);
			getParent().setBackground(color);
			getParent().repaint();					
		}

		// Get the total number of billboards that the applet will use &
		// Create an array to store the Data for each billboard
		billboards = new BillData[Integer.parseInt(getParameter("billboards"))];

		// Chose a random billboard to start with
		current_billboard = next_billboard = (int)(Math.random() * billboards.length);

		// create the BillData object for the first billboard
		parseBillData();
	}

	/**
	* Gets the applet parameter info about the current_billboard, and creates
	* a new BillData object from this info.
	*/
	void parseBillData() {

		// get the parameter for the next_billboard info
		String s = getParameter("bill" + next_billboard);
		int field_end = s.indexOf(",");

		// get the billboard's image
		Image new_image = getImage(getDocumentBase(), s.substring(0, field_end));

		// get the billboard's URL link
		URL link;
		try {
			link = new URL(getDocumentBase(), s.substring(field_end + 1));
		}
		catch (java.net.MalformedURLException e) {
			e.printStackTrace();
			link = getDocumentBase();
		}

		// construct the new billboard
		billboards[next_billboard] = new BillData(link, new_image);

		if(image == null) {
			image = new_image;
		}
		else {
			// force loading of the image
			prepareImage(new_image, this);

			// create the image pixels
			billboards[next_billboard].initPixels(size().width, size().height);
		}
	}

	/**
	* finishes the Initialization the applet
	*/
	void finishInit() {
		// Make sure this is only called once, otherwise, when
		// a user leaves our page, and comes back, this gets
		// called again, and it messes things up.
		if(delay != -1) {			
			return;
		}

		// Get the delay between transitions in milliseconds
		delay = Long.parseLong(getParameter("delay"));

		// read in the optional target parameter
		link_target_frame = getParameter("target");
		if(link_target_frame == null) {
			link_target_frame = "_top";
		}

		// get the number of transition classes that will be used
		String s = getParameter("transitions");
		int field_end = s.indexOf(",");

		// get the total number of transitions the applet will use
		int trans_count = Integer.parseInt(s.substring(0, field_end));

		// get the transition classes that will be used
		transition_classes = new String[trans_count];

		for(--trans_count; trans_count > 0; --trans_count) {
			s = s.substring(field_end + 1);			
			field_end = s.indexOf(",");
			transition_classes[trans_count] = s.substring(0, field_end);
		}
		transition_classes[0] = s.substring(field_end + 1);

		// initialize the pixel data for the first billboard
		billboards[next_billboard].initPixels(size().width, size().height);

		mouse_inside_applet = false;
	}

	/**
	* Called When the mouse moves over the applet.  Displays the URL link in
	* the status bar.  Sets the mouse_inside_applet flag to true
	*/
	public boolean mouseMove(Event evt, int x, int y) {
		// show the URL on the status bar	
		mouse_inside_applet = true;
		showStatus(billboards[current_billboard].link.toExternalForm());
		return true;
	}

	/**
	* Called When the mouse moves out of the applet.  Sets the
	* mouse_inside_applet flag to false
	*/
	public boolean mouseExit(Event evt, int x, int y) {
		// clear the URL on the status bar		
		mouse_inside_applet = false;
		showStatus("");
		return true;
	}

	/**
	* Called When the mouse button is released over the applet.  Goes to the
	* URL link
	*/
	public boolean mouseUp(Event evt, int x, int y) {
		// go to the URL link of the billboard
		getAppletContext().showDocument(billboards[current_billboard].link, link_target_frame);
		return true;
	}

	/**
	* Overide to prevent flickering
	*/
	public void update(Graphics g) {		
		paint(g);
	}

	/**
	* Called when the applet needs to be painted.  Draws the image
	*/
	public void paint(Graphics g) {
		g.drawImage(image, 0, 0, this);
	}

	/**
	* Called to start the execution of the applet.
	*/
	public void start() {
		// there is not a next bill_board at this time...
		next_billboard = current_billboard;

		// need to set the current image
		image = billboards[current_billboard].image;

		// make the mouse appear as a link cursor when the mouse is over
		// the applet
		if(getParent() instanceof Frame) {
			((Frame)getParent()).setCursor(Frame.HAND_CURSOR);
		}

		if(thread == null) {
			thread = new Thread(this);
			thread.start();
		}
	}

	/**
	* Called to stop the execution of the applet
	*/
	public void stop() {
		if(thread != null) {
			thread.stop();
			thread = null;
		}
	}

	/**
	* the main execution method of the applet
	*/
	public void run() {

		// Get the first image to the screen ASAP
		while((checkImage(image, this) & ImageObserver.ALLBITS) == 0) {
			try { Thread.sleep(600); } catch (InterruptedException e) {}
		}

		// do the rest of the initialization required
		finishInit();

		// Index into the transition_classes array of the current transition
		int last_transition_type = -1;
		// reference to the actual transition object
		BillTransition transition;
		// variable to hold the time of the next transition
		long next_billboard_time;

		while(true) {

			// Schedule the beginning of the next transition
			next_billboard_time = System.currentTimeMillis() + delay;

			// determine which billboard to display next
			current_billboard = next_billboard;
			if(++next_billboard >= billboards.length) {
				next_billboard = 0;
			}

			// Load the billboard if it has not yet been loaded
			if(billboards[next_billboard] == null) {
				parseBillData();
				try { Thread.sleep(120); } catch (InterruptedException e) {}
			}

			// Randomly Determine the next transition to use, don't include
			// the last transition in the random set, so that the applet will
			// not use the same transition consecutively.
			int transition_type = (int)(Math.random() * (transition_classes.length - 1));
			if(transition_type >= last_transition_type) {
				++transition_type;
			}		
			last_transition_type = transition_type;

			try {
				transition = (BillTransition)Class.forName(transition_classes[
							transition_type]).newInstance();
			}
			catch(Exception e) {
				// NOTE: Was your class part of a package?  You might need
				// "package_name.XXX"
				e.printStackTrace();
				continue;
			}

			// initialize this transition
			transition.init(this, billboards[current_billboard].image_pixels, billboards[next_billboard].image_pixels);

			// get the current time and compare it against the next scheduled
			// transition time.  If it is not yet time for the transition, wait
			// whatever time is needed.
			if(System.currentTimeMillis() < next_billboard_time) {
				try {
					Thread.sleep(next_billboard_time - System.currentTimeMillis());
				}
				catch (InterruptedException e) {}
			}

			Graphics g = getGraphics();

			// loop through each frame of the transition
			for(int c = 0; c < transition.cells.length; ++c) {

				// show the next transition image
				image = transition.cells[c];

				// immediately paint the new image
				g.drawImage(image, 0, 0, null);
				getToolkit().sync();

				try { Thread.sleep(transition.delay); } catch (InterruptedException e) {}
			}

			// the next billboard should be shown is its entirety
			image = billboards[next_billboard].image;

			// immediately paint the new image
			g.drawImage(image, 0, 0, null);
			getToolkit().sync();

			transition.flushCells();
			g.dispose();

			// if the mouse is currently is in the applet, show the new link
			if(mouse_inside_applet == true) {
				showStatus(billboards[next_billboard].link.toExternalForm());
			}

			// clean up resources from the completed transition
			transition = null;

			try { Thread.sleep(120); } catch (InterruptedException e) {}
		}
	}
}


