package SimpleChatOne;

// Fri Oct 15 18:07:43 EST 2004
//
// Written by Sean R. Owens, sean at guild dot net, released to the
// public domain.  Share and enjoy.  Since some people argue that it is
// impossible to release software to the public domain, you are also free
// to use this code under any version of the GPL, LPGL, or BSD licenses,
// or contact me for use of another license.
// http://darksleep.com/player

import java.net.Socket;
import java.net.ServerSocket;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class Handler implements Runnable {
    private final static int READ_BUF_SIZE=1024;
    private final static int SAVE_BUF_SIZE=10240;
    private Socket sock = null;
    private Switchboard switchboard = null;
    private InputStream sockInput = null;
    private OutputStream sockOutput = null;
    private Thread myThread = null;
    private byte[] bufferedData = null; // This is where we keep bytes we've read until we get a \n
    private boolean loggedIn = false;
    private String name="NotLoggedIn";
    public String getName() { return name; }

    public Handler(Socket sock, Switchboard switchboard, String messageOfTheDay) throws IOException {
        this.sock = sock;
        this.switchboard = switchboard;
        sockInput = sock.getInputStream();
        sockOutput = sock.getOutputStream();
        bufferedData = new byte[SAVE_BUF_SIZE];
        name = sock.getRemoteSocketAddress().toString();
        myThread = new Thread(this);
        try {
            sockOutput.write(messageOfTheDay.getBytes(), 0, messageOfTheDay.getBytes().length);
            // This call to flush() is optional - we're saying go
            // ahead and send the data now instead of buffering
            // it.
            sockOutput.flush();
        }
        catch (IOException e) {
        }
        // Note that if we call myThread.start() now, we run the risk
        // of this new thread calling run before we're finished
        // constructing.  We can't count on the fact that we call
        // .start() last - javac or the jvm might have reordered the
        // above lines.  The class constructing us must wait for the
        // constructor to return and then call start() on us.
        System.out.println(this.getClass().getName()+": New handler created.");
    }

    public void start() {
        myThread.start();
    }
    
    public synchronized void write(byte[] data, int numBytesRead) {
        try {
            sockOutput.write(data, 0, numBytesRead);
            // This call to flush() is optional - we're saying go
            // ahead and send the data now instead of buffering
            // it.
            sockOutput.flush();
        }
        catch (IOException e){
            try {
                System.err.println(this.getClass().getName()+": Error writing to socket, closing socket.");
                sock.close();
            }
            catch (Exception e2){
                System.err.println(this.getClass().getName()+": Exception while closing socket, e2="+e2);
                e.printStackTrace(System.err);
            }
            switchboard.remove(this);
        }
    }

    // This method is responsible for processing the bytes we've read.
    // In the interest of clarity, it is not using the most efficient
    // approach.  In particular, it is not trying to be to clever to
    // avoid copying the data around.
    public void process(byte[] data, int numBytes) {
        // For this version of SimpleChat, all we're going to do is
        // forward data onto the other chatters.
        switchboard.sendToAllConnections(this, data, numBytes);
    }

    // All this method does is wait for some bytes from the
    // connection, read them, then write them back again, until the
    // socket is closed from the other side.
    public void run() {
        System.out.println(this.getClass().getName()+": Handler run() starting.");
        byte[] buf = new byte[READ_BUF_SIZE];
        while(true) {
            int numBytesRead = 0;
            try {
                // This call to read() will wait forever, until the
                // program on the other side either sends some data,
                // or closes the socket.
                numBytesRead = sockInput.read(buf, 0, buf.length);
                if(numBytesRead < 0) {
                    System.err.println(this.getClass().getName()+": Tried to read from socket, read() returned < 0,  Closing socket.");
                    break;
                }
                System.err.println(this.getClass().getName()+": Received "+numBytesRead+" bytes, sending to other clients: "+new String(buf));

                process(buf, numBytesRead);
            }
            catch (Exception e){
                e.printStackTrace(System.err);
                break;
            }
        }

        try {
            System.err.println(this.getClass().getName()+":Closing socket.");
            sock.close();
        }
        catch (Exception e) {
            System.err.println(this.getClass().getName()+":Exception while closing socket, e="+e);
            e.printStackTrace(System.err);
        }
        switchboard.remove(this);
    }
}