// WeatherMan.java
// Copyright (c) 2000
//  Jon Lin <jonlin@tesuji.org>
//
// Permission to use, copy, modify, distribute, and sell this software and its
// documentation for any purpose is hereby granted without fee, provided that
// the above copyright notice appear in all copies and that both that
// copyright notice and this permission notice appear in supporting
// documentation.  No representations are made about the suitability of this
// software for any purpose.  It is provided "as is" without express or 
// implied warranty.
//


package org.jonlin;

import java.net.*;
import java.io.*;
import java.awt.*;

//import kinetoscope.awt.*;

public class WeatherMan extends Frame//DoubleBufferedFrame
{
	public static int checkInterval;
	public static int zipcode;
	public static int initialWidth;
	public static int initialHeight;
	public static int xOffset;
	public static int yOffset;
	public static boolean dontResize;
	public static boolean dontMerge;

	public static Color bg;
	public static Color fg;
	

	WeatherThread weatherThread;

	Image currentImage;
	PicturePanel picture;
	TextPanel text;


	public static void main(String[] args)
	{
		try {
			for(int x=0;x<args.length;x++) {
				if(args[x].startsWith("-")) {
					if(args[x].startsWith("-t")) {
						checkInterval = Integer.parseInt(args[++x].trim()) * 60000;
					}
					else if(args[x].startsWith("-x")) {
						initialWidth = Integer.parseInt(args[++x].trim());
					}
					else if(args[x].startsWith("-y")) {
						initialHeight = Integer.parseInt(args[++x].trim());
					}
					else if(args[x].startsWith("-off_x")) {
						xOffset = Integer.parseInt(args[++x].trim());
					}
					else if(args[x].startsWith("-off_y")) {
						yOffset = Integer.parseInt(args[++x].trim());
					}
					else if(args[x].startsWith("-rv")) {
						fg = Color.white;
						bg = Color.black;
					}
					else if(args[x].startsWith("-fg")) {
						int red = Integer.parseInt(args[++x].trim());
						int green = Integer.parseInt(args[++x].trim());
						int blue = Integer.parseInt(args[++x].trim());
						fg = new Color(red,green,blue);
					}
					else if(args[x].startsWith("-bg")) {
						int red = Integer.parseInt(args[++x].trim());
						int green = Integer.parseInt(args[++x].trim());
						int blue = Integer.parseInt(args[++x].trim());
						bg = new Color(red,green,blue);
					}
					else if(args[x].startsWith("-dontAutoResize")) {
						dontResize = true;
					}
					else if(args[x].startsWith("-dontMerge")) {
						dontMerge = true;
					}
				}
				else {
					if(args[x].trim().length()>5) {
						System.err.println("Invalid zipcode: "+args[x]);
						return;
					}
					if(args[x].trim().length()<5) {
						System.err.println("Invalid zipcode: "+args[x]);
						return;
					}
					zipcode = Integer.parseInt(args[x].trim());
				}
			}

			if(WeatherMan.checkInterval <= 0) {
				WeatherMan.checkInterval = 15000 * 60;
			}

			if(WeatherMan.initialWidth <= 0) {
				WeatherMan.initialWidth = 110;
			}

			if(WeatherMan.initialHeight <= 0) {
				WeatherMan.initialHeight = 167;
			}

			if(!dontMerge) {
				if(xOffset==0) {
					xOffset = 10;
				}
				if(yOffset==0) {
					yOffset = 30;
				}
			}

			if(zipcode <= 0) {
				System.err.println("Must provide a zipcode.");
				throw(new Exception());
			}

			new WeatherMan();
		}
		catch(Exception e) {
			e.printStackTrace(System.err);

			System.err.println("Usage:  Java WeatherMan v0.1A");
			System.err.println("java org.jonlin.WeatherMan <options> <zipcode>");
			System.err.println(" <options>");
			System.err.println("   -t <minutes>           The number of minutes the WeatherMan updates the weather information.");
			System.err.println("   -x <width>             The initial width of the window.");
			System.err.println("   -y <height>            The initial height of the window.");
			System.err.println("   -off_x <pixels>        The main x offset.");
			System.err.println("   -off_y <pixels>        The main y offset.");
			System.err.println("   -rv                    Inverse the video (white is black and black is white).");
			System.err.println("   -fg <int> <int> <int>  The Red, Green, and Blue values for the foreground color.");
			System.err.println("   -bg <int> <int> <int>  The Red, Green, and Blue values for the background color.");
			System.err.println("   -dontAutoResize        This flag keeps the WeatherMan from resizing the window automatically.");
			System.err.println("   -dontMerge             This flag prevents Weatherman from overlaying the text on top of the picture.");
			System.err.println("\n");

		}
	}


	public WeatherMan()
	{
		super("Weather Man");

		setTitle("Weather Man");
		setResizable(true);

		if(bg!=null) {
			setBackground(bg);
		}

		picture = new PicturePanel();
		text = new TextPanel();

		setSize(initialWidth,initialHeight);

		picture.setSize(picture.getPreferredSize());
		text.setSize(text.getPreferredSize());

		if(dontMerge) {
			/*
			GridBagLayout layout = new GridBagLayout();
			setLayout(layout);
			
			GridBagConstraints constraints = new GridBagConstraints();
			constraints.insets.top	  = 0;
			constraints.insets.left	  = 0;
			constraints.insets.bottom = 0;
			constraints.insets.right  = 0;
			*/
			/*
			constraints.weightx	 = 1;
			constraints.weighty	 = 1;
			constraints.anchor    = constraints.CENTER;
			constraints.fill      = constraints.BOTH;
			constraints.gridwidth = constraints.REMAINDER;
			*/

			setLayout(new GridLayout(2,1));

			//			layout.setConstraints(picture, constraints);
			add(picture);
			
			//			layout.setConstraints(text, constraints);
			add(text);

			pack();

			setSize(initialWidth,initialHeight);
		}

		weatherThread = new WeatherThread();


		//		pack();
		//		show();



		show();

		//		setSize(initialWidth,initialHeight);

		setSize();
	}

	public Dimension getPreferredSize()
	{
		Dimension d = new Dimension();

		if(dontResize) {
			d = getSize();
		}
		else {
			if(dontMerge) {
				Dimension textSize = text.getPreferredSize();
				Dimension pictureSize = picture.getPreferredSize();
				d.width = pictureSize.width;
				d.height = pictureSize.height + textSize.height;
				//print("Pref Size: ("+d.width+","+d.height+")");
			}
			else {
				d = picture.getPreferredSize();
			}
		}

		if(d.width<=10) {
			d.width=initialWidth;
		}
		if(d.height<=10) {
			d.height=initialHeight;
		}

		return(d);
	}

	public void setSize()
	{
		//print("Setting size to "+getPreferredSize());
		setSize(getPreferredSize());
	}

	public void update()
	{
		repaint();

	}

	//	public void paintContent(Graphics g, int width, int height)
	public void paint(Graphics g)
	{
		//print("paint()");
		if(bg!=null) {
			g.setColor(bg);
			g.fillRect(-5,-5,750,750);
		}

		/*
		if(!dontResize) {
			setSize(getPreferredSize());
		}
		*/

		if(!dontMerge) {
			picture.paint(g);
			text.paint(g);
		}
		else {
			picture.repaint();
			text.repaint();
		}
	}

	public class PicturePanel extends Panel
										  implements java.awt.image.ImageObserver
	{
		//		public Image currentImage = null;

		public void update()
		{
			repaint();
		}

		public void paint(Graphics g)
		{
			//WeatherMan.this.print("PictureCanvas.paint()");
			int startTextY = yOffset;

			try {
				if(currentImage!=null) {
					//WeatherMan.this.print("image.x="+currentImage.getWidth(null)+", image.y="+currentImage.getHeight(null));
					g.drawImage(currentImage,xOffset,yOffset,currentImage.getWidth(null),currentImage.getHeight(null),PicturePanel.this);
					startTextY = startTextY+currentImage.getHeight(null)+15;
				}

				if(!dontResize) {
					PicturePanel.this.setSize(PicturePanel.this.getPreferredSize());
				}

			}
			catch(Exception e) {
				error("Could not draw current weather image.",e);
			}
	
		}

		public Dimension getPreferredSize()
		{
			Dimension d = new Dimension();

			if(dontResize) {
				d = getSize();
			}
			else {
				if(currentImage!=null) {
					d.width = currentImage.getWidth(null)+(2*xOffset);
					d.height = currentImage.getHeight(null)+(2*yOffset);
					//WeatherMan.this.print("Picture Pref Size: ("+d.width+","+d.height+")");
				}
				else {
					d = getSize();
				}
			}

			return(d);
		}

		public boolean imageUpdate(Image image, int a, int b, int c, int d, int e)
		{
			if(!dontResize) {
				PicturePanel.this.setSize(image.getWidth(null)+(2*xOffset),image.getHeight(null)+(2*yOffset));
			}
			WeatherMan.this.setSize();
			WeatherMan.this.repaint();
			return(true);
		}

	}

	public class TextPanel extends Panel
	{
		int fontHeight;

		public void update()
		{
			repaint();
		}

		public void paint(Graphics g)
		{
			//WeatherMan.this.print("TextCanvas.paint()");
			try {
				if(fg!=null) {
					g.setColor(fg);
				}
				FontMetrics fm = g.getFontMetrics();
				fontHeight = fm.getHeight();

				int mysteryConstant = 0;
				if(dontMerge) {
					mysteryConstant = fontHeight;
				}

				//print("Font start="+startTextY+", Font height="+fm.getHeight());
				if(weatherThread.currentTemp==0) {
					g.drawString("Fetching",xOffset,yOffset+(fontHeight)+mysteryConstant);
					g.drawString("Current",xOffset,yOffset+(fontHeight*2)+mysteryConstant);
					g.drawString("Weather",xOffset,yOffset+(fontHeight*3)+mysteryConstant);
				}
				else {
					g.drawString("T: "+weatherThread.currentTemp+" F",xOffset,yOffset+(fontHeight)+mysteryConstant);
					g.drawString("H: "+weatherThread.relativeHumidity+"%",xOffset,yOffset+(fontHeight*2)+mysteryConstant);
					g.drawString("P: "+weatherThread.barometer+" in",xOffset,yOffset+(fontHeight*3)+mysteryConstant);
				}
				if(!dontResize) {
					TextPanel.this.setSize(TextPanel.this.getPreferredSize());
				}
			}
			catch(Exception e) {
				error("Could not draw text for weather information.",e);
			}
		}

		public Dimension getPreferredSize()
		{
			Dimension d = new Dimension();

			if(dontResize) {
				d = getSize();
			}
			else {
				if(currentImage!=null) {
					if(dontMerge) {
						d.width = currentImage.getWidth(null)+(2*xOffset);
						d.height = (4*fontHeight)+(2*yOffset);
					}
					else {
						d = picture.getPreferredSize();
					}
				}
				else {
					d = getSize();
				}
			}
			
			return(d);
		}
	}


	public class WeatherThread extends Thread
	{
		public String currentImageURL;
		public int currentTemp;
		public int relativeHumidity;
		public float barometer;

		public WeatherThread()
		{
			super("WeatherThread");
			start();
		}
		
		public void run()
		{
			boolean firstTime = true;

			while(true) {
				try {
					WeatherMan.this.repaint();

					parsePage(getWeatherPage());
					
					updateWeatherMan();

					//					picture.repaint();
					//					text.repaint();
					WeatherMan.this.repaint();

					//					if(firstTime) {
					//						WeatherMan.this.show();
					//						firstTime = false;
					//					}

					try {
						sleep(checkInterval);
					}
					catch(Exception e) {;}
				}
				catch(Exception e) {
					error("Error in Weather Gathering Thread.",e);
				}
			}
		}


		void updateWeatherMan()
		{
			try {
				//				Image oldImage = currentImage;
				currentImage = getToolkit().getImage(new URL(currentImageURL));
				//				oldImage.flush();
				//				currentImage = getToolkit().getImage(new URL("http://www.weather.com/weather/wx_icons/PFMScurrent/39.gif"));
				//print("Got image for picture.");
			}
			catch(Exception e) {
				error("Could not load the current weather image.",e);
			}
		}



		String getWeatherPage()
		{
			String rval = null;
			Socket socket = null;
			BufferedReader in = null;
			BufferedWriter out = null;
			try {
				// Get Socket
				try {
					socket = new Socket("www.weather.com",80);
					socket.setSoTimeout(5000);
					in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
					out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
				}
				catch(Exception e) {
					error("Cannot open socket to \"www.weather.com\"",e);
				}

				if(out!=null) {
					// write short HTTP/1.0 request
					out.write("GET /weather/us/zips/"+zipcode+".html\n\n");
					out.flush();
					
					// read the HTML (weather)
					StringBuffer page = new StringBuffer();
					try {
						String line = in.readLine();
						while((line!=null) || (!line.trim().equals("<HTML>"))) {
							page.append(line);
							page.append("\n");
							line = in.readLine();
						}
					}
					catch(Exception e) {;}
					rval = page.toString();
				}
				
			}
			catch(Exception e) {
				error("Error opening \"http://www.weather.com/weather/us/zips/"+zipcode+".html\".",e);
			}
	
			if(in!=null) {
				try {
					in.close();
				}
				catch(Exception e1) {;}
			}
			if(out!=null) {
				try {
					out.close();
				}
				catch(Exception e1) {;}
			}
			if(socket!=null) {
				try {
					socket.close();
				}
				catch(Exception e1) {;}
			}

			return(rval);
		}


		void parsePage(String html)
		{
			try {
				int index = html.indexOf("<!-- begin content -->");
				//print("Found begin context");
				if(index>0) {
					html = html.substring(index);
					index = html.indexOf("CURRENTLY");

					//print("Found currently");
					if(index>0) {
						html = html.substring(index);

						// look for the first image
						index = html.indexOf("<IMG SRC=\"");
						if(index>0) {
							index = index+10;
							html = html.substring(index);
							index = html.indexOf("\"");
							if(index>0) {
								currentImageURL = html.substring(0,index);
							}
							//print("Current Image URL: "+currentImageURL);
						}
							
						// look for the current temperature.
						index = html.indexOf("<B>Temp:</B>");
						if(index>0) {
							html = html.substring(index);
							index = html.indexOf("<FONT FACE");
							index = html.indexOf(">",index);
							html = html.substring(index+1);
							index = html.indexOf("&deg;");
							currentTemp = Integer.parseInt(html.substring(0,index).trim());
							//print("Current Temp: "+currentTemp);
						}
						
						// look for the Relative Humidity.
						index = html.indexOf("<B>Rel. humidity:</B>");
						if(index>0) {
							html = html.substring(index);
							index = html.indexOf("<FONT FACE");
							index = html.indexOf(">",index);
							html = html.substring(index+1);
							index = html.indexOf("%");
							relativeHumidity = Integer.parseInt(html.substring(0,index).trim());
						}

						// look for the Barometer.
						index = html.indexOf("<B>Barometer:</B>");
						if(index>0) {
							html = html.substring(index);
							index = html.indexOf("<FONT FACE");
							index = html.indexOf(">",index);
							html = html.substring(index+1);
							index = html.indexOf("inches");
							barometer = Float.valueOf(html.substring(0,index).trim()).floatValue();
						}
					}
					else {
						error("Cannot find the current weather content from the web page.");
					}
				}
				else {
					error("Cannot find the beginning of the weather content from the web page.");
				}
			}
			catch(Exception e) {
				error("Error parsing HTML containing the weather information.",e);
			}
		}
	}


	public void print(String s)
	{
		System.out.println("[WeatherMan] "+s);
	}

	public void error(String s)
	{
		System.err.println("{WeatherMan} "+s);
	}

	public void error(String s, Exception e)
	{
		System.err.println("{WeatherMan} "+e+" : "+s);
		e.printStackTrace(System.err);
	}
}


