package util.net;

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


/**
 * Generic Server class:  broadcasts whatever messages it receives to
 * all clients other than the message's originator.  Spawns one thread 
 * to listen and another for each client as they connect.
 */
public class Server implements Runnable
{
    //////////////////////////////////////////////////////////////
    // state
    private ArrayList myClients = new ArrayList();
    private ServerSocket mySocket;
    private Thread mySpirit;


    //////////////////////////////////////////////////////////////
    // constructors
    /**
     * Creates a server socket.  Waits for connections.
     */
    public Server (int port)
    {
	listenOn(port);
	mySpirit = new Thread(this);
	mySpirit.start();
    }

    /**
     * Wait loop to service the ServerSocket.  Called by this server's
     * Thread.  Should not be called externally.
     * Each time that  a connection is recieved, forks a BabySitter to 
     * handle that connection.
     */
    public void run ()
    {
	try
	{
	    Debug.println("Server:  listening for connections");
	    while (true)
	    {
		spawnBabySitter(mySocket.accept());
	    }
	}
	catch (IOException e)
	{
	    System.err.println("Server:  abrupt failure on accept attempt.");
	    System.err.println("Server:  no longer accepting connections.");
	}
    }


    //////////////////////////////////////////////////////////////
    // helper methods
    /**
     * Helper method to actually open the ServerSocket and intialize
     * other state.  Also spawns Thread to continue listening.
     */
    protected void listenOn (int port)
    {
	try
	{
	    Debug.println("Server:  starting up on port " + port);
	    mySocket = new ServerSocket(port);
	}
	catch (IOException e)
	{
	    throw new RuntimeException("Server:  failed to listen on port " + port);
	}
    }

    /** 
     * Creates a BabySitter with the client socket passed and
     * adds it to the list of BabySitters.
     *
     * @param s the client socket that this BabySitter will handle
     */
    protected void spawnBabySitter (Socket s)
    {
	myClients.add(new BabySitter(s, this));
    }

    /**
     * Removes a BabySitter for the list of babySitters.
     * 
     * @param bbst the BabySitter to be remooved from use.
     */
    protected void removeBabySitter (BabySitter sitter)
    {
	myClients.remove(sitter);
	sitter.stop();
    }

    /**
     * Sends the object passed in to all clients accept the one
     * represented by the BabySitter passed in.
     */
    protected synchronized void sendToAllExcept (Object o, BabySitter sitter)
    {
	Iterator iter = myClients.iterator();
	while (iter.hasNext())
	{
	    BabySitter current = (BabySitter)iter.next();
	    if (current != sitter)
	    {
		current.send(o);
	    }
	}
    }
}
