/*
 * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */
import java.net.*;
import java.io.*;

class TalkServer {
    TalkServerThread[] tstList = new TalkServerThread[2];
    boolean DEBUG = true;

    public static void main(String[] args) {
	new TalkServer().start();
    }

    public void start() {
	ServerSocket serverRSocket = null;
	int numConnected = 0;

        try {
            serverRSocket = new ServerSocket(0);
            System.out.println("TalkServer listening on rendezvous port: "
                               + serverRSocket.getLocalPort());
        } catch (IOException e) {
            System.err.println("Server could not create server socket for rendezvous.");
	    return;
        }

	while (true) {

	    //Connect to two clients.
	    while (numConnected < 2) {
		TalkServerThread tst;
	        tst = connectToClient(serverRSocket);
	        if (tst != null) {
		    numConnected++;
		    if (tstList[0] == null) {
			tstList[0] = tst;
		    } else {
			tstList[1] = tst;
		    }
	        }
	    } //end while (numConnected < 2) loop

	    if (DEBUG) {
		try {
		    System.out.println("tst #0 = " + tstList[0]);
		} catch (Exception e) {}
		try {
		    System.out.println("tst #1 = " + tstList[1]);
		} catch (Exception e) {}
	    }

	    //If they're really OK, tell them to start writing.
	    if (everythingIsOK(0) & everythingIsOK(1)) {
	        for (int i = 0; i < 2; i++) {
	            writeToStream("START WRITING!\n----------------------"
			          + "-------------", tstList[i].os);
		}
	    } else {
		System.err.println("2 server threads created, but "
				   + "not everything is OK");
	    }

	    while (numConnected == 2) {
	        if (!everythingIsOK(0)) {
		    if (DEBUG) {
			System.out.println("Applet #0 is hosed; disconnecting.");
		    }
		    numConnected--;
		    cleanup(tstList[0]);
		    tstList[0] = null;
	        }
	        if (!everythingIsOK(1)) {
		    if (DEBUG) {
			System.out.println("Applet #1 is hosed; disconnecting.");
		    }
		    numConnected--;
		    cleanup(tstList[1]);
		    tstList[1] = null;
	        }
	       	try {
		    Thread.sleep(1000);
	        } catch (InterruptedException e) {
		} 
	    } //end while(numConnected==2) loop

	    if (DEBUG) {
		try {
		    System.out.println("Number of connections = " + numConnected);
		    System.out.println("tst #0 = " + tstList[0]);
		    System.out.println("tst #1 = " + tstList[1]);
		} catch (Exception e) {}
	    }

	} //end while (true) loop
    }

    protected TalkServerThread connectToClient(ServerSocket serverRSocket) {

	DataOutputStream os = null;
        Socket rendezvousSocket = null;
        int port;
	ServerSocket serverCSocket = null;
	Socket clientSocket = null;
	TalkServerThread tst = null;

	//Listen for client connection on the rendezvous socket.
        try {
	    rendezvousSocket = serverRSocket.accept();
	    os = new DataOutputStream(rendezvousSocket.getOutputStream());
        } catch (IOException e) {
            System.err.println("Accept failed.");
            e.printStackTrace();
	    return null;
        }

	//Create a new socket just for this client.
        try {
            serverCSocket = new ServerSocket(0);
	    port = serverCSocket.getLocalPort();
	    if (DEBUG) {
                System.out.println("TalkServer connected to applet.");
	    }
        } catch (IOException e) {
            System.err.println("Server could not create server socket.");
	    return null;
        }

	//Tell the client about this socket; wait for it to connect.
	try {
	    os.writeInt(port);
	    os.flush();
	    clientSocket = serverCSocket.accept();
	} catch (IOException e) {
            System.err.println("Couldn't connect applet with its socket.");
	    return null;
	}

	//Create a thread to handle this connection.
	try {
	    tst = new TalkServerThread(clientSocket, this);
	    tst.start();
	} catch (Exception e) {
 	    System.err.println("Couldn't create TalkServerThread:");
	    e.printStackTrace();
	    return null;
	}

	writeToStream("Successful connection. "
		      + "Please wait for second applet to connect...",
		      tst.os);
	return tst;
    }

    boolean everythingIsOK(int tstNum) {
	TalkServerThread tst = tstList[tstNum];

	if (tst == null) {
	    if (DEBUG) {
	        System.out.println("TalkServerThread #" + tstNum
				   + " is null");
	    }
	    return false;
	} else {
	    if (tst.os == null) {
		if (DEBUG) {
		    System.out.println("TalkServerThread #" + tstNum
				       + " output stream is null.");
		}
		return false;
	    }
	    if (tst.is == null) {
		if (DEBUG) {
		    System.out.println("TalkServerThread #" + tstNum
				       + " input stream is null.");
		}
		return false;
	    }
	    if (tst.socket == null) {
		if (DEBUG) {
		    System.out.println("TalkServerThread #" + tstNum
				       + " socket is null.");
		}
		return false;
	    }
	}
	//try {
	    //if ((tst.os == null) |
	        //(tst.is == null) |
	        //(tst.socket == null)) {
	        //return false;
	    //}
	//} catch (Exception e) {
	    //return false;
	//}
	return true;
    }

    void cleanup(TalkServerThread tst) {
	if (tst != null) {
	    try {
		if (tst.os != null) {
		    tst.os.close();
		    tst.os = null;
		}
	    } catch (Exception e) {} //Ignore errors
	    try {
		if (tst.is != null) {
		    tst.is.close();
		    tst.is = null;
		}
	    } catch (Exception e) {} //Ignore errors
	    try {
		if (tst.socket != null) {
		    tst.socket.close();
		    tst.socket = null;
		}
	    } catch (Exception e) {} //Ignore errors
	}
    }

    public void forwardString(String string, TalkServerThread requestor) {
	DataOutputStream clientStream = null;

	if (tstList[0] == requestor) {
	    if (tstList[1] != null) {
	        clientStream = tstList[1].os;
	    } else {
		if (DEBUG) {
		    System.out.println("Applet #0 has a string to forward, "
				       + "but Applet #1 is gone...");
		}
	        //cleanup();
		return;
	    }
	} else {
	    if (tstList[0] != null) {
	        clientStream = tstList[0].os;
	    } else {
		if (DEBUG) {
		    System.out.println("Applet #1 has a string to forward, "
				       + "but Applet #0 is gone...");
		}
	        //cleanup();
		return;
	    }
	}

	if (clientStream != null) {
	    writeToStream(string, clientStream);   
	} else if (DEBUG) {
	    System.out.println("Can't forward string -- no output stream.");
	}
    }

    public void writeToStream(String string, DataOutputStream stream) {
        if (DEBUG) {
            System.out.println("TalkServer about to forward data: "
                               + string);
        }

        try { 
            stream.writeUTF(string);
	    stream.flush();
            if (DEBUG) {
                System.out.println("TalkServer forwarded string.");
            }
        } catch (IOException e) {
            System.err.println("TalkServer failed to forward string:");
            e.printStackTrace();
	    //cleanup();
            return;
        } catch (NullPointerException e) {
            System.err.println("TalkServer can't forward string "
			       + "since output stream is null.");
	    //cleanup();
            return;
        }
    }

}
