// Melvin K Lew <melvin@columbia.edu>
// $Source: /amd/cheers-fast/root/np/melvin/AIS/aisproj/RCS/WBCListenThread.java,v $
// $Date: 1998/04/27 00:44:30 $
// $Author: melvin $
// $Revision: 1.9 $
//
// CS-E6998-03 Advanced Internet Services: Term Project
//
// The JINCS Project: Collaborative Whiteboard Client/Server Application
//
// Group:
//	Johan Andersen <johan@columbia.edu>
//	Richard Denmark <thor@columbia.edu>
//	Melvin Lew <melvin@columbia.edu
//
// A multi-threaded networked Whiteboard Server
//

import java.io.*;
import java.net.*;
import java.util.*;
import WBCPrintThread.*;

/**
 * Extends Thread objects, an instance of this object exists for every
 * client connected to the Whiteboard server.  This listens for client
 * commands and calls the parser to handle the commands.
 *
 * @author	Melvin K Lew
 * @version	$Revision: 1.9 $, $Date: 1998/04/27 00:44:30 $
 *
 * @see		ClientInfo
 * @see		WBCPrintThread
 * @see		WBParser
 * @see		WBServer
 * @see		WBSListenThread
 */
public class WBCListenThread extends Thread
{
  private Socket socket = null;
  private ClientInfo clientinfo = null;
  private Hashtable clienthash = null;

  /**
   * Class constructor.
   *
   * @param	clienthash	a hashtable containing client information
   * @param	socket		a socket to which the client is connected
   *
   * @see	ClientInfo
   */
  public WBCListenThread(Hashtable clienthash, Socket socket)
  {
    super("WBCListenThread");
    this.socket = socket;
    this.clienthash = clienthash;

    try {
      // create a new ClientInfo object to store client information
      this.clientinfo = new ClientInfo(socket);
      // make sure we don't interfere with other threads
      synchronized ( clienthash ) {
	clienthash.put(this.clientinfo.getUsername(), this.clientinfo);
      }
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }

  /**
   * The execution part of this thread that runs until either the client
   * requests a shutdown or until the connection is lost.
   *
   * @see	ClientInfo
   * @see	WBCParser
   * @see	WBCPrintThread
   * @see	WBServer
   */
  public void run()
  {
    BufferedReader in = clientinfo.getBrin();
    PrintWriter out = clientinfo.getPwout();
    String inputLine, outputLine;
    ClientInfo ci;

    try {
      // display message indicating new connection
      System.out.println("### Connected to " + clientinfo.getHostport());

// Extra debug information: print out what's in clienthash
//      if ( WBServer.debug ) {
//	for ( Enumeration en = clienthash.elements(); en.hasMoreElements(); ) {
//	  ci = (ClientInfo) en.nextElement();
//	  System.out.println("%%% clienthash: " + ci.getUsername());
//	}
//      }

      // send an initial chat message to the client telling some basic info
      outputLine = "SERVER:ALL:CHAT: Your hostname = " +
	clientinfo.getHostname() + "\nSERVER:ALL:CHAT: Your port     = " +
	clientinfo.getPort() + "\nSERVER:ALL:CHAT: Connected on  = " +
	clientinfo.getDate() + "\n";
      // spawn a thread to send this string to the client
      if ( clientinfo.getAlive() ) {
	new WBCPrintThread(clientinfo, outputLine).start();
      }
      // broadcast a new userlist to all connected clients
      WBCParser.doUserlistBroadcast(clienthash);

      // if the client is still "alive", sit on the socket and read any
      // strings that come from the client.  Call the parser on the strings.
      while ( clientinfo.getAlive() &&
	      ((inputLine = in.readLine()) != null) ) {
	// the parser handles all commands
	WBCParser.parseCommand(clienthash, clientinfo, out, inputLine);
	if ( WBServer.debug ) {
	  System.out.println("--- " + clientinfo.getHostport() + " \"" +
			     clientinfo.getUsername() + "\": " + inputLine);
	}
      }
      // shut down the client
      out.close();
      in.close();
      socket.close();

      // remove this client's entry from the clienthash hashtable
      ci = (ClientInfo) clienthash.get(clientinfo.getUsername());
      if ( ci == clientinfo ) {
	// make sure we don't interfere with other threads accessing clienthash
	synchronized ( clienthash ) {
	  clienthash.remove(clientinfo.getUsername());
	}
	if ( WBServer.debug ) {
	  System.out.println("### Removed \"" + clientinfo.getUsername() +
			     "\" from clienthash");
	}
      }
      // broadcast a new userlist to all connected clients
      WBCParser.doUserlistBroadcast(clienthash);

      // display a message that the client has disconnected
      System.out.println("### Disconnected " + clientinfo.getHostport());
      if ( WBServer.debug ) {
	for ( Enumeration en = clienthash.elements(); en.hasMoreElements(); ) {
	  ci = (ClientInfo) en.nextElement();
	  System.out.println("%%% clienthash: " + ci.getUsername());
	}
      }

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

    return;
  }
}
