//******************************************************************************
// NewsTicker.java:	Applet
//
//******************************************************************************
import java.applet.*;
import java.awt.*;
import java.util.*;
import java.net.*;
import java.io.*;

//==============================================================================
// Main Class for applet NewsTicker
//
//==============================================================================
public class NewsTicker extends bApplet implements Runnable
{
    // General data members
    //--------------------------------------------------------------------------
	private Vector m_headers = new Vector();
	private Vector m_urls = new Vector();
	private int m_sel = -1; 
	private int m_headerCount = 0;
	private int m_currHeader = 0;
	private Thread m_thread;
	private Image m_imgBuffer = null;
	private int m_scrollPos = 14;
	private boolean m_over = false;
	private boolean m_pause = false;
	private int m_cursory;
	private boolean m_activatepause = false;
	private boolean m_firstrender = true;

	// PARAMETER SUPPORT:
	//		Parameters allow an HTML author to pass information to the applet;
	// the HTML author specifies them using the <PARAM> tag within the <APPLET>
	// tag.  The following variables are used to store the values of the
	// parameters.
    //--------------------------------------------------------------------------

    // Members for applet parameters
    // <type>       <MemberVar>    = <Default Value>
    //--------------------------------------------------------------------------
	private String m_newsfile = "news.txt";
	private int m_x = 0;
	private int m_y = 0;
	private int m_cx = 0;
	private int m_cy = 0;
	private String m_background = "";
	private Color m_bgcolor;
	private Color m_textcolor;
	private Color m_hilitecolor;
	private String m_frame = null;

    // Parameter names.  To change a name of a parameter, you need only make
	// a single change.  Simply modify the value of the parameter string below.
    //--------------------------------------------------------------------------
	private final String PARAM_newsfile = "newsfile";
	private final String PARAM_x = "x";
	private final String PARAM_y = "y";
	private final String PARAM_cx = "cx";
	private final String PARAM_cy = "cy";
	private final String PARAM_background = "background";
	private final String PARAM_bgcolor = "bgcolor";
	private final String PARAM_textcolor = "textcolor";
	private final String PARAM_hilitecolor = "hilitecolor";
	private final String PARAM_frame = "frame";
	private final String PARAM_pause = "pause";

    // Variables.  
    //--------------------------------------------------------------------------
	private Image m_imgBackground;
	private Font m_font;

	// NewsTicker Class Constructor
	//--------------------------------------------------------------------------
	public NewsTicker()
	{
		m_font = new Font("Helvetica", Font.PLAIN, 11);
	}

	// APPLET INFO SUPPORT:
	//		The getAppletInfo() method returns a string describing the applet's
	// author, copyright date, or miscellaneous information.
    //--------------------------------------------------------------------------
	public String getAppletInfo()
	{
		return "Name: NewsTicker\r\n" +
		       "Author: John Mannix\r\n" +
		       "Created with Microsoft Visual J++ Version 1.1";
	}

	// PARAMETER SUPPORT
	//		The getParameterInfo() method returns an array of strings describing
	// the parameters understood by this applet.
	//
    // NewsTicker Parameter Information:
    //  { "Name", "Type", "Description" },
    //--------------------------------------------------------------------------
	public String[][] getParameterInfo()
	{
		String[][] info =
		{
			{ PARAM_newsfile, "String", "News text file" },
			{ PARAM_x, "int", "X position of news view" },
			{ PARAM_y, "int", "Y position of news view" },
			{ PARAM_cx, "int", "Width of news view" },
			{ PARAM_cy, "int", "Height of news view" },
			{ PARAM_background, "String", "Background image URL" },
			{ PARAM_bgcolor, "String", "Background colour 'R,G,B'" },
			{ PARAM_textcolor, "String", "Text colour 'R,G,B'" },
			{ PARAM_hilitecolor, "String", "Highlight colour 'R,G,B'" },
			{ PARAM_frame, "String", "Frame used to launch URLs" },
			{ PARAM_pause, "String", "Whether to activate pause" }
		};
		return info;		
	}

	// The init() method is called by the AWT when an applet is first loaded or
	// reloaded.  Override this method to perform whatever initialization your
	// applet needs, such as initializing data structures, loading images or
	// fonts, creating frame windows, setting the layout manager, or adding UI
	// components.
    //--------------------------------------------------------------------------
	public void init()
	{
		// PARAMETER SUPPORT
		//		The following code retrieves the value of each parameter
		// specified with the <PARAM> tag and stores it in a member
		// variable.
		//----------------------------------------------------------------------
		String param;

		// newsfile: News text file
		//----------------------------------------------------------------------
		param = getParameter(PARAM_newsfile);
		if (param != null)
			m_newsfile = param;

		// x: X position of news view
		//----------------------------------------------------------------------
		param = getParameter(PARAM_x);
		if (param != null)
			m_x = Integer.parseInt(param);

		// y: Y position of news view
		//----------------------------------------------------------------------
		param = getParameter(PARAM_y);
		if (param != null)
			m_y = Integer.parseInt(param);

		// cx: Width of news view
		//----------------------------------------------------------------------
		param = getParameter(PARAM_cx);
		if (param != null)
			m_cx = Integer.parseInt(param);

		// cy: Height of news view
		//----------------------------------------------------------------------
		param = getParameter(PARAM_cy);
		if (param != null)
			m_cy = Integer.parseInt(param);

		// background: Background image URL
		//----------------------------------------------------------------------
		param = getParameter(PARAM_background);
		if (param != null)
			m_background = param;

		// pause: Whether to activate pause
		//----------------------------------------------------------------------
		param = getParameter(PARAM_pause);
		if (param != null)
		{
			m_activatepause = true;
			m_scrollPos += m_y + m_cy;
		}

		// bgcolor: Background colour
		//----------------------------------------------------------------------
		StringTokenizer t;
		String bgcolor = getParameter(PARAM_bgcolor);
		if (bgcolor != null)
		{
			t = new StringTokenizer(bgcolor, ",");
			m_bgcolor = new Color(Integer.parseInt(t.nextToken()), 
				Integer.parseInt(t.nextToken()), 
				Integer.parseInt(t.nextToken()));
			setBackground(m_bgcolor);
		}

		// textcolor: Text colour
		//----------------------------------------------------------------------
		String textcolor = getParameter(PARAM_textcolor);
		if (textcolor != null)
		{
			t = new StringTokenizer(textcolor, ",");
			m_textcolor = new Color(Integer.parseInt(t.nextToken()), 
				Integer.parseInt(t.nextToken()), 
				Integer.parseInt(t.nextToken()));
		}
		else
			m_textcolor = Color.black;

		// hilitecolor: Highlight colour
		//----------------------------------------------------------------------
		textcolor = getParameter(PARAM_textcolor);
		if (textcolor != null)
		{
			t = new StringTokenizer(textcolor, ",");
			m_hilitecolor = new Color(Integer.parseInt(t.nextToken()), 
				Integer.parseInt(t.nextToken()), 
				Integer.parseInt(t.nextToken()));
		}
		else
			m_hilitecolor = Color.red;

		// frame: Frame used to launch URLs
		//----------------------------------------------------------------------
		m_frame = getParameter(PARAM_frame);
		
		// Load the background image and prepare it
        //----------------------------------------------------------------------
		m_imgBackground = getImage(getDocumentBase(), m_background);
		prepareImage(m_imgBackground, this);

		// Read the news file into an array
        //----------------------------------------------------------------------
		readNewsFile();
	}

	// Place additional applet clean up code here.  destroy() is called when
	// when you applet is terminating and being unloaded.
	//-------------------------------------------------------------------------
	public void destroy()
	{
	}

	// NewsTicker Paint Handler
	//--------------------------------------------------------------------------
	public void paint(Graphics g)
	{
		if (m_imgBuffer == null)
		{
			m_imgBuffer = createImage(size().width, size().height);
			render();
		}

		g.drawImage(m_imgBuffer, 0, 0, null);
	}

	public void render()
	{
		if (m_imgBuffer != null)
		{
			Graphics g = m_imgBuffer.getGraphics();
			g.setColor(m_bgcolor);
			g.fillRect(0, 0, size().width, size().height);
			g.drawImage(m_imgBackground, 0, 0, this);
			g.clipRect(m_x, m_y, m_cx, m_cy);
			g.translate(0, m_scrollPos);
			g.setColor(m_textcolor);
			g.setFont(m_font);

			String line;
			int y = m_y, i;
			m_pause = false;
			boolean reset = false;
			m_sel = -1;

			while (y + m_scrollPos < m_y + m_cy + 34)
			{
				for (i = 0; i < m_headerCount; i++)
				{
					if (m_over)
					{
						line = (String)m_headers.elementAt(i);
						int height = 0, index = 0;
						while (line.indexOf('|', index) > 0)
						{
							height+=14;
							index = line.indexOf('|', index)+1;
						}

						if (m_cursory+14 > y+m_scrollPos && m_cursory < y+height+m_scrollPos)
						{
							m_sel = i;
							g.setColor(m_hilitecolor);
						}
					}

					if (y + m_scrollPos == m_y + 14) m_pause = true;

					StringTokenizer t = new StringTokenizer((String)m_headers.elementAt(i), "|");
					while (t.hasMoreTokens())
					{
						line = t.nextToken();
						g.drawString(line, m_x, y);
						y += 14;
					}
					y += 24;

					g.setColor(m_textcolor);
				}

				if (y + m_scrollPos < m_y) reset = true;
			}

			if (reset) m_scrollPos = 0;
		}
	}

	public void update(Graphics g)
	{
		render();
		paint(g);
	}

	// The start() method is called when the page containing the applet
	// first appears on the screen. The AppletWizard's initial implementation
	// of this method starts execution of the applet's thread.
	//--------------------------------------------------------------------------
	public void start()
	{
		m_thread = new Thread(this);
		m_thread.start();
	}
	
	// The stop() method is called when the page containing the applet is
	// no longer on the screen. The AppletWizard's initial implementation of
	// this method stops execution of the applet's thread.
	//--------------------------------------------------------------------------
	public void stop()
	{	
		m_thread.stop();
	}

	// The run() method is called when by m_thread and implements the
	// header animations between the display of each news item.
	//--------------------------------------------------------------------------
	public void run()
	{
		try
		{
			while (true)
			{
				if (m_pause && m_activatepause) m_thread.sleep(4000);
				else if (m_activatepause) m_thread.sleep(20);
				else m_thread.sleep(100);
				//m_currHeader = (m_currHeader+1) % m_headerCount;
				m_scrollPos--;
				repaint();
			}
		}
		catch (InterruptedException e)
		{
		}
	}


	// MOUSE SUPPORT:
	// The mouseDown() method is called if the mouse button is pressed
	// while the mouse cursor is over the applet's portion of the screen.
	//--------------------------------------------------------------------------
	public boolean mouseDown(Event evt, int x, int y)
	{
		if (m_sel >= 0 && m_sel < m_headerCount)
		{
			if (m_frame == null)
				getAppletContext().showDocument((URL)m_urls.elementAt(m_sel));
			else
				getAppletContext().showDocument((URL)m_urls.elementAt(m_sel), m_frame);
		}

		return true;
	}

	// MOUSE SUPPORT:
	// The mouseUp() method is called if the mouse button is released
	// while the mouse cursor is over the applet's portion of the screen.
	//--------------------------------------------------------------------------
	public boolean mouseUp(Event evt, int x, int y)
	{
		return true;
	}

	public boolean mouseEnter(Event evt, int x, int y)
	{
		stop();
		m_over = true;
		m_cursory = y;
		repaint();
		return true;
	}

	public boolean mouseExit(Event evt, int x, int y)
	{
		start();
		m_over = false;
		m_cursory = y;
		repaint();
		return true;
	}

	public boolean mouseMove(Event evt, int x, int y)
	{
		m_cursory = y;
		repaint();
		return true;
	}

	// Read the news file into m_headers, one line per entry.
	//-------------------------------------------------------------------------
	private void readNewsFile()
	{
		String line;
		int count = 0;
		
		try
		{
			URL url = new URL(getCodeBase(), m_newsfile);

			try
			{
				DataInputStream in = new DataInputStream(url.openStream());
	
				do
				{
					line = in.readLine();
					if (line != null)
					{
						StringTokenizer t = new StringTokenizer(line, "~");
						m_headers.addElement(t.nextToken());
						if (t.hasMoreTokens())
							m_urls.addElement(new URL(getDocumentBase(), t.nextToken()));
						m_headerCount++;
					}
				} while (line != null);
			}
			catch (IOException e)
			{
			}
		}
		catch (MalformedURLException e)
		{
			System.err.println("Could not open news feed file.");
		}
	}
}
