import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

/**
 *
 * An applet that allows one to enter a date & time in one time zone
 * and displays the corresponding date and time in another time zone.
 * Can also be run stand-alone.
 *
 * @author Bert Bos <bert@w3.org>
 * @version $Id: tzcomputer.java,v 1.4 2002/10/24 11:48:33 bbos Exp $
 *
 **/
public class tzcomputer extends Applet implements TextListener, ItemListener
{
  private static final int NR_DISPLAYS = 6;	// How many rows of dates & times

  protected final static TimeZone UTC = new SimpleTimeZone(0, "UTC");
  protected final static TimeZone BST = new SimpleTimeZone
  (0, "BST",
   Calendar.MARCH, -1, Calendar.SUNDAY, 2 * 60*60*1000,
   Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60*60*1000);

  protected final String[][] params = {
    {"fgcolor", "6 hex digits", "foreground color as RRGGBB"},
    {"bgcolor", "6 hex digits", "background color as RRGGBB"},
  };

  private static final TimeZone zoneID[] = {
    UTC,
    BST,
    TimeZone.getTimeZone("ECT"),
    TimeZone.getTimeZone("EET"),
    TimeZone.getTimeZone("ART"),
    TimeZone.getTimeZone("EAT"),
    TimeZone.getTimeZone("MET"),
    TimeZone.getTimeZone("NET"),
    TimeZone.getTimeZone("PLT"),
    TimeZone.getTimeZone("IST"),
    TimeZone.getTimeZone("BST"),
    TimeZone.getTimeZone("VST"),
    TimeZone.getTimeZone("CTT"),
    TimeZone.getTimeZone("JST"),
    TimeZone.getTimeZone("ACT"),
    TimeZone.getTimeZone("AET"),
    TimeZone.getTimeZone("SST"),
    TimeZone.getTimeZone("NST"),
    TimeZone.getTimeZone("MIT"),
    TimeZone.getTimeZone("HST"),
    TimeZone.getTimeZone("AST"),
    TimeZone.getTimeZone("PST"),
    TimeZone.getTimeZone("PNT"),
    TimeZone.getTimeZone("MST"),
    TimeZone.getTimeZone("CST"),
    TimeZone.getTimeZone("EST"),
    TimeZone.getTimeZone("IET"),
    TimeZone.getTimeZone("PRT"),
    TimeZone.getTimeZone("CNT"),
    TimeZone.getTimeZone("AGT"),
    TimeZone.getTimeZone("BET"),
    TimeZone.getTimeZone("CAT")};
  private static final String zonedesc[] = {
    "UTC/GMT",
    "London (BST)",
    "Paris (MET)",
    "Athens (EET)",
    "Cairo",
    "Nairobi (EAT)",
    "Baghdad",
    "Near East (?)",
    "Karachi (PKT)",
    "New Delhi (ICT)",
    "Dacca (BGT)",
    "Hanoi",
    "Taipei (CST)",
    "Tokyo (JST)",
    "Darwin",
    "Sydney",
    "Solomon Islands",
    "Wellington",
    "Midway Islands",
    "Hawaii",
    "Anchorage",
    "Los Angeles (Pacific)",
    "Phoenix",
    "Denver (Mountain)",
    "Chicago (Central)",
    "New York (Eastern)",
    "Indiana",
    "Puerto Rico",
    "Newfoundland",
    "Buenos Aires (ARST)",
    "Sao Paulo",
    "Kinshasa (CAT)"};
  private final String monthname[] = {
    "January", "February", "March", "April", "May", "June", "July",
    "August", "September", "October", "November", "December"};

  private NumField day[] = new NumField[NR_DISPLAYS];
  private NumField year[] = new NumField[NR_DISPLAYS];
  private NumField hour[] = new NumField[NR_DISPLAYS];
  private NumField minute[] = new NumField[NR_DISPLAYS];
  private Choice timezone[] = new Choice[NR_DISPLAYS];
  private Choice month0;
  private TextField month[] = new TextField[NR_DISPLAYS];



  /** create a window and display the applet in it */
  public static void main(String args[])
  {
    //String z[] = TimeZone.getAvailableIDs();
    //for (int i = 0; i < z.length; i++) System.out.println(z[i]);

    Frame win = new Frame("tzcomputer");	// Create a top level window
    win.addWindowListener(new CloseHandler());	// Add handler for Close events
    tzcomputer t = new tzcomputer();
    t.init();
    win.add(t);					// Add a tzcomputer applet
    win.pack();					// Compute layout & size
    t.start();
    win.show();					// Display the window
  }


  /** return applet's parameter info */
  public String[][] getParameterInfo() {return params;}


  /** return description of th applet */
  public String getAppletInfo()
  {
    return "An applet that translates a given date & time for different time zones.";
  }


  /** (constructor) populate the applet with widgets */
//  public tzcomputer()
//  {
//  }


  /** initialize the applet */
  public void init()
  {
    Calendar now = new GregorianCalendar(UTC);
    now.setTime(new Date());			// Get current time in UTC
    String now_day = "" + now.get(Calendar.DAY_OF_MONTH);
    int now_month = now.get(Calendar.MONTH);
    String now_year = "" + now.get(Calendar.YEAR);
    String now_hour = "" + now.get(Calendar.HOUR_OF_DAY);
    String now_minute = "" + now.get(Calendar.MINUTE);

    // Get parameters
    try {
      String font;
      if ((font = getParameter("font-family")) == null) font = "Helvetica";
      int res = Toolkit.getDefaultToolkit().getScreenResolution();
      int wd = size().width;
      setFont(new Font(font, Font.PLAIN, 2*wd/res));
    } catch (Exception e) {}
    try {
      setForeground(new Color(Integer.parseInt(getParameter("fgcolor"), 16)));
    } catch (Exception e) {}
    try {
      setBackground(new Color(Integer.parseInt(getParameter("bgcolor"), 16)));
    } catch (Exception e) {}

    // Create a GridBagLayout manager
    LayoutManager layout = new GridBagLayout();
    GridBagConstraints constraints = new GridBagConstraints();
    setLayout(layout);

    // Create row of labels
    //constraints.gridx = 0;
    //constraints.gridy = 0;
    //constraints.gridwidth = 1;
    //constraints.gridheight = 1;
    //constraints.weightx = 0;
    //constraints.weighty = 0;
    //constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = GridBagConstraints.RELATIVE;
    add(new Label("day"), constraints);
    add(new Label("month"), constraints);
    add(new Label("year"), constraints);
    add(new Label("hour"), constraints);
    add(new Label("min."), constraints);
    constraints.gridwidth = GridBagConstraints.REMAINDER;
    add(new Label("zone"), constraints);

    // Create row of input fields

    // Create NR_DISPLAYS rows of date & time fields
    // ToDo: initialize fields with current date & time
    for (int i = 0; i < NR_DISPLAYS; i++) {

      constraints.gridwidth = 1;
      day[i] = new NumField(now_day, 2);	// New field, init'ed to today
      if (i == 0) day[i].addTextListener(this);
      else day[i].setEditable(false);
      add(day[i], constraints);

      if (i == 0) {
	month0 = new Choice();
	for (int j = 0; j < 12; j++) month0.addItem(monthname[j]);
	month0.addItemListener(this);
	month0.select(now_month);		// Initialize to today's month
	add(month0, constraints);
      } else {
	month[i] = new TextField(9);
	month[i].setEditable(false);
	add(month[i], constraints);
      }

      year[i] = new NumField(now_year, 4);	// New field, init'ed to today
      if (i == 0) year[i].addTextListener(this);
      else year[i].setEditable(false);
      add(year[i], constraints);

      hour[i] = new NumField(now_hour, 2);	// New field, init'ed to today
      if (i == 0) hour[i].addTextListener(this);
      else hour[i].setEditable(false);
      add(hour[i], constraints);

      minute[i] = new NumField(now_minute, 2);	// New field, init'ed to today
      if (i == 0) minute[i].addTextListener(this);
      else minute[i].setEditable(false);
      add(minute[i], constraints);

      timezone[i] = new Choice();		// New choice (init = UTC/GMT)
      for (int j = 0; j < zonedesc.length; j++)
	timezone[i].addItem(zonedesc[j]);
      timezone[i].addItemListener(this);
      constraints.gridwidth = GridBagConstraints.REMAINDER;
      add(timezone[i], constraints);
    }
  }

  /** recompute all rows, based on the value of row 0 */
  private void recompute()
  {
    try {					// Don't update if field illegal

      // Get values from fields in first row, make a Calendar out of them
      //Calendar cal1 =
      //Calendar.getInstance(zoneID[timezone[0].getSelectedIndex()]);
      GregorianCalendar cal1 =
	new GregorianCalendar(zoneID[timezone[0].getSelectedIndex()]);
      int year1 = Integer.parseInt(year[0].getText());
      int month1 = month0.getSelectedIndex();
      int day1 = Integer.parseInt(day[0].getText());
      int hour1 = Integer.parseInt(hour[0].getText());
      int minute1 = Integer.parseInt(minute[0].getText());
      cal1.set(year1, month1, day1, hour1, minute1, 0);

      // Update the other rows, based on their time zone
      for (int i = 1; i < NR_DISPLAYS; i++) {

	try {					// Catch wrong time zone names
	  //Calendar cal2 =
	  //Calendar.getInstance(zoneID[timezone[i].getSelectedIndex()]);
	  GregorianCalendar cal2 =
	    new GregorianCalendar(zoneID[timezone[i].getSelectedIndex()]);
	  cal2.setTime(cal1.getTime());
	  year[i].setText("" + cal2.get(Calendar.YEAR));
	  month[i].setText(monthname[cal2.get(Calendar.MONTH)]);
	  int h = cal2.get(Calendar.DAY_OF_MONTH);
	  if (h < 10) day[i].setText("0" + h); else day[i].setText("" + h);
	  h = cal2.get(Calendar.HOUR_OF_DAY);
	  if (h < 10) hour[i].setText("0" + h); else hour[i].setText("" + h);
	  h = cal2.get(Calendar.MINUTE);
	  if (h < 10) minute[i].setText("0" + h); else minute[i].setText("" + h);

	} catch (Exception e) {}
      }
    } catch (Exception e) {}
  }


  // ----------------------------------------------------------------------
  // Implements ItemListener
  // ----------------------------------------------------------------------


  /** a Choice changed; update all other fields */
  public void itemStateChanged(ItemEvent e) {recompute();}


  // ----------------------------------------------------------------------
  // Implements TextListener
  // ----------------------------------------------------------------------


  /** a TextField changed; update all other fields */
  public void textValueChanged(TextEvent e) {recompute();}

}


class CloseHandler extends WindowAdapter
{
  public void windowClosing(java.awt.event.WindowEvent e) {System.exit(0);}
}


/**
 *
 * A NumField is a TextField that only accepts digits.
 *
 **/
class NumField extends TextField
{
  private Toolkit toolkit;			// Used for beep


  /** Make NumField columns wide with intial text */
  public NumField(String s, int columns)
  {
    super(s, columns);
    toolkit = this.getToolkit();
    enableEvents(AWTEvent.KEY_EVENT_MASK);
  }


  /** Make NumField columns wide */
  public NumField(int columns) {this("", columns);}


  /** Make NumField with intial text */
  public NumField(String s) {this(s, 0);}


  /* Don't process the key unless it is a digit */
  protected void processKeyEvent(KeyEvent e)
  {
    char c = e.getKeyChar();
    if (e.getID() != KeyEvent.KEY_TYPED) super.processKeyEvent(e);
    else if (Character.isISOControl(c)) super.processKeyEvent(e);
    else if (! Character.isDigit(c)) {toolkit.beep(); e.consume();}
    else if (getText().length() == getColumns()) {toolkit.beep(); e.consume();}
    else super.processKeyEvent(e);
  }

}
