package com.mwc.cosc610.project1;

import java.io.*;
import java.util.*;
import java.text.*;
import java.lang.reflect.*;
import javax.servlet.*;
import javax.servlet.http.*;

import com.mwc.util.*;

/**
 * COSC610 - Project #1
 * 
 * Manage a table of persistent people objects with a web
 * based interface.
 * 
 * Used APIs: IO, Util, Text API,
 *            Servlet API, Collections API, 
 *            Object Serialization API 
 *            and the Reflection API
 * 
 * @author Matthew W. Coan (9/3/2002)
 */

/**
 * A basic person representation that 
 * can be serialized:
 */
class Person implements Serializable {
   private int _id;
   private String _firstName;
   private String _lastName;
   private Date _birthDate;
   private boolean _sex;
   private String _zip;
   
   public Person() {
      super();
   }

   public Person(int id,
                 String firstName,
                 String lastName,
                 Date birthDate,
                 boolean sex,
                 String zip) {
      super();
      _id = id;
      _firstName = firstName;
      _lastName = lastName;
      _birthDate = birthDate;
      _sex = sex;
      _zip = zip;
   }
   
   public int id() { return _id; }
   public String firstName() { return _firstName; }
   public String lastName() { return _lastName; }
   public Date birthDate() { return _birthDate; }
   public boolean isMale() { return _sex; }
   public boolean isFemale() { return !_sex; }
   public String zip() { return _zip; }

   public void setFirstName(String firstName) { _firstName = firstName; }
   public void setLastName(String lastName) { _lastName = lastName; }
   public void setBirthDate(Date birthDate) { _birthDate = birthDate; }
   public void setSex(boolean sex) { _sex = sex; }
   public void setZip(String zip) { _zip = zip; }
}

/**
 * Person Comparator factory class. Create a comparator for 
 * the way you wish to sort the person list.
 */
class PersonCompFactory {
   public static Comparator createComp(String sortBy, String order) {
      if(order.compareTo("ASC") == 0) {
         if(sortBy.compareTo("ID") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  if(p1.id() == p2.id())
                     return 0;
                  else if(p1.id() < p2.id())
                     return -1;
                  else
                     return 1;
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("firstName") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  return p1.firstName().compareTo(p2.firstName());
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("lastName") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  return p1.lastName().compareTo(p2.lastName());
               }
                  
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("birth") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
               Person p1 = (Person)o1;
               Person p2 = (Person)o2;
               return p1.birthDate().compareTo(p2.birthDate());
               }
                  
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("sex") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  if(p1.isMale() == p2.isMale())
                     return 0;
                  else if(p1.isMale() == true)
                     return 1;
                  else
                     return -1;
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else 
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  return p1.zip().compareTo(p2.zip());
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
      }
      else {
         if(sortBy.compareTo("ID") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  if(p1.id() == p2.id())
                     return 0;
                  else if(p1.id() < p2.id())
                     return 1;
                  else
                     return -1;
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("firstName") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  return -p1.firstName().compareTo(p2.firstName());
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("lastName") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  return -p1.lastName().compareTo(p2.lastName());
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else if(sortBy.compareTo("birth") == 0)
            return new Comparator() {
            public int compare(Object o1, Object o2) {
               Person p1 = (Person)o1;
               Person p2 = (Person)o2;
               return -p1.birthDate().compareTo(p2.birthDate());
            }
   
            public boolean equals(Object obj) {
               return false;
            }
         };
         else if(sortBy.compareTo("sex") == 0)
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  if(p1.isMale() == p2.isMale())
                     return 0;
                  else if(p1.isMale() == true)
                     return -1;
                  else
                     return 1;
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
         else 
            return new Comparator() {
               public int compare(Object o1, Object o2) {
                  Person p1 = (Person)o1;
                  Person p2 = (Person)o2;
                  return -p1.zip().compareTo(p2.zip());
               }
   
               public boolean equals(Object obj) {
                  return false;
               }
            };
      }
   }
}

/**
 * Main servlet class:
 */
public class Project1 extends HttpServlet {
   public final static long MAX_MALE_AGE = 2207520000L;//70 * 365 * 24 * 60 * 60;
   public final static long MAX_FEMALE_AGE = 2522880000L;//80 * 365 * 24 * 60 * 60;
   
   private int _currentId = 0;
   private Object _idLock = new Object();
   private String _personFileName;
   private TreeMap _personMap = new TreeMap();
   private TreeMap _eventHandlerMap = new TreeMap();
   
   // Init the servlet
   public void init(ServletConfig config) 
   throws ServletException {
      super.init(config);
      // get person file name
      if((_personFileName = config.getInitParameter("personFileName")) == null)
         _personFileName = "person.dat";      
      
      // Load the event handlers
      Class clazz = Project1.class;
      Class argTypes[] = new Class[] { HttpServletRequest.class, 
                                       HttpServletResponse.class };
      try {
         Method m = clazz.getMethod("_onShowMainPage", argTypes);
         _eventHandlerMap.put("onShowMainPage", m);
         m = clazz.getMethod("_onShowAddPersonPage", argTypes);
         _eventHandlerMap.put("onShowAddPersonPage", m);
         m = clazz.getMethod("_onAddPerson", argTypes);
         _eventHandlerMap.put("onAddPerson", m);
         m = clazz.getMethod("_onShowPersonListPage", argTypes);
         _eventHandlerMap.put("onShowPersonListPage", m);
         m = clazz.getMethod("_onShowEditPersonPage", argTypes);
         _eventHandlerMap.put("onShowEditPersonPage", m);
         m = clazz.getMethod("_onEditPerson", argTypes);
         _eventHandlerMap.put("onEditPerson", m);
         m = clazz.getMethod("_onShowLifePage", argTypes);
         _eventHandlerMap.put("onShowLifePage", m);
         m = clazz.getMethod("_onDeletePerson", argTypes);
         _eventHandlerMap.put("onDeletePerson", m);
      }
      catch(Throwable th) {
         throw new ServletException(th.getMessage(), th);
      }

      // load the data
      try {
         if(new File(_personFileName).exists()) {
            FileInputStream fin = new FileInputStream(_personFileName);
            BufferedInputStream bin = new BufferedInputStream(fin);
            ObjectInputStream oin = new ObjectInputStream(bin);
            Person p;
            try {
               _currentId = oin.readInt();
               while((p = (Person)oin.readObject()) != null) {
                  _personMap.put(p.id()+"", p);
               }
            }
            catch(EOFException eof) {
            }
            fin.close();
         }
      }
      catch(Throwable th) {
         throw new ServletException(th.getMessage(), th);
      }
   }

   // Send back an error page
   private void _sendErrorPage(HttpServletResponse response, String errors)
   throws IOException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Error</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>Project 1 -- Error</H1>");
      out.println("<FONT FACE=\"Arial\" COLOR=\"red\">");
      out.println(errors);
      out.println("</FONT><BR><BR>");
      out.println("<A HREF=\"javascript:history.go(-1);\">Back</A>");
      out.println("</FONT></BODY></HTML>");
      out.flush();
   }
   
   // Send back a report page
   private void _sendReportPage(HttpServletResponse response, String msg)
   throws IOException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Report</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>Project 1 -- Report</H1>");
      out.println(msg);
      out.println("<BR><A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowMainPage\">Main Page</A>");
      out.println("</FONT></BODY></HTML>");
      out.flush();
   }

   // Print out a date drop down
   private void _printDateHTML(int month, int day, int year, PrintWriter out)
   throws IOException {
      out.println("<SELECT NAME=\"month\">");
      for(int i = 1; i <= 12; i++) {
         if(i == month)
            out.println("<OPTION SELECTED VALUE=\""+i+"\">"+i+"</OPTION>");
         else
            out.println("<OPTION VALUE=\""+i+"\">"+i+"</OPTION>");
      }
      out.println("</SELECT>");
      out.println(" / <SELECT NAME=\"day\">");
      for(int i = 1; i <= 31; i++) {
         if(i == day)
            out.println("<OPTION SELECTED VALUE=\""+i+"\">"+i+"</OPTION>");
         else
            out.println("<OPTION VALUE=\""+i+"\">"+i+"</OPTION>");
      }
      out.println("</SELECT>");
      out.println(" / <INPUT NAME=\"year\" TYPE=\"TEXT\" SIZE=\"4\" "
                  +"MAXLENGTH=\"4\" VALUE=\"" + year + "\">");
   }
   
   // Save the person data
   private synchronized void _saveData() 
   throws IOException {
      FileOutputStream fout = new FileOutputStream(_personFileName);
      BufferedOutputStream bout = new BufferedOutputStream(fout);
      ObjectOutputStream oout = new ObjectOutputStream(bout);
      Person p;
      synchronized(_idLock) {
         oout.writeInt(_currentId);
      }
      synchronized(_personMap) {
         Iterator it = _personMap.values().iterator();
         while(it.hasNext()) {
            p = (Person)it.next();
            synchronized(p) {
               oout.writeObject(p);
            }
         }
      }
      oout.flush();
      fout.close();
   }

   // Print out the sort by otpions.
   private void _printSortByOptions(PrintWriter out, String sortBy) {
      String optArray[][] = {{"ID", "ID"},
                             {"firstName","First name"},
                             {"lastName", "Last name"},
                             {"birth", "Birth date"},
                             {"sex", "Sex"}, 
                             {"zip", "Zip"}};
      for(int i = 0; i < optArray.length; i++) {
         if(sortBy.compareTo(optArray[i][0]) == 0)
            out.println("<OPTION SELECTED VALUE=\""+optArray[i][0]+"\">"
                        +optArray[i][1]+"</OPTION>");
         else
            out.println("<OPTION VALUE=\""+optArray[i][0]+"\">"
                        +optArray[i][1]+"</OPTION>");
      }
      out.flush();
   }
   
   // Print the sorting form.
   private void _printSortForm(String event, PrintWriter out, 
                               String sortBy, String order) {
      out.println("<FORM METHOD=\"GET\" ACTION=\"/servlet/com.mwc.cosc610.project1.Project1\">");
      out.println("<INPUT TYPE=\"HIDDEN\" NAME=\"event\" VALUE=\""+event+"\">");
      out.println("Sort by <SELECT NAME=\"sortBy\">");
      _printSortByOptions(out, sortBy);
      out.println("</SELECT> in ");
      out.println("<SELECT NAME=\"order\">");
      if(order.compareTo("ASC") == 0) {
         out.println("<OPTION SELECTED VALUE=\"ASC\">ascending</OPTION>");
         out.println("<OPTION VALUE=\"DESC\">descending</OPTION>");
      }
      else {
         out.println("<OPTION VALUE=\"ASC\">ascending</OPTION>");
         out.println("<OPTION SELECTED VALUE=\"DESC\">descending</OPTION>");
      }
      out.println("</SELECT> order ");
      out.println("<INPUT TYPE=SUBMIT VALUE=\"Sort\">");
      out.println("</FORM><BR>");
   }
   
   // Process requests (called by the servlet container)
   public void service(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      String event = request.getParameter("event");
      if(event == null) 
         _onShowMainPage(request, response);
      else {
         try {
            Method m = (Method)_eventHandlerMap.get(event);
            if(m == null)
               throw new ServletException("Bad event name!");
            m.invoke(this, new Object[] { request, response });
         }
         catch(Throwable th) {
            throw new ServletException(th.getMessage(), th);
         }
      }
   }
   
   //////////////////
   // Event handlers:
   //////////////////
   
   // Show the main page
   public void _onShowMainPage(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Main Page</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>COSC 610.001 Project 1 -- Main Page</H1>");
      out.println("<FONT SIZE=\"1\"><I>By: Matthew W. Coan</I></FONT><BR>");
      out.println("<UL><LI><A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowAddPersonPage\">");
      out.println("Add Person</A> -- Add a person</LI>");
      out.println("<LI><A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowPersonListPage\">");
      out.println("Show Person List</A> -- Display the person list</LI>");
      out.println("<LI><A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowLifePage\">");
      out.println("Life</A> -- Display the remaining life for each person</LI>");
      out.println("</UL></FONT></BODY></HTML>");
      out.flush();
   }
   
   // Show the add person page
   public void _onShowAddPersonPage(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Add Person Page</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>Project 1 -- Add Person Page</H1>");
      out.println("<FORM METHOD=\"POST\" ACTION=\"/servlet/com.mwc.cosc610.project1.Project1\">");
      out.println("<INPUT TYPE=\"HIDDEN\" NAME=\"event\" VALUE=\"onAddPerson\">");
      out.println("<TABLE BORDER=\"1\">");
      out.println("<TR><TD>First name:</TD><TD><INPUT TYPE=\"TEXT\" NAME=\"firstName\"></TD></TR>");
      out.println("<TR><TD>Last name:</TD><TD><INPUT TYPE=\"TEXT\" NAME=\"lastName\"></TD></TR>");
      out.println("<TR><TD>Birth date:</TD><TD>");
      _printDateHTML(3,25,1976,out);
      out.println("</TD></TR>");
      out.println("<TR><TD>Sex:</TD><TD><SELECT NAME=\"sex\">"
                  +"<OPTION></OPTION><OPTION VALUE=\"true\">Male</OPTION>");
      out.println("<OPTION VALUE=\"false\">Female</OPTION></SELECT></TD></TR>");
      out.println("<TR><TD>Zip:</TD><TD><INPUT TYPE=\"TEXT\" NAME=\"zip\"></TD></TR>");
      out.println("<TR><TD COLSPAN=\"2\"><CENTER><INPUT TYPE=SUBMIT> *"
                  +"<INPUT TYPE=RESET></CENTER></TD></TR>");
      out.println("</TABLE>");
      out.println("</FORM><BR>");
      out.println("<A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowMainPage\">Main Page</A>");
      out.println("</FONT></BODY></HTML>");
      out.flush();
   }   

   // Add a new person
   public void _onAddPerson(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      StringBuffer errors = new StringBuffer();
      String firstName = request.getParameter("firstName");
      if(firstName == null || firstName.length() == 0)
         errors.append("Please fill out the first name field.<BR>\n");
      String lastName = request.getParameter("lastName");
      if(lastName == null || lastName.length() == 0)
         errors.append("Please fill out the last name field.<BR>\n");
      Date birthDate = null;
      try {
         String month = request.getParameter("month");
         String day = request.getParameter("day");
         String year = request.getParameter("year");
         SimpleDateFormat format = new SimpleDateFormat("M/d/yyyyy");
         birthDate = format.parse(month+"/"+day+"/"+year);
      }
      catch(Throwable th1) {
         errors.append("Please select a valid birth date. (m/d/yyyy)<BR>\n");
      }
      boolean sex = false;
      try {
         sex = new Boolean(request.getParameter("sex")).booleanValue();
      }
      catch(Throwable th) {
         errors.append("Please select a sex.<BR>\n");
      }
      String zip = request.getParameter("zip");
      if(zip == null || zip.length() != 5)
         errors.append("Zip field must be 5 digits long.<BR>\n");
      else {
         for(int i = 0; i < zip.length(); i++) {
            if(!Character.isDigit(zip.charAt(i))) {
               errors.append("Zip field must be 5 digits long.<BR>\n");
            }
         }
      }
      
      if(errors.length() == 0) {
         int id;
         synchronized(_idLock) {
            id = _currentId;
            _currentId++;
         }
         Person p = new Person(id, firstName, lastName, birthDate, sex, zip);
         synchronized(_personMap) {
            _personMap.put(p.id()+"", p);
         }
         _saveData();
         _sendReportPage(response, "New person added ID = " + p.id()+"<BR>\n");
      }
      else
         _sendErrorPage(response, errors.toString());
   }   
   
   // Show a listing of the people
   public void _onShowPersonListPage(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Person List</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>Project 1 -- Person List</H1>");
      String sortBy = request.getParameter("sortBy");
      if(sortBy == null) 
         sortBy = "ID";
      String order = request.getParameter("order");
      if(order == null)
         order = "ASC";
      _printSortForm("onShowPersonListPage", out, sortBy, order);
      out.println("<TABLE BORDER=\"1\">");
      out.println("<TR><TD><B>ID</B></TD>");
      out.println("<TD><B>First name</B></TD>");
      out.println("<TD><B>Last name</B></TD>");
      out.println("<TD><B>Birth date</B></TD>");
      out.println("<TD><B>Sex</B></TD>");
      out.println("<TD><B>Zip</B></TD>");
      out.println("<TD><B>Edit</B></TD>");
      out.println("<TD><B>Delete</B></TD>");
      out.println("</TR>");
      LinkedList list = new LinkedList();
      Iterator it;
      synchronized(_personMap) {
         it = _personMap.values().iterator();
         while(it.hasNext()) {
            list.add(it.next());
         }
      }
      Collections.sort(list, PersonCompFactory.createComp(sortBy, order));
      it = list.iterator(); 
      Person p;
      SimpleDateFormat dateFormat = new SimpleDateFormat("M/d/yyyyy");
      while(it.hasNext()) {
         p = (Person)it.next();
         synchronized(p) {
            out.println("<TR><TD>"+p.id()+"</TD>");
            out.println("<TD>"+FormEncoder.encode(p.firstName())+"</TD>");
            out.println("<TD>"+FormEncoder.encode(p.lastName())+"</TD>");
            out.println("<TD>"+dateFormat.format(p.birthDate())+"</TD>");
            out.println("<TD>"+(p.isMale()?("Male"):("Female"))+"</TD>");
            out.println("<TD>"+FormEncoder.encode(p.zip())+"</TD>");
            out.println("<TD><A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                        +"event=onShowEditPersonPage&id="
                        +p.id()+"\">Edit</A></TD>");
            out.println("<SCRIPT LANGUAGE=\"JavaScript\">");
            out.println("function del_"+p.id()+"() {");
            out.println("   if(confirm(\"Delete person with ID = " + p.id() + "?\")) {");
            out.println("      document.location = \"/servlet/com.mwc.cosc610.project1.Project1?"
                        +"event=onDeletePerson&id="
                        +p.id()+"\";");
            out.println("   }");
            out.println("}");
            out.println("</SCRIPT>");
            out.println("<TD><A HREF=\"javascript:del_"+p.id()+"();\">Delete</A></TD>");
         }
         out.println("</TR>");
      }
      out.println("</TABLE><BR><BR>");
      out.println("<A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowMainPage\">Main Page</A>");
      out.println("</FONT></BODY></HTML>");
      out.flush();
   }

   // Delete a person.
   public void _onDeletePerson(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      String id = request.getParameter("id");
      synchronized(_personMap) {
         _personMap.remove(id);
      }
      _saveData();
      _sendReportPage(response, "User ID = " + id + " removed.<BR>");
   }
   
   // Show the edit page for a person
   public void _onShowEditPersonPage(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      String id = request.getParameter("id");
      Person p;
      synchronized(_personMap) {
         if(id == null || (p = (Person)_personMap.get(id)) == null) {
            _sendErrorPage(response, "Bad person ID!<BR>\n");
            return;
         }
      }
         
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Edit Person Page</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>Project 1 -- Edit Person Page</H1>");
      out.println("<FORM METHOD=\"POST\" ACTION=\"/servlet/com.mwc.cosc610.project1.Project1\">");
      out.println("<INPUT TYPE=\"HIDDEN\" NAME=\"event\" VALUE=\"onEditPerson\">");
      synchronized(p) {
         out.println("<INPUT TYPE=\"HIDDEN\" NAME=\"id\" VALUE=\""+p.id()+"\">");
         out.println("<TABLE BORDER=\"1\">");
         out.println("<TR><TD>ID:</TD><TD>"+p.id()+"</TD></TR>");
         out.println("<TR><TD>First name:</TD><TD><INPUT TYPE=\"TEXT\" "
                     +"NAME=\"firstName\" VALUE=\""+FormEncoder.encode(p.firstName())+"\"></TD></TR>");
         out.println("<TR><TD>Last name:</TD><TD><INPUT TYPE=\"TEXT\" "
                     +"NAME=\"lastName\" VALUE=\""+FormEncoder.encode(p.lastName())+"\"></TD></TR>");
         out.println("<TR><TD>Birth date:</TD><TD>");
         GregorianCalendar gc = new GregorianCalendar();
         gc.setTime(p.birthDate());
         _printDateHTML(gc.get(Calendar.MONTH)+1,
                        gc.get(Calendar.DAY_OF_MONTH),
                        gc.get(Calendar.YEAR),
                        out);
         out.println("</TD></TR>");
         out.println("<TR><TD>Sex:</TD><TD><SELECT NAME=\"sex\"><OPTION "
                     +(p.isMale()?("SELECTED"):(""))+" VALUE=\"true\">Male</OPTION>");
         out.println("<OPTION "+(p.isFemale()?("SELECTED"):(""))
                     +" VALUE=\"false\">Female</OPTION></SELECT></TD></TR>");
         out.println("<TR><TD>Zip:</TD><TD><INPUT TYPE=\"TEXT\" NAME=\"zip\" VALUE=\""
                     +FormEncoder.encode(p.zip())+"\"></TD></TR>");
      }
      out.println("<TR><TD COLSPAN=\"2\"><CENTER><INPUT TYPE=SUBMIT> *"
                  +"<INPUT TYPE=RESET></CENTER></TD></TR>");
      out.println("</TABLE>");
      out.println("</FORM><BR>");
      out.println("<A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowMainPage\">Main Page</A>");
      out.println("</FONT></BODY></HTML>");
      out.flush();
   }   

   // Update a person's fields/attributes
   public void _onEditPerson(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      StringBuffer errors = new StringBuffer();
      String firstName = request.getParameter("firstName");
      if(firstName == null || firstName.length() == 0)
         errors.append("Please fill out the first name field.<BR>\n");
      String lastName = request.getParameter("lastName");
      if(lastName == null || lastName.length() == 0)
         errors.append("Please fill out the last name field.<BR>\n");
      Date birthDate = null;
      try {
         String month = request.getParameter("month");
         String day = request.getParameter("day");
         String year = request.getParameter("year");
         SimpleDateFormat format = new SimpleDateFormat("M/d/yyyyy");
         birthDate = format.parse(month+"/"+day+"/"+year);
      }
      catch(Throwable th1) {
         errors.append("Please select a valid birth date. (m/d/yyyy)<BR>\n");
      }
      boolean sex = false;
      try {
         sex = new Boolean(request.getParameter("sex")).booleanValue();
      }
      catch(Throwable th) {
         errors.append("Please select a sex.<BR>\n");
      }
      String zip = request.getParameter("zip");
      if(zip == null || zip.length() != 5)
         errors.append("Zip field must be 5 digits long.<BR>\n");
      else {
         for(int i = 0; i < zip.length(); i++) {
            if(!Character.isDigit(zip.charAt(i))) {
               errors.append("Zip field must be 5 digits long.<BR>\n");
            }
         }
      }
      
      String id = request.getParameter("id");
      Person p = null;
      synchronized(_personMap) {
         if(id == null || (p = (Person)_personMap.get(id)) == null)
            errors.append("Person not found!<BR>\n");
      }
      
      if(errors.length() == 0) {
         synchronized(p) {
            p.setFirstName(firstName);
            p.setLastName(lastName);
            p.setBirthDate(birthDate);
            p.setSex(sex);
            p.setZip(zip);
         }
         _saveData();
         _sendReportPage(response, "Person updated.<BR>\n");
      }
      else
         _sendErrorPage(response, errors.toString());
   }
   
   // Display the estimated amout of life left for each person by computing his/her
   // age from the birth date field
   public void _onShowLifePage(HttpServletRequest request, HttpServletResponse response) 
   throws IOException, ServletException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<HTML><HEAD><TITLE>Project 1 -- Life</TITLE></HEAD>");
      out.println("<BODY BGCOLOR=\"WHITE\"><FONT FACE=\"Arial\">");
      out.println("<H1>Project 1 -- Life</H1>");
      String sortBy = request.getParameter("sortBy");
      if(sortBy == null) 
         sortBy = "ID";
      String order = request.getParameter("order");
      if(order == null)
         order = "ASC";
      _printSortForm("onShowLifePage", out, sortBy, order);
      out.println("<TABLE BORDER=\"1\">");
      out.println("<TR><TD><B>ID</B></TD>");
      out.println("<TD><B>First name</B></TD>");
      out.println("<TD><B>Last name</B></TD>");
      out.println("<TD><B>Birth date</B></TD>");
      out.println("<TD><B>Sex</B></TD>");
      out.println("<TD><B>Zip</B></TD>");
      out.println("<TD><B>Life Remaining (in seconds)</B></TD>");
      out.println("</TR>");
      LinkedList list = new LinkedList();
      Iterator it;
      synchronized(_personMap) {
         it = _personMap.values().iterator();
         while(it.hasNext()) {
            list.add(it.next());
         }
      }
      Collections.sort(list, PersonCompFactory.createComp(sortBy, order));
      it = list.iterator(); 
      Person p;
      long temp;
      int index = 0;
      SimpleDateFormat dateFormat = new SimpleDateFormat("M/d/yyyyy");
      while(it.hasNext()) {
         p = (Person)it.next();
         synchronized(p) {
            out.println("<TR><TD>"+p.id()+"</TD>");
            out.println("<TD>"+FormEncoder.encode(p.firstName())+"</TD>");
            out.println("<TD>"+FormEncoder.encode(p.lastName())+"</TD>");
            out.println("<TD>"+dateFormat.format(p.birthDate())+"</TD>");
            out.println("<TD>"+(p.isMale()?("Male"):("Female"))+"</TD>");
            out.println("<TD>"+FormEncoder.encode(p.zip())+"</TD>");
            // compute remaining life
            Date now = new Date();
            temp = Math.abs(now.getTime() - p.birthDate().getTime());
            temp /= 1000;
            if(p.isMale())
               temp = MAX_MALE_AGE - temp;
            else
               temp = MAX_FEMALE_AGE - temp;
            if(temp < 0)
               temp = 0;
            out.println("<TD><FORM onSubmit=\"return false;\" NAME=\"form"
                        +index+"\"><INPUT TYPE=\"TEXT\" NAME=\"lifeRemaining\" "
                        +"VALUE=\""+temp+"\"></FORM></TD>");
            out.println("<SCRIPT LANGUAGE=\"JavaScript\">");
            out.println("function countDown"+index+"() {");
            out.println("   if(parseInt(document.form"+index+".lifeRemaining.value) <= 0) {");
            out.println("      document.form"+index+".lifeRemaining.value = \"Dead :(\";");
            out.println("      return;");
            out.println("   }");
            out.println("   document.form"+index+".lifeRemaining.value = parseInt(document.form"
                        +index+".lifeRemaining.value) - 1;");
            out.println("   setTimeout('countDown"+index+"()', 1000);");
            out.println("}");
            out.println("countDown"+index+"();");
            out.println("</SCRIPT>");
         }
         index++;
         out.println("</TR>");
      }
      out.println("</TABLE><BR><BR>");
      out.println("<FONT FACE=\"Arial\" SIZE=\"2\">"
                  +"I guess my point hear is that every second is oh so important!<BR><BR>"
                  +"Matthew W. Coan<BR>"
                  +";)<BR><BR>"
                  +"</FONT>");
      out.println("<A HREF=\"/servlet/com.mwc.cosc610.project1.Project1?"
                  +"event=onShowMainPage\">Main Page</A>");
      out.println("</FONT></BODY></HTML>");
      out.flush();
   }
}