// Written by Dr. Deborah Hwang for CS 350
// Pizza Parlor Order Form Java application

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;


public class PizzaApp3 extends JPanel // application is a JPanel
{  

   // Network elements
   Socket socket;
   PrintWriter out;
   BufferedReader in;
   boolean connected = false;
   final boolean DEBUG = true;
   JTextField portField;

   // The components that are named for entire application
   JFrame frame;   // outer frame
   JTextField nameField;
   JTextArea addressField;
   JTextArea debugArea;
   JRadioButton[] sizeButtons;
   JCheckBox[] toppingsBoxes;
   final int numButtons = 4;
   final int numBoxes = 6;

   public PizzaApp3 (JFrame frame) // constructor
   {
      // initialize layout manager using superclass constructor
      // super class construction must always be first
      super (new BorderLayout());

      this.frame = frame;

      // Create the components

      // Create title label with HTML decorated text
      JLabel title = new JLabel ("<html><font size=+2 color=\"purple\">" +
                                  "Purple Pizza Parlor</font></html>", 
                                  JLabel.CENTER);
      // Pad the title on top and bottom
      title.setBorder(BorderFactory.createEmptyBorder(10,0,10,0));

      // Create other panels
      JPanel namePanel = createNamePanel();
      JPanel addressPanel = createAddressPanel();
      JPanel sizePanel = createSizePanel();
      JPanel toppingsPanel = createToppingsPanel();
      JPanel buttonPanel = createButtonPanel();

      // Create options panel with sizes and toppings
      JPanel optionsPanel = new JPanel();
      optionsPanel.add (sizePanel);
      optionsPanel.add (toppingsPanel);

      // Create port panel
      JPanel portPanel = new JPanel();
      portPanel.add (new JLabel ("Enter server port number here: "));
      portField = new JTextField (10);
      portPanel.add (portField);

      // Create center panel in a horizontal column
      JPanel centerPanel = new JPanel();
      centerPanel.setLayout (new BoxLayout (centerPanel, BoxLayout.PAGE_AXIS));
      centerPanel.add (namePanel);
      centerPanel.add (addressPanel);
      centerPanel.add (optionsPanel);
      centerPanel.add (buttonPanel);
      centerPanel.add (portPanel);


      // Create the debug area at bottom with vertical scrollbar
      debugArea = new JTextArea ("The status of your order is:\n", 10, 40);
      JScrollPane debugScrollPane = new JScrollPane (
         debugArea, 
         JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
         JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      debugArea.setEditable (false);

      // Lay out the main panel
      add (title, BorderLayout.PAGE_START);
      add (centerPanel, BorderLayout.CENTER);
      add (debugScrollPane, BorderLayout.PAGE_END);
   }  // end constructor

   private JPanel createNamePanel ()
   {
      JPanel panel = new JPanel ();
      nameField = new JTextField(20);

      panel.add (new JLabel ("Name: "));
      panel.add (nameField);
      return panel;
   }  // end createNamePanel

   private JPanel createAddressPanel ()
   {
      JPanel panel = new JPanel ();
      addressField = new JTextArea(3, 20);

      panel.add (new JLabel ("Address: "));
      panel.add (addressField);
      return panel;
   }  // end createAddressPanel

   private JPanel createSizePanel ()
   {
      JPanel panel = new JPanel(new GridLayout(0,1));

      final String[] sizes = {"Small", "Medium", "Large", "Extra Large"};
      ButtonGroup sizeButtonGroup = new ButtonGroup();  // create button group

      sizeButtons = new JRadioButton[numButtons];

      // Create an action listener
      ActionListener radioListener = 
         new ActionListener()  // anonymous inner class
         {
             public void actionPerformed (ActionEvent e)  // handler
             {
                String size = e.getActionCommand();
                debugArea.append (size + " has been chosen.\n");
                debugArea.setCaretPosition(
                   debugArea.getDocument().getLength());
             }  // end actionPerformed
         };  // end ActionListener

      for (int i = 0; i < numButtons; i++)
      {
         // Create radio button and add it to button group and panel
         sizeButtons[i] = new JRadioButton (sizes[i]);
         sizeButtonGroup.add (sizeButtons[i]);
         panel.add(sizeButtons[i]);

         // Set action command (for clicks)
         // and add the listener to each button
         sizeButtons[i].setActionCommand(sizes[i]);
         sizeButtons[i].addActionListener(radioListener);
      }  // end for

      // Make Large the default choice
      sizeButtons[2].setSelected(true);

      // Add compound border around panel
      panel.setBorder(BorderFactory.createCompoundBorder(
                         BorderFactory.createTitledBorder("Choose one:"),
                         BorderFactory.createEmptyBorder(5,5,5,5)));
      
      return panel;
   }  // end createSizePanel

   
   private JPanel createToppingsPanel()
   {
      JPanel panel = new JPanel (new GridLayout(0,1));

      final String[] toppings = {"Italian Sausage", "Hamburger", "Pepperoni",
                                 "Onions", "Green Peppers", "Mushrooms"};

      toppingsBoxes = new JCheckBox[numBoxes];

      // Create an item listener
      ItemListener boxListener = 
         new ItemListener ()  // anonymous inner class
         {
               public void itemStateChanged (ItemEvent e)  // handler
               {
                  for (int i = 0; i < numBoxes; i++)
                  {
                     Object source = e.getItemSelectable();
                     if (source == toppingsBoxes[i])
                     {
                        String text = toppings[i] + " was ";
                        if (e.getStateChange() == ItemEvent.DESELECTED)
                           text = text + "unchecked.\n";
                        else
                           text = text + "checked.\n";
                        debugArea.append (text);
                        debugArea.setCaretPosition(
                           debugArea.getDocument().getLength());
                        break;
                     }  // end if
                  }  // end for
               }  // end itemStateChanged
         }; // end ItemListener

      for (int i = 0; i < numBoxes; i++)
      {
         // Create checkbox and add it to panel
         toppingsBoxes[i] = new JCheckBox (toppings[i]);
         panel.add (toppingsBoxes[i]);
         
         // Add the listener to each box
         toppingsBoxes[i].addItemListener (boxListener);
      }  // end for

      // Add compound border around panel
      panel.setBorder(BorderFactory.createCompoundBorder(
                         BorderFactory.createTitledBorder("Choose from:"),
                         BorderFactory.createEmptyBorder(5,5,5,5)));
      
      return panel;
   }  // end createSizePanel

   private JPanel createButtonPanel()
   {
      JPanel panel = new JPanel();  // default layout is FlowLayout
      JButton submit = new JButton ("Submit");
      JButton reset = new JButton ("Reset");
      JButton quit = new JButton ("Quit");

      // Create an action listener
      ActionListener buttonListener =
         new ActionListener () // anonymous inner class
         {
             public void actionPerformed (ActionEvent e) // handler
             {
		String command = e.getActionCommand();
		int port = 0;
		if (command == "Submit")
		{
		   // error check
		   if (nameField.getText().equals(""))
		   {
		      JOptionPane.showMessageDialog(
			 frame,
			 "Name field must be filled in.",
			 "Name field missing",
			 JOptionPane.ERROR_MESSAGE);
		      return;
		   }  // if name missing
		   if (addressField.getText().equals(""))
		   {
		      JOptionPane.showMessageDialog(
			 frame,
			 "Address field must be filled in.",
			 "Address field missing",
			 JOptionPane.ERROR_MESSAGE);
		      return;
		   }  // if address missing

		   if (portField.getText().equals(""))
		   {
		      JOptionPane.showMessageDialog(
			 frame,
			 "Port field must be filled in.",
			 "Port field missing",
			 JOptionPane.ERROR_MESSAGE);
		      return;
		   }  // end port field check
             
		   if (!connected)        // We need to attempt a rendezvous.
		   {
		      if (DEBUG)
			 debugArea.append(
			    "connected = false; about to attempt a rendezvous.\n");

		      // Get the port the user entered...
		      try 
		      {
			 port = Integer.parseInt(portField.getText());
			 debugArea.append ("Connecting to port # " + port + "\n");
		      }  // end try
		      catch (NumberFormatException ex) // Non-integer entered.
		      {  
			 debugArea.append("Please enter a valid port number above\n");
			 return;
		      }  // end catch
		      
		      //...and rendezvous with it.
		      rendezvous(port);

		      if (!connected)  // rendezvous failed
		      {
			 debugArea.append ("Connecting to port # " + port + " failed\n");
			 return;
		      }  // end if
		   }  // end attempt to rendevous

		   // Send data over.
		   if (DEBUG)
		      debugArea.append("connected = true; about to send data.\n");
		   try 
		   {
		      out.println(nameField.getText());
		      out.println(addressField.getText());
		      out.println("AddrEnd");  // in case address has newlines in it
		      for (int i = 0; i < sizeButtons.length; i++)
			 if (sizeButtons[i].isSelected())
			 {
			    out.println(sizeButtons[i].getText());
			    break;  // only one can be selected
			 }  // end if
		      for (int i = 0; i < toppingsBoxes.length; i++)
			 if (toppingsBoxes[i].isSelected())
			    out.println (toppingsBoxes[i].getText());
		      out.println("TopEnd");  // To mark end of toppings list.
		      out.flush();            // Move data out of buffer
		   }  // end try
/*		   catch (IOException ex) 
		   {
		      debugArea.append("ERROR: Couldn't write to socket.\n");
		      debugArea.append("...Disconnecting.\n");
		      cleanup();
		      return;
		   } // end catch writing errors
*/
		   catch (NullPointerException ex) 
		   {
		      debugArea.append("ERROR: No output stream!\n");
		      debugArea.append("...Disconnecting.\n");
		      cleanup();
		      return;
		   }  // end catch stream error

		   // Receive response message back
		   debugArea.append ("Order sent.  Waiting for response.\n");
		   try
		   {
		      String message = in.readLine();
		      debugArea.append ("Response is:\n");
		      while (!message.equals("End"))
		      {
			 debugArea.append(message + "\n");
			 message = in.readLine();
		      }  // end reading response message lines
		   }  // end try
		   catch (IOException ex) 
		   {  
		      debugArea.append("Couldn't read from socket.\n");
		      debugArea.append("...Disconnecting.\n");
		      cleanup();
		      return;
		   }  // end catch
		   debugArea.setCaretPosition(
		      debugArea.getDocument().getLength());
		}  // end Submit
		else if (command == "Reset")
		{
		   nameField.setText ("");
		   addressField.setText ("");
		   for (int i = 0; i < numButtons; i++)
		      sizeButtons[i].setSelected(false);
		   sizeButtons[2].setSelected(true);
		   for (int i = 0; i < numBoxes; i++)
		      toppingsBoxes[i].setSelected(false);
		   debugArea.setText ("The status of your order is:\n");
		}  // end Reset
		else if (command == "Quit")
		{
		   System.exit(0);
		}  // end Quit
		else
		   System.out.println ("Unknown command");
	     }  // end actionPerformed
         };  // end ActionListener
      
      // Set action command and add listener to each button
      submit.setActionCommand("Submit");
      submit.addActionListener (buttonListener);

      reset.setActionCommand("Reset");
      reset.addActionListener (buttonListener);

      quit.setActionCommand("Quit");
      quit.addActionListener (buttonListener);

      // Set default button (when Enter is pressed)
      frame.getRootPane().setDefaultButton(submit);
      
      // Lay out panel
      panel.add (submit);
      panel.add (reset);
      panel.add (quit);

      return panel;
   }  // end createButtonPanel

   // Sets up socket connection to server
   private void rendezvous(int port) 
   {
      String host = "csserver.evansville.edu";

      // Try to open a socket to the port.
      if (DEBUG)
	 debugArea.append ("attempting to connect to " + host + ":" 
			   + port +"\n");
      try 
      {
         socket = new Socket(host, port);
      } 
      catch (UnknownHostException e) 
      {
	 debugArea.append("ERROR: Can't find host: " + host + "\n");
         cleanup();
         return;
      } 
      catch (IOException e) 
      {
         debugArea.append("ERROR: Can't open socket on rendezvous port "
                           + port + " (on host " + host + ").\n");
	 debugArea.append("Check http://csserver.evansville.edu/~hwang/f06-courses/cs350/PizzaPort.txt\n");
	 debugArea.append("for current port number or server is down\n");
         cleanup();
         return;
      }

      //Try to open streams to read and write to the socket.
      if (DEBUG)
	 debugArea.append ("attempting to set up socket streams\n");
      try 
      {
          out = new PrintWriter(socket.getOutputStream(), true);
      } 
      catch (IOException e) 
      {
         debugArea.append("ERROR: Can't open output stream.\n");
         cleanup();
         return;
      }   

      try 
      {
          in = new BufferedReader(
	          new InputStreamReader(socket.getInputStream()));
      } 
      catch (IOException e) 
      {
         debugArea.append("ERROR: Can't open input stream\n");
         cleanup();
         return;
      }   

      if ((out != null) && (in != null)) 
      {
         portField.setEditable(false);
      } 
      else 
      {
         debugArea.append("ERROR: Port connected, but I/O problems.\n");
         debugArea.append("Please TRY AGAIN.\n");
         cleanup();
         return;
      }  // end else

      connected = true;

      if (DEBUG)
	 debugArea.append("streams connected\n");
      debugArea.setCaretPosition(debugArea.getDocument().getLength());
   }  // end rendevous

   // cleans up streams
   private void cleanup()
   {
      if (DEBUG)
	 debugArea.append("cleaning up\n");
      connected = false;
      portField.setEditable(true);

      // close down all the streams
      try 
      {
         if (in != null) 
         {
            in.close();
            in = null;
         }  // end if
      }  // end try
      catch (Exception e) {} //Ignore errors.

      try
      {
         if (out != null) 
         {
            out.close();
            out = null;
         }  // end if
      }  // end try
      catch (Exception e) {} //Ignore errors.

      try
      {
         if (socket != null) 
         {
            socket.close();
            socket = null;
         }  // end if
      }  // end try
      catch (Exception e) {} //Ignore errors.
      debugArea.setCaretPosition(debugArea.getDocument().getLength());
   }  // end cleanup

    // Every Swing application will have these two functions

    // Create the GUI and show it.  For thread safety, this method should 
    // be invoked from the event-dispatching thread.
    private static void createAndShowGUI() 
    {
        // Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);
       
        // Create and set up the main window.
        JFrame frame = new JFrame("Purple Pizza Parlor Order Form");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

        // Set up the main frame panel, 
        // only the application itself is created and added
        frame.setLayout(new GridLayout(1,1));
        frame.add(new PizzaApp3 (frame));

        // Display the window.
        frame.pack();
        frame.setVisible(true);
    }  // createAndShowGUI

    public static void main(String[] args) 
    {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(
           new Runnable() // anonymous inner class
           {
                 public void run() // from Runnable interface
                 {
                    createAndShowGUI();
                 }  // end run
           });  // end new Runnable
    }  // end main
}  // end PizzaApp3
