import net.jini.entry.*;
import net.jini.core.entry.*;
import net.jini.lookup.*; 
import net.jini.core.lookup.*; 
import net.jini.lookup.entry.*; 
import net.jini.discovery.*;    
import net.jini.core.discovery.*;    
import net.jini.core.event.* ;
import java.rmi.*;
import java.rmi.server.* ;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

class LunchClient extends JPanel {

    class EventHandler extends UnicastRemoteObject implements RemoteEventListener {
        public EventHandler() throws RemoteException {
            super() ;
        }

        public void notify(RemoteEvent e) throws UnknownEventException, RemoteException {
            fillTable() ;
        }
    }

    // extend JRadioButton to accept a tooltip in its constructor

    class TTRadioButton extends JRadioButton {
	public TTRadioButton(String label,String tip) {
	    super(label) ;
	    setToolTipText(tip) ;
	}
    }

    ServiceRegistrar registrar = null;
    ButtonGroup bg;
    String food;
    JTextField nameTextField;
    JTable preferenceTable;
    JScrollPane preferenceScrollPane ;
    LunchCoordinator myServerInterface;
    DefaultTableModel tableModel ;
    JComboBox protocolChoice = new JComboBox() ;

    // an inner class which implements a custom renderer for the protocol
    // choice. It locates the Protocol entry for the ServiceItem passed
    // as 'value' and sets the text. The selected choice is also colored red

    class ProtocolRenderer extends JLabel implements ListCellRenderer {
        public Component getListCellRendererComponent(JList list,
                                                      Object value,
						      int index,
						      boolean isSelected,
						      boolean cellHasFocus) {
            if (value == null) {
		setText("Initializing!") ;
		return this ;
            }
            ServiceItem item = (ServiceItem)value ;
	    Entry[] attribs = item.attributeSets ;
	    String protocolString = "" ;
	    for (int i=attribs.length ; --i>=0 ; ) {
		if (attribs[i] instanceof Protocol)
		   protocolString = ((Protocol)attribs[i]).protocol ;
	    }
	    setForeground(isSelected?Color.red:Color.black) ;
	    setText(protocolString) ;
	    return this ;
	}
    }

    public LunchClient() {

        // Construct GUI for Lunch Client
        setLayout (new GridLayout (1, 2));

	// Build the left side of the GUI

        JPanel leftPanel = new JPanel ();
        leftPanel.setBorder (new TitledBorder ("Your Preference:"));
        leftPanel.setLayout (new GridBagLayout ());
        GridBagConstraints gridBagConstraints1;

	// User name components

        gridBagConstraints1 = new GridBagConstraints ();
        gridBagConstraints1.fill = GridBagConstraints.BOTH;
        gridBagConstraints1.anchor = GridBagConstraints.EAST;
        gridBagConstraints1.insets = new Insets(0, 10, 0, 10);
        leftPanel.add (new JLabel("Name:"), gridBagConstraints1);

        nameTextField = new JTextField ();
        gridBagConstraints1 = new GridBagConstraints ();
        gridBagConstraints1.gridwidth = 0;
        gridBagConstraints1.fill = GridBagConstraints.BOTH;
        leftPanel.add (nameTextField, gridBagConstraints1);

	// the food selection components and radio button panel

        gridBagConstraints1 = new GridBagConstraints ();
        gridBagConstraints1.anchor = GridBagConstraints.NORTHWEST;
        gridBagConstraints1.insets = new Insets(10, 10, 0, 10);
        leftPanel.add (new JLabel("Food:"), gridBagConstraints1);

        JPanel radioButtonPanel = new JPanel ();
        radioButtonPanel.setLayout (new GridLayout (3, 3));

	JRadioButton[] radioButtons = {
	    new TTRadioButton("Garlic Pizza","The Choice of All Good SE's"), 
	    new TTRadioButton("Chinese","The use garlic in Chinese food."),
	    new TTRadioButton("Garlic Pizza","Food of the Gods"),
	    new TTRadioButton("Sea Food","Garlic goes with fish"),
	    new TTRadioButton("Garlic Pizza","Why would you want anything else?"),
	    new TTRadioButton("Mexican","Hot and spicy, with a hint of garlic."),
	    new TTRadioButton("Garlic Pizza","It's good to stink"),
	    new TTRadioButton("Subs","Have you tried fresh, sliced garlic on a sub?"),
	    new TTRadioButton("Garlic Pizza","Repetitive, isn't it?")
	} ;

	// build the radiobutton panel and wire the event handlers

        bg = new ButtonGroup();
        ActionListener listen = new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                food = e.getActionCommand();
            }
        };

	for (int i=radioButtons.length ; --i>=0 ; ) {
	    JRadioButton rb = radioButtons[i] ;
	    radioButtonPanel.add(rb) ;
	    bg.add(rb) ;
	    rb.addActionListener(listen) ;
	}

        gridBagConstraints1 = new GridBagConstraints ();
        gridBagConstraints1.gridwidth = 0;
        gridBagConstraints1.insets = new Insets(10, 10, 10, 10);
        leftPanel.add (radioButtonPanel, gridBagConstraints1);

	// build the set/change button panel

        JPanel leftButtonPanel = new JPanel();
        leftButtonPanel.setLayout(new FlowLayout());
        gridBagConstraints1 = new GridBagConstraints ();
        gridBagConstraints1.gridwidth = 0;
        gridBagConstraints1.insets = new Insets(10, 10, 10, 10);
        leftPanel.add (leftButtonPanel, gridBagConstraints1);

        JButton setPreferenceButton = new JButton ("Set Preference");
        setPreferenceButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (myServerInterface != null) {
                    try {
                        myServerInterface.registerPref(nameTextField.getText(), food);
                    } catch (Exception exp) {
                        exp.printStackTrace();
                    }
                }
            }});
        leftButtonPanel.add (setPreferenceButton);

        JButton changePreferenceButton = new JButton ("Change Preference");
        changePreferenceButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (myServerInterface != null) {
                    try {
                        myServerInterface.changePref(nameTextField.getText(), food);
                    } catch (Exception exp) {
                        exp.printStackTrace();
                    }
                }
            }});
        leftButtonPanel.add (changePreferenceButton);

	// build the protocol choice comboBox

        gridBagConstraints1.insets = new Insets(10, 10, 0, 10);
	leftPanel.add(new JLabel("Protocol"),gridBagConstraints1) ;
        gridBagConstraints1.insets = new Insets(0, 10, 10, 10);
	leftPanel.add(protocolChoice,gridBagConstraints1) ;
	protocolChoice.setRenderer(new ProtocolRenderer()) ;
        protocolChoice.addItemListener(new ItemListener() {
	    public void itemStateChanged(ItemEvent e) {
                ServiceItem item = (ServiceItem)protocolChoice.getSelectedItem() ;
		myServerInterface = (LunchCoordinator)item.service ;
	    }
	}) ;
                
        add (leftPanel);

        JPanel rightPanel = new JPanel ();
        rightPanel.setBorder (new TitledBorder("Other Lunchers"));
        rightPanel.setLayout (new GridBagLayout ());
        GridBagConstraints gridBagConstraints2;

        preferenceScrollPane  = new JScrollPane ();

        tableModel  = new DefaultTableModel();
        tableModel .addColumn("Lunchers");
        tableModel .addColumn("Preference");

        preferenceTable = new JTable (tableModel );

        preferenceScrollPane .setViewportView (preferenceTable);
        gridBagConstraints2 = new GridBagConstraints ();
        gridBagConstraints2.gridwidth = 0;
        gridBagConstraints2.fill = GridBagConstraints.BOTH ;
        gridBagConstraints2.weightx = 1. ;
        gridBagConstraints2.weighty = 1. ;
        gridBagConstraints2.insets = new Insets(5, 10, 0, 10);
        rightPanel.add (preferenceScrollPane , gridBagConstraints2);

        JPanel rightButtonPanel = new JPanel();
        rightButtonPanel.setLayout(new FlowLayout());
        gridBagConstraints2 = new GridBagConstraints ();
        gridBagConstraints2.gridwidth = 0;
        gridBagConstraints2.insets = new Insets(10, 10, 10, 10);
        rightPanel.add(rightButtonPanel, gridBagConstraints2);
        
        JButton viewButton = new JButton ("View");
        viewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                fillTable();
            }});
        gridBagConstraints2 = new GridBagConstraints ();
        gridBagConstraints2.insets = new Insets(0, 10, 0, 10);
        rightButtonPanel.add (viewButton, gridBagConstraints2);

        JButton clearButton = new JButton ("Clear Server");
        clearButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (myServerInterface != null) {
                    try {
                        myServerInterface.clearServer();
                        clearTable();
                    } catch (Exception exp) {
                        exp.printStackTrace();
                    }
                }
            }});
        rightButtonPanel.add (clearButton, gridBagConstraints2);

        JButton listButton = new JButton ("List Server");
        listButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (myServerInterface != null) {
                    try {
                        myServerInterface.dump();
                    } catch (Exception exp) {
                        exp.printStackTrace();
                    }
                }
            }});
        rightButtonPanel.add (listButton, gridBagConstraints2);

        add (rightPanel);
        invalidate();
    }

    public void clearTable() {
      for (int i = tableModel .getRowCount(); i > 0; i--)
          tableModel .removeRow(i - 1);
    }

    // here to find the lookup service and obtain the LunchCoordinator proxies

    public void init () {
        LookupDiscovery discovery;
        Entry[] aeAttributes;
        ServiceTemplate template;

        try {
	    // Set an RMI security manager to control what downloaded
            // code can do.

            System.setSecurityManager (new RMISecurityManager ());

            // Create a LookupDiscovery helper to find lookup services.
            // Our LookupListener class will listen to the LookupDiscovery
            // helper and take appropriate action when something happens.

            discovery = new LookupDiscovery(null);
            discovery.addDiscoveryListener(new LookupListener());

            // Wait until the LookupDiscovery helper finds a lookup.

            System.out.println("looking for a lookup service") ;
            while (registrar == null){
                try {
                    Thread.sleep(500);
                } 
                catch (Exception e) {
                }
            }
            System.out.println("Got a lookup service") ;

            template = new ServiceTemplate (null, new Class[]{LunchCoordinator.class},null);

            // lookup all services which implement LunchCoordinator
            // and fill in the protocolChoice component. The initial
	    // proxy is also set in initProtocolChoice.

            ServiceMatches sm = registrar.lookup (template,10);
            ServiceItem[] items = sm.items ;
	    initProtocolChoice(items) ;

            System.out.println("myserverInterface = " + myServerInterface) ;

            // Calling fillTable polls the server for currently registered
            // lunchers.  This will let us see everyone who has already
            // expressed a preference.

            fillTable();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // initialize the protocolChoice component and select the
    // first entry as the active proxy. The entire ServiceItem
    // is passed to the JComboBox component, and a customer renderer
    // responsible for updating the display

    protected void initProtocolChoice(ServiceItem[] items) {
        try {
	    if (items.length > 0) {
		for (int i = 0 ; i < items.length ; i++) {
		    protocolChoice.addItem(items[i]) ;
		}
		protocolChoice.setSelectedIndex(0) ;
		myServerInterface = (LunchCoordinator)items[0].service ;
		myServerInterface.registerListener((RemoteEventListener)new EventHandler()) ;
	    }
        }
	catch (Exception e) {
	    System.out.println("initProtocolChoice exception: " + e) ;
	}
    }

    private void fillTable() {

        // Can't do anything else we have a valid reference to
        // our service.
        if (myServerInterface != null) {
            try {
                clearTable();

                // Ask the remote service for list of registered lunchers.

                Hashtable lunchers = myServerInterface.getLunchers();

                // Use list of lunchers to populate table model
                String name;
                String [] row = new String[2];
                Enumeration enum = lunchers.keys();
                while (enum.hasMoreElements()){
                    name = (String) enum.nextElement();
                    row[0] = name;
                    row[1] = (String) lunchers.get(name);
                    tableModel .addRow(row);
                }

                // Repaint the table (remember the table is actually contained
                // in the scroll pane, in case dozens of people register
                RepaintManager.currentManager(preferenceScrollPane )
                                         .markCompletelyDirty(preferenceScrollPane) ;
            } catch (Exception exp) {
                exp.printStackTrace();
            }
        }
    }

    // This is the event handler called by the LookupDiscovery object
    // when a lookup service is detected. Just save the Registrar

    public class LookupListener implements DiscoveryListener {

        public void discovered(DiscoveryEvent e) {
            // We only bother to keep track of 1 lookup servivce.
            // Could handle many lookups if we cared to.
            ServiceRegistrar[] registrars = e.getRegistrars();
            registrar = registrars[0];
        }

        public void discarded(DiscoveryEvent e) {
        }
    }

    // the application main method. Build the window and initialize

    public static void main (String[] args) {
        // Create a new frame to contain our GUI
	JFrame frame = new JFrame("Let's Do Lunch.");

        // Add a listener to kill client when window is closed
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {System.exit(0);}
        });

        // Instantiate GUI client, add it to frame and put it on screen
        LunchClient myClient = new LunchClient();
        frame.getContentPane().add(myClient);
        frame.pack();
        frame.setVisible(true);

        // Call GUI client's init method.  This is where all the real
        // work of finding the lookup service and the server is done.

        myClient.init();
    }
}
