package talk;

import java.util.Vector;
import java.util.Enumeration;
import java.net.*;
import java.io.*;

class Connection implements Runnable
{
	// The socket through which we talk to the client.
	Socket socket;
	// The input stream on which we will read messages.
	// DataInputStream is a very useful class because it
	// has standard methods to read integers, strings, 
	// and so forth.
	DataInputStream inputStream;
	// The output stream on which we will write messages.
	DataOutputStream outputStream;
	// The thread that will read messages from the client.
	Thread inputThread;
	// The thread that will write messages to the client.
	Thread outputThread;
	// A queue of strings to be sent to the client.
	Vector outputStrings;
	// The server.
	Server owner;
	// The name of the user.
	String name;
	Connection(Server ownerArg, Socket s)
	{
		owner = ownerArg;
		socket = s;
		outputStrings = new Vector();
		// Start input and output threads.
		inputThread = new Thread(this);
		outputThread = new Thread(this);
		inputThread.start();	
		outputThread.start();	
	}
	public void run()
	{
		if (Thread.currentThread() == inputThread) {
			// Read input from the server.
			inputRun();	
		} else if (Thread.currentThread() == outputThread) {
			// Write output to the server.
			outputRun();	
		}
	}
	void inputRun()
	{
		try {
			// Set up the input stream.
			inputStream = new DataInputStream(
				socket.getInputStream());
			// Get the user's name.
			name = inputStream.readUTF();
			// Announce the user.
			owner.outputLine(this, "* * * " + name + 
				" has connected.");
			while (true) {
				// Input a line from the client.
				String s = inputStream.readUTF();
				// Output it to the other connections.
				owner.outputLine(this, "<" + name + "> " + s);
			}
		} catch (java.io.IOException e)
		{
			// Something went wrong. Stop this connection.
			outputThread.stop();
			owner.forgetConnection(this);
		}
	}
	void outputRun()
	{
		try {
			// Set up the output stream.
			outputStream = new DataOutputStream(
				socket.getOutputStream());
			while (true) {
				outputStep();
			}
		} catch (java.io.IOException e)
		{
			// Something went wrong. Stop this connection.
			inputThread.stop();
			owner.forgetConnection(this);
		}
	}
	void outputStep() throws java.io.IOException
	{
		// Do any work pending
		// in the output queue.
		flushOutputStrings();
		// Synchronize on the output queue,
		// then, if no output is ready yet,
		// signal our willingness to wait
		// until it is.
		synchronized (outputStrings) {
			if (outputStrings.size() == 0) {
				try {
					outputStrings.wait();
				} catch (InterruptedException ie) 
				{
					// This shouldn't happen.
				}
			}
		}
	}
	// Send the current contents of the output queue.
	void flushOutputStrings() throws java.io.IOException
	{
		while (outputStrings.size() > 0) {
			// Output a string to the server
			String s = (String) 
				outputStrings.elementAt(0);
			outputStream.writeUTF(s);
			// And remove what we have sent
			// from our queue of strings
			outputStrings.removeElementAt(0);
		}
	}
	// Accept a line of input from the server,
	// to be output to the client.
	void inputLine(String s)
	{
		// We don't want to output it now; that could
		// cause the server to pause. Instead, we add
		// it to the queue of strings to be sent by
		// the output thread, and we notify the 
		// output thread that the queue has changed.
		synchronized (outputStrings) {
			outputStrings.addElement(s);
			// Notify the output thread that there
			// is new data in the queue.
			outputStrings.notify();
		}
	}
}

