/*
 * LunchClient
 *
 * The client side consumer of the LunchCoordinator service. The
 * client obtains all proxies which implement LunchCoordinator from
 * the lookup service and populates a combobox with the set of
 * supported protocols
 *
 * Authors: Steve Fritzinger and Brian Jeltema
 */

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 net.jini.core.lease.* ;
import com.sun.jini.lease.* ;
import java.io.* ;
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 {

    // The RemoteEventListener class used to receive service callbacks

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

        public void notify(RemoteEvent e) throws UnknownEventException, 
                                                 RemoteException {
            try {
		final Hashtable ht = (Hashtable)e.getRegistrationObject().get() ;
		SwingUtilities.invokeLater(new Runnable() {
		    public void run() {
			fillTable(ht) ;
		    }
		}) ;
	    }
	    catch (Exception ex) {
		System.out.println("notify exception: " + ex) ;
	    }
        }
    }

    // extend JRadioButton to accept a tooltip in its constructor

    class TTRadioButton extends JRadioButton {
	public TTRadioButton(String label,String tip) {
	    super(label) ;
	    setToolTipText(tip) ;
            getModel().setActionCommand(label) ; // set model's command string
	}
    }

    // 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 ;
	}
    }

    // Lunch client class atrributes

    LunchCoordinator myServerInterface;
    ServiceRegistrar registrar ;

    JTextField nameTextField          = new JTextField ();
    JScrollPane preferenceScrollPane  = new JScrollPane ();
    DefaultTableModel tableModel      = new DefaultTableModel();
    JTable preferenceTable            = new JTable (tableModel );
    JComboBox protocolChoice          = new JComboBox() ;
    ButtonGroup buttonGroup           = new ButtonGroup();
    JButton setPreferenceButton       = new JButton ("Set Preference");
    JButton changePreferenceButton    = new JButton ("Change Preference");
    JButton viewButton ;  // here so that RemoteEventHandler can post event
    LeaseRenewalManager leaseManager ;

    public LunchClient() {

        // Construct GUI for Lunch Client

        setLayout (new GridLayout (1, 2));  // contains two adjacent panels

	// Build the left side of the GUI

        GridBagJPanel leftPanel = new GridBagJPanel ();
        leftPanel.setBorder (new TitledBorder ("Your Preference:"));

	// User name components

        leftPanel.add (new JLabel("Name:"), "f=both a=e in=0,10,0,10");
        leftPanel.add (nameTextField, "w=0 f=both");

	// the food selection radio button panel

        leftPanel.add (new JLabel("Food:"), "a=nw in=10,10,0,10");

        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 the button group

	for (int i=0 ; i<radioButtons.length ; i++) {
	    JRadioButton rb = radioButtons[i] ;
	    radioButtonPanel.add(rb) ;
	    buttonGroup.add(rb) ;
	}
        leftPanel.add (radioButtonPanel, "w=0 in=10,10,10,10");

	// build the set/change button panel

        GridBagJPanel leftButtonPanel = new GridBagJPanel();
        leftButtonPanel.setLayout(new GridLayout(1,2,10,0));
        leftPanel.add (leftButtonPanel, "w=0 in=10,10,10,10");

        leftButtonPanel.add (setPreferenceButton);
        leftButtonPanel.add (changePreferenceButton);

	// build the protocol choice comboBox

	leftPanel.add(new JLabel("Protocol"),"w=0 in=10,10,0,10") ;
	leftPanel.add(protocolChoice,"w=0 in=10,10,0,10") ;
                
        GridBagJPanel rightPanel = new GridBagJPanel ();
        rightPanel.setBorder (new TitledBorder("Other Lunchers"));

        preferenceScrollPane .setViewportView (preferenceTable);
        rightPanel.add (preferenceScrollPane , "w=0 f=both wx=1 wy=1 in=5,10,0,10");

        JPanel rightButtonPanel = new JPanel();
        rightButtonPanel.setLayout(new GridLayout(1,3,10,0));
        rightPanel.add(rightButtonPanel, "w=0 in=10,10,10,10");
        
        viewButton = new JButton ("View");
        JButton listButton = new JButton ("List Server");
        JButton clearButton = new JButton ("Clear Server");

        rightButtonPanel.add (viewButton);
        rightButtonPanel.add (clearButton);
        rightButtonPanel.add (listButton);

        add (leftPanel);
        add (rightPanel);

	// wire event handlers

	ActionListener l = new PreferenceHandler() ;
        setPreferenceButton.addActionListener(l) ;
        changePreferenceButton.addActionListener(l) ;
        listButton.addActionListener(new ListHandler()) ;
        viewButton.addActionListener(new ViewHandler()) ;
        clearButton.addActionListener(new ClearHandler()) ;
        protocolChoice.addItemListener(new ProtocolHandler()) ;

	// set component attributes

	protocolChoice.setRenderer(new ProtocolRenderer()) ;
        tableModel .addColumn("Lunchers");
        tableModel .addColumn("Preference");

        invalidate();
    }

    protected class PreferenceHandler implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    if (myServerInterface != null) {
		try {
		    ButtonModel m = buttonGroup.getSelection() ;
		    String name = nameTextField.getText() ;
		    if (m == null || name.trim().length() == 0) return ;
		    String food = m.getActionCommand() ;
		    if (e.getSource() == setPreferenceButton)
		        myServerInterface.registerPref(name,food);
		    else
			myServerInterface.changePref(name,food) ;
		} 
		catch (Exception exp) {
		    exp.printStackTrace();
		}
	    }
        }
    }

    protected class ViewHandler implements ActionListener {
	public void actionPerformed(ActionEvent e) {
            try {
                if (myServerInterface != null) 
                    fillTable(myServerInterface.getLunchers());
                }
            catch (Exception e2) {
                System.out.println("ViewHandler exception: " + e2) ;
            }
	}
    }

    protected class ClearHandler implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    if (myServerInterface != null) {
		try {
		    myServerInterface.clearServer();
		    clearTable();
		} catch (Exception exp) {
		    exp.printStackTrace();
		}
	    }
	}
    }

    protected class ListHandler implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    if (myServerInterface != null) {
		try {
		    myServerInterface.dump();
		} catch (Exception exp) {
		    exp.printStackTrace();
		}
	    }
	}
    }

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

    protected class ProtocolHandler implements ItemListener {
	public void itemStateChanged(ItemEvent e) {
	    ServiceItem item = (ServiceItem)protocolChoice.getSelectedItem() ;
	    myServerInterface = (LunchCoordinator)item.service ;
	}
    }

    // 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.

            if (myServerInterface != null)
                fillTable(myServerInterface.getLunchers());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class LunchLeaseListener implements LeaseListener {
        public void notify(LeaseRenewalEvent e) {
	    System.out.println("Lease expiration or denial detected") ;
	}
    }

    
    // 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 ;
		EventRegistration reg = myServerInterface.registerListener((RemoteEventListener)new EventHandler(),Lease.FOREVER) ;
                leaseManager = new LeaseRenewalManager(reg.getLease(),Lease.FOREVER,new LunchLeaseListener()) ;
	    }
        }
	catch (Exception e) {
	    System.out.println("initProtocolChoice exception: " + e) ;
	}
    }

    private void fillTable(Hashtable lunchers) {

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

                // Use list of lunchers to populate table model alphabetically
                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);
                    for (int i=0 ; i<=tableModel.getRowCount() ; i++) {
                        if (i == tableModel.getRowCount()) {
                            tableModel.addRow(row);
                            break ;
                        }
                        String s = (String)tableModel.getValueAt(i,0) ;
                        if (name.compareTo(s) <= 0) {
                            tableModel.insertRow(i,row);
                            break ;
                        }
                    }
                }

                // Repaint the table (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();
    }
}
