package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * Talks to one chat client: the first line is the client's name, then every
 * line is broadcast to everyone. run() is executed by this client's thread, but
 * send() is called by whichever handler thread is broadcasting, so send() is
 * synchronized: two threads must not write to the same stream at once.
 */
public class ChatHandler implements Runnable {

	private Socket socket_;
	private ChatHub chat_;
	private BufferedReader in_; // used only by this client's thread
	private PrintWriter out_; // used only in send()

	/**
	 * Sets up the streams. Runs on the server's main thread, before this
	 * handler's thread is started.
	 */
	public ChatHandler ( Socket socket, ChatHub chat ) throws IOException {
		socket_ = socket;
		chat_ = chat;
		in_ = new BufferedReader(new InputStreamReader(socket.getInputStream(),
		                                               StandardCharsets.UTF_8));
		out_ = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),
		                                              StandardCharsets.UTF_8));
	}

	@Override
	public void run () {
		String name = null;
		try {
			send("name?");
			name = in_.readLine();
			if ( name == null ) {
				return; // left before giving a name
			}
			chat_.addClient(this);
			chat_.broadcast("* " + name + " joined (" + chat_.getClientCount()
			    + " here)");

			for ( ; true ; ) {
				String line = in_.readLine();
				if ( line == null || line.equals("bye") ) {
					break;
				}
				chat_.broadcast(name + ": " + line);
			}
		} catch ( IOException e ) {
			System.out
			    .println(Thread.currentThread().getName() + ": " + e.getMessage());
		} finally {
			chat_.removeClient(this);
			if ( name != null ) {
				chat_.broadcast("* " + name + " left");
			}
			try {
				socket_.close();
			} catch ( IOException e ) {}
		}
	}

	/**
	 * Sends one line to this client. Called from any handler's thread.
	 */
	public synchronized void send ( String message ) {
		out_.println(message);
		out_.flush();
	}

}
