/*****************************************************************************
 *                                                                           *
 *         Licensed Materials - Property of IBM                              *
 *                                                                           *
 *         "Restricted Materials of IBM"                                     *
 *                                                                           *
 *         5799-QRY                                                          *
 *                                                                           *
 *         (C) Copyright IBM Corp. 1998                                      *
 *                                                                           *
 *****************************************************************************/
package com.ibm.devtools.SIPNoTE;

import java.util.*;
import java.io.*;
import java.net.*;
import java.awt.event.*;
import com.sun.java.swing.*;

/**
 * This class represents the GUI which is presented to the user to
 * monitor logged on users and their locations, and as a front end
 * for originating messages. <p>
 *
 * The GUI presented is a listbox which contains instance names and
 * currently logged on user names.  The items which may be included
 * in the list are read in from the .snwatch file, which contains
 * id & alias pairs.  The alias, if present, will be displayed instead
 * of the actual id.  The user can select one or more entries in the
 * listbox, and then either double click, or press ctrl-s to bring
 * up a window in which to type their message.  If this window is
 * closed, then the SNClient will exit.<p>
 *
 * This class utilizes Swing version 1.0 for the GUI components.
 *
 * @author Bill Nagy
 * @see JSendWnd
 * @see SNClient
 */
public class SNWatch
{
  Hashtable watchtable;
  Vector displaylist;
  Vector aliaslist;
  Vector jwatchentries;
  int numpeople = 0;
  int localport;
  JFrame wndFrame;
  JList namelist;
  SNWatchKeyListener llistener;
  SNWatchMouseListener mlistener;
  SNWatchWndListener wlistener;

  /**
   * Create a new SNWatch window, and intialize its contents.
   * 
   * @exception Exception Initialization failed. 
   */
  public SNWatch()
       throws Exception
  {
    BufferedReader br;
    String inputstr;
    String hashentry, hashval;
    Properties props = new Properties();
    FileInputStream fin;

    try
      {
	fin = new FileInputStream(".sipnote");
	props.load(fin);
	fin.close();
      }
    catch (FileNotFoundException e)
      {
      }
    catch (IOException e)
      {
	System.err.println("I/O Error loading .sipnote file --> "+e);
      }

    localport = (new Integer(props.getProperty("SNClient.jport",
					       "5062")).intValue());
    

    System.out.println("Parsing .snwatch file.\n");
    /*Read in the file and create the window*/
    try
      {
	br = new BufferedReader(new FileReader(".snwatch"));
      }
    catch (FileNotFoundException e)
      {
	e.printStackTrace(System.err);
	//really want to signify failure
	throw new Exception();
      }

    watchtable = new Hashtable();
    displaylist = new Vector();
    aliaslist = new Vector();
    jwatchentries = new Vector();

    while (true)
      {
	try
	  {
	    if ((inputstr = br.readLine()) == null)
	      break;

	    if (inputstr.length() != 0)
	      {
		/*Ignore #*/
		if ((inputstr.trim()).startsWith("#"))
		  continue;

		StringTokenizer st = new StringTokenizer(inputstr);

		try
		  {
		    hashentry = st.nextToken(":");
		    if (!hashentry.startsWith(";"))
		      numpeople++;
		  }
		catch (NoSuchElementException e)
		  {
		    System.err.println("Error processing .snwatch file: "+inputstr);
		    continue;
		  }
		try
		  {
		    hashval = st.nextToken();
		    watchtable.put(hashentry, hashval);
		    jwatchentries.addElement(hashentry);

		    if (DebugDefine.DEBUG)
		      System.out.println("Watching "+hashentry+" with alias "+hashval);
		  }
		catch (NoSuchElementException e)
		  {
		    try
		      {
			watchtable.put(hashentry, "");
			jwatchentries.addElement(hashentry);

		    if (DebugDefine.DEBUG)
		      System.out.println("Watching "+hashentry);

		      }
		    catch (NullPointerException e1)
		      {
			e1.printStackTrace(System.err);
			continue;
		      }
		  }
		catch (NullPointerException e)
		  {
		    e.printStackTrace(System.err);
		    continue;
		  }
	      }
	  }
	catch (IOException e)
	  {
	    e.printStackTrace(System.err);
	    br.close();
	    //really want to signify failure
	    throw new Exception();
	  }
      }
    br.close();

    wndFrame = new JFrame("SNWatch");
    namelist = new JList(displaylist);
    llistener = new SNWatchKeyListener(this);
    mlistener = new SNWatchMouseListener(this);
    wlistener = new SNWatchWndListener(this);
    namelist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    wndFrame.getContentPane().add(namelist);
    wndFrame.pack();
    wndFrame.show();
    wndFrame.toFront();
  }
  
  
  /**
   * Remove a user from the displayed list.
   *
   * @param alias The user id to remove from the list.  (This is not the displayed alias.)
   */
  public void removeAlias(String alias)
  {
    int loc;

    if ((loc = aliaslist.indexOf(alias)) == -1)
      return;

    try
      {
	aliaslist.removeElementAt(loc);
	displaylist.removeElementAt(loc);
      }
    catch (ArrayIndexOutOfBoundsException e)
      {
	e.printStackTrace(System.err);
      }

    namelist.setListData(displaylist);
    wndFrame.pack();
    namelist.repaint();
  }

  /**
   * Add a user to the displayed list.
   *
   * @param alias The user id to add to the list.  (This is not necessarily the displayed name.)
   */
  public void addAlias(String alias)
  {
    int cnt, aliasloc;
    boolean placedelement = false;

    if (aliaslist.contains(alias))
      return;

    if ((aliasloc = jwatchentries.indexOf(alias)) == -1)
      return;

    aliasloc--;

    while (aliasloc >= 0)
      {
	if (aliaslist.contains(jwatchentries.elementAt(aliasloc)))
	  {
	    try
	      {
		if (aliasloc+1 > aliaslist.size())
		  aliaslist.addElement(alias);
		else
		  aliaslist.insertElementAt(alias, aliasloc+1);

		String displayval = (String)watchtable.get(alias);
		if (displayval.length() == 0)
		  displayval = alias;

		if (aliasloc+1 > displaylist.size())
		  displaylist.addElement(displayval);
		else
		  displaylist.insertElementAt(displayval, aliasloc+1);
	      }
	    catch (ArrayIndexOutOfBoundsException e)
	      {
		e.printStackTrace(System.err);
	      }
	    placedelement = true;
	    break;
	  }
	aliasloc--;
      }

    if (!placedelement)
      {
	try
	  {
	    aliaslist.insertElementAt(alias, 0);

	    String displayval = (String)watchtable.get(alias);
	    if (displayval.length() == 0)
	      displayval = alias;
	    
	    displaylist.insertElementAt(displayval, 0);
	  }
	catch (ArrayIndexOutOfBoundsException e)
	  {
	    e.printStackTrace(System.err);
	  }
      }

    namelist.setListData(displaylist);
    wndFrame.pack();
    namelist.repaint();
  }

  /**
   * Cause the SNWatch to poll for logged on users by sending a LOCATE
   * message.  This will then update the list if necessary.
   */
  public void poll()
  {
    String hashentry, hashval;
    String peoplearr[] = new String[numpeople];
    Enumeration keyenum;
    int cnt = 0;

    /*Do a jlocate on all of the users*/
    /*Now enumerate the hash table, wander through it, and locate things*/
    //    keyenum = watchtable.keys();
    keyenum = jwatchentries.elements();

    /*Flush the vector*/
    displaylist.removeAllElements();
    aliaslist.removeAllElements();

    while (true)
      {
	try
	  {
	    hashentry = (String)keyenum.nextElement();
	  }
	catch (NoSuchElementException e)
	  {
	    break;
	  }

	if (hashentry.startsWith(";"))
	  {
	    hashval = (String)watchtable.get(hashentry);
	    if (hashval.length() != 0)
	      {
		displaylist.addElement(hashval);
		aliaslist.addElement(hashentry);
	      }
	    else
	      {
		displaylist.addElement(hashentry);
		aliaslist.addElement(hashentry);
	      }
	  }
	else
	  {
	    peoplearr[cnt++] = hashentry;
	  }
      }
    
    if (cnt > 0)
      {
	/*Need to do a locate*/
	JMessage message =
	  new JMessage(new String[] {"LOCATE"},
	  System.getProperty("user.name"), peoplearr);
	message.setCommandMessage(true);
	
	try
	  {
	    Socket server = new Socket("127.0.0.1", localport);
	    ObjectOutputStream out =
	      new ObjectOutputStream(server.getOutputStream());
	    ObjectInputStream in =
	      new ObjectInputStream(server.getInputStream());
	    Object incoming;
	    JMessage response;
	    String[] msglines;
	    int loop;
	    String token, alias;
	    
	    out.writeObject(message);
	    out.flush();
	    
	    incoming = in.readObject();
	    if (incoming instanceof JMessage)
	      {
		response = (JMessage)incoming;
		msglines = response.getMessage();
		
		for (loop = 0; loop < msglines.length; loop++)
		  {
		    StringTokenizer st = new StringTokenizer(msglines[loop]);
		    
		    try
		      {
			token = st.nextToken("@");
			alias = (String)watchtable.get(token);

			if (!displaylist.contains(alias))
			  {
			    displaylist.addElement(alias);
			    aliaslist.addElement(token);
			  }
		      }
		    catch (NoSuchElementException e)
		      {
		      }
		  }
	      }
	    server.close();
	  }
	catch (ClassNotFoundException e)
	  {
	    System.err.println("Missing class for returned object --> "+e);
	  }
	catch (IOException e)
	  {
	    System.err.println("I/O Error --> " + e); //IO Error
	  }
	
      }
    namelist.setListData(displaylist);
    wndFrame.pack();
    namelist.repaint();
  }

  public static void main(String argv[])
  {
    SNWatch watcher;

    try
      {
	watcher= new SNWatch();
      }
    catch (Exception e)
      {
	return;
      }

    while (true)
      {
	watcher.poll();
	try
	  {
	    Thread.sleep(30000);
	  }
	catch (InterruptedException e)
	  {
	    return;
	  }
      }
  }

}

/*The classes below process events for the SNWatch window*/
class SNWatchWndListener extends WindowAdapter
{
  private SNWatch watch;

  SNWatchWndListener(SNWatch watch)
  {
    this.watch = watch;

    watch.wndFrame.addWindowListener(this);
  }
  
  public void windowClosing(WindowEvent e)
  {
    /*Kill the SNClient if this window is closed*/
    SNClient.exit();
  }

}

class SNWatchMouseListener extends MouseAdapter
{
  private SNWatch watch;

  SNWatchMouseListener(SNWatch watch)
  {
    this.watch = watch;

    watch.namelist.addMouseListener(this);
  }

  public void mouseClicked(MouseEvent e)
  {
    boolean b1 = (e.getModifiers() & e.BUTTON1_MASK) > 0;

    if (((e.getModifiers() & e.BUTTON1_MASK) > 0)
	&& (e.getClickCount() == 2))
      {
	int[] selected;
	int cnt;
	String tolist = new String();
	
	selected = watch.namelist.getSelectedIndices();
	
	for (cnt = 0; cnt < selected.length; cnt++)
	  {
	    tolist = tolist+(String)watch.aliaslist.elementAt(selected[cnt])+" ";
	  }
	
	JSendWnd rspwnd = new JSendWnd(tolist);
	
	watch.namelist.clearSelection();
      }
  }
}

class SNWatchKeyListener extends KeyAdapter
{
  private SNWatch watch;

  SNWatchKeyListener(SNWatch watch)
  {
    this.watch = watch;

    watch.namelist.addKeyListener(this);
  }

  public void keyPressed(KeyEvent e)
  {

    if ((e.getKeyCode() == KeyEvent.VK_C) && e.isControlDown())
      {
	SNClient.exit();
      }
    
    if ((e.getKeyCode() == KeyEvent.VK_R))
      {
	watch.poll();
      }

    if ((e.getKeyCode() == KeyEvent.VK_S))
      {
	int[] selected;
	int cnt;
	String tolist = new String();

	selected = watch.namelist.getSelectedIndices();
	
	for (cnt = 0; cnt < selected.length; cnt++)
	  {
	    tolist = tolist+(String)watch.aliaslist.elementAt(selected[cnt])+" ";
	  }

	JSendWnd rspwnd = new JSendWnd(tolist);
	
	watch.namelist.clearSelection();
      }
  }

}
