/*
 * Marquee.java      v1.0    Nov 10, 1996
 *
 * Copyright (c) 1996-7 H.J. Tsai, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for any purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. 
 *
 * H.J. Tsai MAKES 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. H.J. Tsai SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * Author: H.J. Tsai	hjtsai@cargobay.com
 *
 * Version 1.0  Nov 10, 1996
 *              Initial version

 *
 */

/**
 * Marquee - a class object for a scrolling text marquee.
 * The scrolling text appears in the container.
 *
 * The following Microsoft Explorer's MARQUEE Tag parameters
 * and description are supported:
 *	<MARQUEE
 *		BEHAVIOR=type
 *		BGCOLOR=color
 *		DIRECTION=direction
 *		LOOP=n
 *		SCROLLAMOUNT=n
 *		SCROLLDELAY=n
 *	</MARQUEE>
 *
 *	Parameters 
 *
 *	BEHAVIOR=type 
 *		Specifies how the text should behave.
 *		The type can be one of these values: 
 *		SCROLL 		Start completely off one side,
 *					scroll all the way across and completely off,
 *					and then start again. This is the default. 
 *		SLIDE		Start completely off one side, scroll in, and
 *					stop as soon as the text touches the other margin. 
 *		ALTERNATE	Bounce back and forth within the marquee. 
 *
 *	BGCOLOR=color 
 *		Specifies a background color for the marquee.
 *
 *	DIRECTION=direction 
 *		Specifies in which direction the text should scroll.
 *		The direction can be LEFT or RIGHT. The default is LEFT,
 *		which means scrolling from right to left. 
 *
 *	LOOP=n 
 *		Specifies how many times a marquee will loop when activated.
 *		If LOOP=-1, or if LOOP=INFINITE, the marquee will loop indefinitely. 
 *  
 *	SCROLLAMOUNT=n 
 *		Specifies the number of pixels between each successive draw of the
 *		marquee text. 
 *
 *	SCROLLDELAY=n 
 *		Specifies the number of milliseconds between each successive draw of
 *		the marquee text. 
 * 
 *
 * The following additional parameters are required/supported:
 * See Marquee() constructor.
 *
 * Rectangle r
 *		Sepcifies the bounding rectangle for the marquee.
 *
 * Color fgColor
 *		Sepcifies the color used for the text.
 *
 * Strng fontName
 *		Specifies the font family used for the text.
 *
 * int	fontStyle
 *		Specifies the font style used for the text.
 *
 * int fontSize
 *		Specifies the font size used for the text.
 *
 * String text
 *		Specifies the text to be scrolled.
 *
 * Component comp
 *		Specifies the containing component.
 */

import java.awt.*;

public class Marquee implements Runnable {
	// behaviors
	public static final int SCROLL = 1;
	public static final int SLIDE = 2;
	public static final int ALTERNATE = 3;
	// directions
	public static final int LEFT	= 1;
	public static final int RIGHT	= 2;
	// loop
	public static final int INFINITE	= -1;
	
	private int loop = INFINITE;
	private int scrollamount = 10;
	private int scrolldelay = 100;
	private int behavior = SCROLL;
	private int	dir	= LEFT;
	private Rectangle rect;
	private Color	foreground;
	private Color	background;
	private String	text;
	private String	fontName = "Helvetica";
	private int fontStyle = Font.ITALIC;
	private int	fontSize = 10;
	private int delta;
	private char msg[];
	private int offset = 0;
	private int nchar;
	private int pos = 0;
	private	int msgwidth;
	private Component comp;

	public Marquee(
				Rectangle r,
				int loop,
				int amount,
				int delay,
				int direction,
				int behavior,
				String text,
				Color bgColor,
				Color fgColor,
				Component comp) {
		this(r, loop, amount, delay, direction,
				behavior, text, fgColor, bgColor, 
				"Helvetica", Font.ITALIC, 10, comp);
	}

	public Marquee(
				Rectangle r,
				int loop,
				int amount,
				int delay,
				int direction,
				int behavior,
				String text,
				Color fgColor,
				Color bgColor,
				String fontName,
				int fontStyle,
				int fontSize,
				Component comp) {
		this.rect = r;
		this.loop = loop;
		this.dir  = direction;
		this.scrollamount = amount;
		this.scrolldelay = delay;
		this.behavior = behavior;
		this.text = text;
		this.foreground = fgColor;
		this.background = bgColor;
		this.fontName = fontName;
		this.fontStyle = fontStyle;
		this.fontSize = fontSize;
		this.comp = comp;
	}

	public void run() {

		Image img = comp.createImage(rect.width, rect.height);
		Graphics memg = img.getGraphics();

		// clear background
		memg.setColor(background);
		memg.fillRect(0, 0, rect.width, rect.height);
	
		// create Font
		Font font = new Font(fontName, fontStyle, fontSize);
		memg.setFont(font);
	
		FontMetrics fm = memg.getFontMetrics();
		int maxadvance = fm.getMaxAdvance() < 0 ? 0 : fm.getMaxAdvance();
		msgwidth = fm.stringWidth(text) + maxadvance;
		int height = fm.getAscent();

		msg = text.toCharArray();
		int y = (rect.height+height)/2;
	
		// set the starting x coordinate
		resetPosition();
		Graphics g = comp.getGraphics();
		g.clipRect(rect.x, rect.y, rect.width, rect.height);

		memg.setColor(foreground);

		while (loop == -1 || loop > 0) {
			try {
				memg.setColor(background);
				memg.fillRect(0, 0, rect.width, rect.height);
				memg.setColor(foreground);
				memg.drawChars(msg, offset, nchar, pos, y);
				g.drawImage(img, rect.x, rect.y, null);
				
				Thread.sleep(scrolldelay);

				if (doneScroll()) {
					if (loop > 0)
						loop--;

					if (behavior == SLIDE)
						break;

					if (behavior == ALTERNATE)
						toggleDirection();

					resetPosition();
				}
				else
					setVisiableText();
		
			} catch (InterruptedException e) {
				break;			
			}
		}
	}


	private void toggleDirection() {
		if (dir == LEFT)
			dir = RIGHT;
		else
			dir = LEFT;
	}

	private void resetPosition() {
		if (dir == LEFT) {
			pos = rect.x + rect.width;
			delta = -scrollamount;
		}
		else {
			pos = rect.x - msgwidth; 
			delta = scrollamount;
		}
		offset = 0;
		nchar = msg.length;
	}

	private boolean doneScroll() {
		if (behavior == SLIDE) {
			if ((dir == LEFT && pos <= rect.x) 
				|| (dir == RIGHT && pos + msgwidth >= rect.x +rect.width))
				return true;
			else
				return false;
		}
		else if (pos + msgwidth < rect.x || pos > rect.x + rect.width)
			return true;
		else
			return false;
	}

	private void setVisiableText() {
		// set offset, nchar
		pos += delta;

		// for SLIDE, we need to stop at the edge
		if (behavior == SLIDE) {
			if (dir == LEFT && pos < rect.x)
				pos = rect.x;
			else if (dir == RIGHT && pos + msgwidth > rect.x + rect.width)
				pos = rect.x + rect.width - msgwidth;
		}
		offset = 0;
		nchar = msg.length;
	}
}
