package apps.pint;


import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

import java.util.*;
import cnrg.itx.ds.*;
	
	
/**
 * The user interface for the pint ITX client application. The UI consists of four items:
 * <bl><li>Text entry box</li><li>Status light</li><li>Status line</li><li></bl>Conference participants pane</li>
 * Numbers can be dialed by typing them into the text entry box and pressing the return key. 
 * The user can hang up by pressing esc. When pint is connected to a conference, the conference pane appears.
 * This pane shows the names of the participants in the conference. After any operation completes, the 
 * status line is updated to reflect the status of the application.
 */
public class PintUI extends WindowAdapter implements  ActionListener, KeyListener{ 
	
	//The following fields are gathered using a pintLogin dialog
	/** the uid of the user currently logged in	 */
	private String uid = "";
	
	/** the password of the user currently logged in	 */
	private String pass = "";
	
	/** should the application use directory services?
	 */
	private boolean useDS = false;
	
	/** is the status line showing?	 */
	private boolean showStatus = true;
	
	/** is the participant pane showing?	 */
	private boolean showParticipants = false;
	
	/** the dialog used to get identity of user	 */
	private PintLogin loginDialog = null;
	
	/**the text entry field	 */
	private JAutoTextField field = null;
	
	/** the ui's status line	 */
	private JTextField statusLine = null;
	
	/** the main frame of the UI	 */
	private JFrame frame = null;
	
	/** the instance of pint -- responsible for all ITX functionality	 */
	private pint thePint = null;
	
	/** the status traffic light to the right of the text entry field	 */
	private JPanel statusBox = null;
	
	/** the list of participants	 */
	private JList partList = null;
	
	/** the vector of participant name strings for display in the conference participant view	 */
	private Vector screenParticipants = null;
	
	/** the scrolling pane that holds the partList participant list	 */
	private JScrollPane partScroll = null;
	
	/** the pane that holds the partScroll participant scrolling list	 */
	private JPanel partPane = null;
	
	/** the text line for displaying conference topic	 */
	private JTextField topicField = null; 
	
	/** the code to update the participant list within the UI's thread	 */
	private Runnable participantUpdate = null;
	
	/** the code to update the status line within the UI's thread	 */
	private Runnable toggleStatus = null;
	
	/** the code to toggle the participant list within the UI's thread	 */
	private Runnable toggleParticipants = null;
	
	/** the constructor for the UI. Presents the login dialog which in turn invokes buildMainUI()	 */
	public PintUI() {	
		
		//Uncomment the following for system look and feel
		/*
		try {
		    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) { }
		*/
		
		//build the login dialog
		loginDialog = new PintLogin(this);
		//present the login dialog
		loginDialog.present();
		
	}
	
	/**Builds the UI, displaying all but the conference participant pane.
	 */
	private void buildMainUI() {
		
		uid = (uid.equals(""))?"Pint user":uid;
		
		//create a new ITX client object
		thePint = new pint(this, uid, pass, useDS);
		
		//set title to the uid of the current user
		String title = uid;
		
		//create the main frame
		frame = new JFrame(title);
		
		//locate the main frame away from the corner of the screen
		frame.setLocation(500,300);
		
		//frame.setResizable(false);
		
		//Create the text entry field
		field = new JAutoTextField();
		if (useDS) {
			try {
				DirectoryService ds = new DirectoryService();
				ds.setCleanUp(false);
				ds.declareIdentity(new UserID(uid),new Password(pass));
				field.populate(ds.dumpAllUsers());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		//register text field to understand <ret>, <esc>, <f1> and <f2>
		field.addKeyListener(this);
		
		//create the pane to hold the text entry field
		JPanel callPane = new JPanel();
		
		// set the width of the field to accomodate a worst case guess of a long uid
		FontMetrics fontMet = field.getFontMetrics(field.getFont());
		field.setPreferredSize(
							   new Dimension(
											 fontMet.stringWidth("skeshav9999@longserver21.cornell.edu"),
											 field.getPreferredSize().height
											 ));
		
		//set up the status light
		final pint cli = thePint;
		statusBox = new JPanel() {
			//paint the light dependent on the state of the ITX client object
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				int state = cli.getState();
				switch(state) {
				case pint.CONNECTED:	
					g.setColor(Color.green);
					break;
				case pint.DIALING:    
					g.setColor(Color.orange);
					break;
				case pint.IDLE:
					g.setColor(Color.red);
					break;
				}
				g.fillOval(0,0,16,16);
				//draw a black circle around the light
				g.setColor(Color.black);
				g.drawOval(0,0,15,15);
				//NOTE: these sizes are correct for jview, but circle drawing appears to be 
				//off-by-one in the sun implementation.
			}
			};
		statusBox.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
								int state = cli.getState();
				switch(state) {
				case pint.CONNECTED:	
					cli.abort();
					break;
				case pint.DIALING:    
					cli.abort();
					break;
				case pint.IDLE:
					Thread th = new Thread() {
						public void run() {
						thePint.call(field.getText());
						}
						};
					th.start();
					
					break;
				}
			}
			});
		
		//size the status light pane to fit the status light
		Dimension d = new Dimension(16,16);
		statusBox.setPreferredSize(d);
		statusBox.setMinimumSize(d);
		statusBox.setMaximumSize(d);
		
		//Put the text entry field and the status light into the callPane
		callPane.add(field);
		callPane.add(statusBox);
		
		//Build the status line
		statusLine = new JTextField("                  ");
		statusLine.setEditable(false);
		statusLine.setFont(new Font("dialog",0,10)); //10 pt. plain
		
		
		
		
			//Build topic display
			topicField = new JTextField();
			topicField.setEditable(false);
			//Build the participant list
			screenParticipants = new Vector();
			partList = new JList(screenParticipants);
			partList.setPrototypeCellValue("jdb30@cornell.edu        \tJames Barabas");        
			partScroll = new JScrollPane(partList,
										 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
										 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
			

			
			topicField.setPreferredSize(new Dimension(partScroll.getPreferredSize().width-10,
													  topicField.getPreferredSize().height));
			//build the participant pane
			partPane = new JPanel();
			partPane.add(topicField);
			partPane.add(Box.createRigidArea(new Dimension(5,5)));
			partPane.add(partScroll);
			partPane.setBorder(BorderFactory.createTitledBorder(
				BorderFactory.createEtchedBorder(Color.white,Color.black),
				"Conference"));
			partPane.setLayout(new BoxLayout(partPane,BoxLayout.Y_AXIS));
		
		
		

		//register the window to accept close and key events
		frame.addWindowListener(this);
		frame.addKeyListener(this);
		
		
		//build main pane
		Container mainFrame = frame.getContentPane();
		mainFrame.add(callPane);
		
		if (showParticipants){
			mainFrame.add(partPane);
		}
		
		if (showStatus) {
			mainFrame.add(statusLine);
		}
		
		//set layout for vertical alignment
		mainFrame.setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.Y_AXIS));
		

		//display main pane
		frame.pack();
		frame.toFront();
		frame.setVisible(true);
		this.dirtyStatus(); //repaint status bar with appropriate message from initialization
		
		
		/* ****** Build the runnable objects needed for ui update	 */
		
		//The runnable to handle updating the participant pane and status line
		participantUpdate = new Runnable() {
			public void run() {
				statusLine.setText(thePint.getStatus()); //update the status line
				
				screenParticipants.removeAllElements();  //clean out participant list.
				if (thePint.getState() == pint.CONNECTED) { //refil the participant list if we are connected to someone
					Enumeration e = thePint.getParticipants().elements();
					topicField.setText((String)e.nextElement());
						
					for (;e.hasMoreElements();) {
						screenParticipants.addElement(((String)e.nextElement()));
					}
					//screenParticipants should be empty if we are not in a conference
					
					//turn on the participant pane if we are in a conference
					
					if (!screenParticipants.isEmpty()) {
						showParticipants = false;//set to the opposite of what we want then toggle.
						toggleParticipants.run();
					}

					
				} else {
					//no connection: we don't want to show the participant pane.
					showParticipants = true; 
					//set to the opposite of what we want then toggle.
					toggleParticipants.run();
				}
				
			}
		};
		
		//Runnable to turn the status line on or off
		toggleStatus = new Runnable() {
			public void run() {
				Container mainFrame2 = frame.getContentPane();
				showStatus = !showStatus;
				//try to remove status line
				mainFrame2.remove(statusLine);
				
				//then put it back if we want a status line
				if (showStatus) {
					mainFrame2.add(statusLine);
				}
				frame.pack();
				
			
			}
		};
		
		//Runnable to toggle display of the participant pane
		toggleParticipants = new Runnable() {
			public void run() {
				Container mainFrame3 = frame.getContentPane();
				showParticipants = !showParticipants;
				mainFrame3.remove(partPane);
				
				//try to remove participant pane
				mainFrame3.remove(statusLine);
				//put it back if we are showing participants.
				if (showParticipants) {
					mainFrame3.add(partPane);
				}
				if (showStatus)
					mainFrame3.add(statusLine);
				
				frame.pack();
				
			}
		};		
		
	}
	
	/** Event listener for button clicks. Handles events from the main window as well as the login dialog.
	 */
	public void actionPerformed(ActionEvent ae) {
		String action = ae.getActionCommand();
		
		//Login
		if (ae.getSource().equals(loginDialog.getLoginButton())) {
			loginDialog.sendLogin(); //get dialog to send us login info
			
			//if we are using directory services, check credentials
			if (this.useDS) {
				try {
					//test out uid and password to make sure they are valid
					DirectoryService ds = new DirectoryService();
					ds.setCleanUp(false);
					ds.declareIdentity(new UserID(this.uid),new Password(this.pass));
				} catch (Exception e) {
					//if invalid uid/pass, flash dialog and clear password field.
					System.out.println(e.getMessage());
					loginDialog.fail();
					return;
				}
			}
			//if login info is ok, display ui
			loginDialog.setVisible(false);
			this.buildMainUI();
		}
	}

	/**
	 * method that allows the login dialog to send login info to the ui
	 * @param uid User ID to register with ui
	 * @param password to register with ui
	 * @param useDS should the app use directory services?
	 */
	protected void login(String uid, String pass, boolean useDS) {
		this.uid = uid;
		this.pass = pass;
		this.useDS = useDS;
	}
	
	/* **************KeyListener interface***************************/
	public void keyTyped(KeyEvent ke) {}

	public void keyPressed(KeyEvent ke) {}

	public void keyReleased(KeyEvent ke) {
		int pressed = ke.getKeyCode();
			
		if (pressed == KeyEvent.VK_ENTER) { 
			final String text = field.getText();
			//System.out.println("Starting thread to Call "+text);
			Thread th = new Thread() {
				public void run() {
				thePint.call(text);
				}
				};
			th.start();
		} else if (pressed == KeyEvent.VK_ESCAPE) {
			//System.out.println("Spawning close thread");
			Thread th = new Thread() {
				public void run() {
				thePint.abort();
				}
				};
			th.start();
			
		}else if (pressed == KeyEvent.VK_F1) {
			SwingUtilities.invokeLater(toggleStatus);
		}else if (pressed == KeyEvent.VK_F2) {
			SwingUtilities.invokeLater(toggleParticipants);
		}
		
		
		
	}
	
	/* ***********************WindowListener interface*****************************/
	public void windowClosed(WindowEvent we) {
		thePint.close();
		System.exit(1);
	}
	
	public void windowClosing(WindowEvent we) {
		thePint.close();
		System.exit(1);
		
	}
	
	
	

	
	/**
	 * Call this method to force the ui to update. Can be called from any thread: uses invoke later
	 * to ensure thread safety in UI
	 */
	public void dirtyStatus() {
		//System.out.println("updating UI");
		try {
			//Update contents of status line and participant pane
			SwingUtilities.invokeLater(participantUpdate);
			
			//redraw the participant participant scroller
			partScroll.revalidate();
			partScroll.repaint();
			
			//redraw the status light
			statusBox.repaint();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
	

