package jasync;

import java.io.IOException;
import java.io.PrintStream;
import java.io.StreamCorruptedException;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Listens for connections and spawns ServerConnectionHandler threads when
 * clients connect.
 * @see ServerConnectionHandler
 */
class ServerListener extends Thread {

    /**
     * Queue for ServerConnectionHandlers as they're spawned.
     */
    private BlockingQueue<ServerConnectionHandler> queue;

    /**
     * The server socket on which we accept connections.
     */
    private ServerSocket serverSocket;

    /**
     * This system. This is given to ServerConnectionHandlers as they're
     * spawned.
     */
    private Peer self;
    

    /**
     * Constructor,
     * @param listenPort port to listen on.
     * @param name a name for this system.
     * @throws IOException
     */
    public ServerListener (int listenPort,
			   String name) throws
			       IOException {
	
	serverSocket = new ServerSocket(listenPort);

	queue = new LinkedBlockingQueue<ServerConnectionHandler>();

	self = new PeerImpl(0, 0, 0, PeerType.SERVER, name);
    }


    /**
     * Returns the ServerConnectionHandler queue.
     * @return ServerConnectionHandler queue for incoming connections.
     */
    public BlockingQueue<ServerConnectionHandler> getQueue () {
	return queue;
    }
    

    /**
     * Disconnects. Does not halt ServerConnectionHandler threads.
     */
    public void disconnect () {
	synchronized (serverSocket) {
	    try {
		serverSocket.close();
		serverSocket.wait();
	    }
	    catch (IOException e) {
		e.printStackTrace();
	    }
	    catch (InterruptedException e) {
		e.printStackTrace();
	    }
	}
    }


    /**
     * Polling loop for incoming connections. Spawns ServerConnectionHandlers
     * as for each connection.
     */
    public void run () {
	try {
	    while (true) {
		Socket socket;
		ServerConnectionHandler handler;

		socket = serverSocket.accept();
		handler = new ServerConnectionHandler(socket, self);
		handler.start();
		
		queue.put(handler);
	    }
	}
	catch (SocketException e) {
	    /* socket was asynchronously closed, thread that closed it is
	       wait()ing on serverSocket so we can guarantee that the thread
	       that called halt() only returns when we've finieshed. */

	    synchronized (serverSocket) {
		serverSocket.notifyAll();
	    }
	}
	catch (Throwable e) {
	    e.printStackTrace();

	    try {
		serverSocket.close();
	    }
	    catch (IOException f) {
		f.printStackTrace();
	    }
	}
    }
}    

