/* RJA	2000 January   23	modified for new data format	
		1998 September 06	this code taken from Apple Applet Runner Bar Chart
                          	example applet, plus some code from the 
                          	Que book _Using Java, Special Edition_ (1996)
*/

/*
 * @(#)Chart.java	1.4 97/02/05
 *
 * Copyright (c) 1994-1997 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.io.*;
import java.lang.*;
import java.net.URL;

// RJA added
import java.net.MalformedURLException;
import java.net.UnknownServiceException;

public class MyWebVisitStatsChart extends java.applet.Applet implements Runnable
{
    static final int	VERTICAL = 0;
    static final int 	HORIZONTAL = 1;

    static final int	SOLID = 0;
    static final int	STRIPED = 1;

    int			orientation;
    String		title;
    Font		titleFont;
    FontMetrics	titleFontMetrics;
    int			titleHeight = 15;
    int			columns;
    int			values[];
    Object		colors[];
    Object		labels[];
    int			styles[];
    int			scale = 10;			// multiplier for bar size
    int			maxLabelWidth = 0;
    int			barWidth;
    int			barSpacing = 10;
    int			max = 0;
	
	// RJA added
	
	int			myWIDTH; 	// will be read from params, then stays constant
	int			myHEIGHT; 	// will be read from params, then stays constant
							// needed because Netscape doesn't like
							// getSize().width, getSize().height
							
	int			mySHRINK;   // divide the values by this value so they will
	                        // fit on the the screen; should be autocalculated

	final int 	DEFAULT_SHRINK = 4;   
	                    
	final int 	DEFAULT_WIDTH  = 500;
	final int 	DEFAULT_HEIGHT = 200;
	
	int			max_paint = -1; // index of maximum value to paint

	// RJA add separate thread for continuous updating
				
	protected Thread urlEaterThread;
	
	URL[] 				webStatURL; 
	
/* *********** init ***************** */
	
	// RJA this sets up the initial parameters
	
    public void init()
    {
    
 	String paramString;
	
	titleFont = new java.awt.Font("Courier", Font.PLAIN, 10);
	titleFontMetrics = getFontMetrics(titleFont);
	
	title = getParameter("title");
	if (title == null) {
	    title = "Chart";
	}
	
	paramString = getParameter("columns");
	if (paramString == null) {
	    columns = 5;
	} else {
	    columns = Integer.parseInt(paramString);
	}
	
	System.out.println( "columns " + columns );
	
	paramString = getParameter("scale");
	if (paramString == null) {
	    scale = 10;
	} else {
	    scale = Integer.parseInt(paramString);
	}

	paramString = getParameter("orientation");
	if (paramString == null) {
	    orientation = VERTICAL;
	} else if (paramString.toLowerCase().equals("vertical")) {
	    orientation = VERTICAL;
	} else if (paramString.toLowerCase().equals("horizontal")) {
	    orientation = HORIZONTAL;
	} else {
	    orientation = VERTICAL;
	}

	// RJA added size params
	
	paramString = getParameter("WIDTH");
	if (paramString == null) {
	    myWIDTH = DEFAULT_WIDTH;
	}
	else
	{
	    myWIDTH = Integer.parseInt(paramString);
	}

	paramString = getParameter("HEIGHT");
	if (paramString == null) {
	    myHEIGHT = DEFAULT_HEIGHT;
	}
	else
	{
	    myHEIGHT = Integer.parseInt(paramString);
	}
		
	paramString = getParameter("SHRINK");
	if (paramString == null)
	{
		mySHRINK = DEFAULT_SHRINK;
	}
	else
	{
		mySHRINK = Integer.parseInt(paramString);
	}

	webStatURL = new URL[columns];
		
	values = new int[columns];
	colors = new Color[columns];
	labels = new String[columns];
	styles = new int[columns];
	
	// RJA we can process all the styles and colors and stuff first
	
	for ( int i = 0; i < columns; i++)
	{
	    // parse the bar style
	    paramString = getParameter("C" + (i+1) + "_style");
	    if (paramString == null || paramString.toLowerCase().equals("solid")) {
		styles[i] = SOLID;
	    } else if (paramString.toLowerCase().equals("striped")) {
		styles[i] = STRIPED;
	    } else {
		styles[i] = SOLID;
	    }
	    
	    // parse the color attribute for this column
	    paramString = getParameter("C" + (i+1) + "_color");
	    if (paramString != null) {
		if (paramString.equals("red")) {
		    colors[i] = Color.red;
		} else if (paramString.equals("green")) {
		    colors[i] = Color.green;
		} else if (paramString.equals("blue")) {
		    colors[i] = Color.blue;
		} else if (paramString.equals("pink")) {
		    colors[i] = Color.pink;
		} else if (paramString.equals("orange")) {
		    colors[i] = Color.orange;
		} else if (paramString.equals("magenta")) {
		    colors[i] = Color.magenta;
		} else if (paramString.equals("cyan")) {
		    colors[i] = Color.cyan;
		} else if (paramString.equals("white")) {
		    colors[i] = Color.white;
		} else if (paramString.equals("yellow")) {
		    colors[i] = Color.yellow;
		} else if (paramString.equals("gray")) {
		    colors[i] = Color.gray;
		} else if (paramString.equals("darkGray")) {
		    colors[i] = Color.darkGray;
		} else {
		    colors[i] = Color.gray;
		}
	    } else {
		colors[i] = Color.gray;
	    }
	    
	} // end for

	// RJA get data here
	
	/* RJA
	This is the format of the data returned
	
	<html><head>
	<title>Page Access Statistics</title>
	<link rev="made" href="mailto:userhelp@chebucto.ns.ca">
	</head><body background="/icons/paper.gif">
	
	<h2>Number of visits to URLs that match URL</h2>
	Yesterday: #
	<p>This month: #
	<p>Last month: #
	</body></html>

	We want to provide an array of URLs to check.
	For each URL an array will hold the values for yesterday, this month, and
	last month.

	Then they will be plotted in a bar graph.
	Then these numbers will be displayed in a table.
	
	Currently the code just does a bar graph for last month's values
	*/

	// RJA this code based in part on _Using Java, Special Edition_
	//     Chapter 27 - Content Handlers, pp. 587-588 "Reading the InputStream"
		
	String 				baseURL;
		
	baseURL = getParameter("BASE_URL");
	if (baseURL == null)
	{
	  System.err.println("no base url!");
	  // do we need more error handling here?
	}
	
	for ( int i = 0; i < columns; i++ )
	{	
		System.out.println("in get url loop, i = " + i);
		
		paramString = getParameter("C" + (i+1) + "_URL");
		if (paramString != null)	
		{
			try
			{
				webStatURL[i] = new URL( baseURL + paramString );
				
				// RJA we can do the labels first
				
				// RJA hack to remove base URL to make short label
				labels[i] = webStatURL[i].toString().substring(baseURL.length());
		
	    		maxLabelWidth = Math.max(titleFontMetrics.stringWidth((String)(labels[i])),
				     			maxLabelWidth);
			}
			catch (MalformedURLException e)
			{
				System.err.println( "Caught Malformed URL Exception" );
				// should die here
			}
			catch (Exception e)
			{
				System.err.println( "Caught exception: " + e.toString() );
				// should die here
			} 
			
			// RJA can't resize panel within browser
	
			switch (orientation) {
			  case VERTICAL:
			  default:
			    barWidth = maxLabelWidth;
			    //resize(Math.max(columns * (barWidth + barSpacing),
				//	     titleFontMetrics.stringWidth(title)) +
				//       titleFont.getSize() + 5,
				//       (max * scale) + (2 * titleFont.getSize()) + 5 + titleFont.getSize());
			    break;
			 case HORIZONTAL:
		    	barWidth = titleFont.getSize();
		    	//resize(Math.max((max * scale) + titleFontMetrics.stringWidth("" + max),
				//	     titleFontMetrics.stringWidth(title)) + maxLabelWidth + 5,
				//       (columns * (barWidth + barSpacing)) + titleFont.getSize() + 10);
		    	break;
   			} // end switch
		} 
		else
		{
			System.err.println("no urls to process");
			// should die here
		} // end if
	} // end for
	} // end init
		
/* *** start ***/

	public void start()
	{
		urlEaterThread = new Thread(this);
		urlEaterThread.start();
	}

/* *** stop *** */
	
	public void stop()
	{
		urlEaterThread.stop();
		urlEaterThread = null;
	}
	
/* *** run *** */

	// RJA this guy eats the URLs one by one and updates the display as it gets
	//     each data value
	
	public void run()
	{
		Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

		Object 				contentObject;
		InputStream 		pageContent;
		StringBuffer 		outStringBuffer;
		String 				outString;
		
		// outString will contain the full data returned
		// we want to parse out the numbers for yesterday, this month, and last month
		// (should be using arrays)
		
		String				lastMonthString;
		int					urlmatchindex;
		int 				lastmonthindex;
		int					endbodyindex;
		int					endhtmlindex;
		Integer[]			lastMonthCount = new Integer[columns];
		int 				inputByte;

		// RJA this is the slow part
	
		for ( int i = 0; i < columns; i++ )
		{				
			try
			{		
				contentObject = webStatURL[i].getContent();

				// RJA used to check class of contentObject but
				// crusty Netscape gives a URLInputStream
						
				pageContent = (InputStream) contentObject;
					
				inputByte = pageContent.read();
				
				outStringBuffer = new StringBuffer();
				
				while( inputByte != -1 ) 	// end of stream is -1
				{
					outStringBuffer.append( (char) inputByte );
					inputByte = pageContent.read();
				}
				
				pageContent.close();
			
				outString = outStringBuffer.toString();
							
			    // take the substring starting at "Number of visits to URLs that match"
			    
			    urlmatchindex = outString.indexOf("Number of visits to URLs that match");
			    
				endhtmlindex = outString.indexOf("</html>");

			    outString = outString.substring( urlmatchindex, endhtmlindex );

				lastmonthindex = outString.indexOf("Last month:");
			
			    endbodyindex = outString.indexOf("</ul>");
			    
				lastMonthString = outString.substring( lastmonthindex, endbodyindex );
				
				try
				{	
					lastMonthCount[i] = new Integer(lastMonthString.substring( lastMonthString.indexOf(":") + 1 ).trim());
					System.out.println("lastMonthCount " + lastMonthCount[i]); 
				}
				catch (NumberFormatException e)
				{
					lastMonthCount[i] = new Integer(0);
					System.err.println("number format exception " + e);
				}
			}
			catch (UnknownServiceException e)
			{
				System.err.println( "Caught Unknown Service Exception" );
				// should die here
			}
			catch (Exception e)
			{
				System.err.println( "Caught exception: " + e.toString() );
				// should die here
			} 
						
			// RJA removed parameters
	
			// RJA get values from data above
			
			values[i] = lastMonthCount[i].intValue() / mySHRINK;
	
		    if (values[i] > max)
		    {
				max = values[i];
		    }
		
			// RJA should be able to call paint now, but want to limit value of i
			max_paint = i+1;
			repaint();
		
		} // end for				
		    
    } // end run
			
/* ************** paint ************************ */
	
    public void paint(Graphics g) {
	
	int i, j;
	int cx, cy;
	char l[] = new char[1];

	// RJA replaced getSize() calls with constants because of Netscape
	
	// draw the title centered at the bottom of the bar graph
	g.setColor(Color.black);
	i = titleFontMetrics.stringWidth(title);
	g.setFont(titleFont);
	//g.drawString(title, Math.max((getSize().width - i)/2, 0),
	//	                          getSize().height - titleFontMetrics.getDescent()); 
	g.drawString(title, Math.max((myWIDTH - i)/2, 0),
	                              myHEIGHT - titleFontMetrics.getDescent()); 
	
	for (i=0; i < max_paint; i++) {
	    switch (orientation) {
	      case VERTICAL:
	      default:
		// set the next X coordinate to account for the label
		// being wider than the bar size().width.
		cx = (Math.max((barWidth + barSpacing),maxLabelWidth) * i) +
		    barSpacing;

		// center the bar chart
		//cx += Math.max((getSize().width - (columns *
		//			 (barWidth + (2 * barSpacing))))/2,0);
		cx += Math.max((myWIDTH - (columns *
		 			   (barWidth + (2 * barSpacing))))/2,0);

		
		// set the next Y coordinate to account for the size().height
		// of the bar as well as the title and labels painted
		// at the bottom of the chart.
		//cy = getSize().height - (values[i] * scale) - 1 - (2 * titleFont.getSize());
		cy = myHEIGHT - (values[i] * scale) - 1 - (2 * titleFont.getSize());

		// draw the label
		g.setColor(Color.black);		
		//g.drawString((String)labels[i], cx,
		//	     getSize().height - titleFont.getSize() - titleFontMetrics.getDescent());
		g.drawString((String)labels[i], cx,
			     myHEIGHT - titleFont.getSize() - titleFontMetrics.getDescent());

		// RJA no shadow

		// draw the shadow bar
		//if (colors[i] == Color.black) {
		//    g.setColor(Color.gray);
		//}
		//g.fillRect(cx + 5, cy - 3, barWidth,  (values[i] * scale));
		
		// draw the bar with the specified color
		g.setColor((Color)(colors[i]));
		switch (styles[i]) {
		  case SOLID:
		  default:
		    g.fillRect(cx, cy, barWidth, (values[i] * scale));
		    break;
		  case STRIPED:
		    {
			int steps = (values[i] * scale) / 2;
			int ys;

			for (j=0; j < steps; j++) {
			    ys = cy + (2 * j);
			    g.drawLine(cx, ys, cx + barWidth, ys);
			}
		    }
		    break;
		}
		g.drawString("" + values[i],
			     cx,
			     cy - titleFontMetrics.getDescent());
		break;
	      case HORIZONTAL:
		// set the Y coordinate
		cy = ((barWidth + barSpacing) * i) + barSpacing;
			       
		// set the X coordinate to be the size().width of the widest
		// label 
		cx = maxLabelWidth + 1;

		//cx += Math.max((getSize().width - (maxLabelWidth + 1 +
		//			 titleFontMetrics.stringWidth("" +
		//					       max) +
		//			 (max * scale))) / 2, 0);
		cx += Math.max((myWIDTH - (maxLabelWidth + 1 +
					 titleFontMetrics.stringWidth("" +
							       max) +
					 (max * scale))) / 2, 0);
		
		
		// draw the labels and the shadow
		g.setColor(Color.black);		
		g.drawString((String)labels[i], cx - maxLabelWidth - 1,
			     cy + titleFontMetrics.getAscent());
		
		// RJA no shadow

		//if (colors[i] == Color.black) {
		//    g.setColor(Color.gray);
		//}
		//g.fillRect(cx + 3,
		//	   cy + 5,
		//	   (values[i] * scale),
		//	   barWidth);

		// draw the bar in the current color
		g.setColor((Color)(colors[i]));
		switch (styles[i]) {
		  case SOLID:
		  default:
		    g.fillRect(cx,
			       cy,
			       (values[i] * scale),
			       barWidth);
		    break;
		  case STRIPED:
		    {
			int steps = (values[i] * scale) / 2;
			int ys;

			for (j=0; j < steps; j++) {
			    ys = cx + (2 * j);
			    g.drawLine(ys, cy, ys, cy + barWidth);
			}
		    }
		    break;
		}
		g.drawString("" + values[i] * mySHRINK,
			     cx + (values[i] * scale) + 3,
			     cy + titleFontMetrics.getAscent());
			     
		break;
	    }
	}
    } // end paint

} // end MyWebVisitStatsChart
