package chatserver1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;

/**
 * Ein einfacher Chatserver. Clients koennen sich anmelden und werden nach einem Namen gefragt.
 * Alle Nachrichten werden an die anderen Teilnehmer verschickt.
 * 
 * @author  Ralf Kunze (rkunze@uos.de), Institut fuer Informatik, Universitaet Osnabrueck
 * @date 11.06.2007
 */
public class ChatServer {
    // Standardport
    final static int PORT = 12345;

    // Alle Chatter
    private static List<ChatServant> chatter = new LinkedList<ChatServant>();

    ServerSocket socket;

   

    public ChatServer(int port) throws IOException {
        socket = new ServerSocket(port); // listening socket
    }

    /**
     * Startet den Server, der Verbindungen mittels des ServerSockets annehmen
     * kann. Jeder Client wird in einem separaten Thread verarbeitet.
     * 
     * @throws IOException
     *             falls der Socket einen I/O Error produziert.
     */
    public void serve() throws IOException {
        for (;;) // endlos Schleife
        {
            Socket client = socket.accept(); // Verbindung annehmen
            ChatServant servant = new ChatServant(client);
            Thread thread = new Thread(servant);
            thread.start(); // Servant starten
        }
    }

    public static void main(String[] args) {
        try {
            int port = PORT; // default port

            if (args.length > 0)
                port = Integer.parseInt(args[0]); // user defined port

            new ChatServer(port).serve();
        } catch (NumberFormatException ex) { // invalid port
            System.err.println("Usage: java chatserver1.ChatServer [portnumber]");
            System.exit(1);
        } catch (IOException ex) { // generic i/o error
            System.err.println(ex);
            System.exit(1);
        }

    }
    
    /**
     * Servant. Repraesentiert einen Client auf der Serverseite.
     * 
     * @author  Ralf Kunze (rkunze@uos.de), Institut fuer Informatik, Universitaet Osnabrueck
     * @date 11.06.2007
     */
    private class ChatServant implements Runnable {

        private Socket client;

        private BufferedReader in;

        private PrintStream out;

        private String name;

        /**
         * Konstruktor erzeugt einen Servant und erstellt die notwendigen Streams.
         * 
         * @param client Socket zum Clienten.
         */
        public ChatServant(Socket client) {
            this.client = client;
            try {
                in = new BufferedReader(new InputStreamReader(client
                        .getInputStream()));
                out = new PrintStream(client.getOutputStream());
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * Run Methode des Servant, in dem der Name abgefragt wird,
         * Meldungen entgegengenommen werden und diese an alle versendet werden.
         */
        public void run() {

            String line;
            try {
                // Name abfragen
                out.print("Ihr Name: ");
                name = in.readLine();
                
                // Chatter zur Liste hinzufuegen => er bekommt nachrichten mit
                chatter.add(this);
                
                // Meldung an alle "Chatter joins"
                synchronized (chatter) {
                    // Meldung an alle: Neuer Chatter
                    for (ChatServant cs: chatter) {
                        cs.out.println(name + " joins");
                    }
                }

                // Nachrichten erhalten und an alle verteilen
                while ((line = in.readLine()) != null && line.compareTo(".")!=0) { // Hauptschleife:
                    // vom Clienten lesen
                    synchronized (chatter) {
                        // an alle Clienten ausgeben
                        for (ChatServant cs : chatter) {
                            cs.out.println(name + ": " + line);
                        }
                    }
                }
//                System.out.println("Verbindung beendet: " + line);
                
                // Client hat Verbindung beendet
                synchronized (chatter) {
                    chatter.remove(this); // entfernt den Chatter
                    client.close(); // schließt den Socket
                    // Meldung an alle: Chatter ist weg
                    for (ChatServant cs: chatter) {
                        cs.out.println(name + " leaves");
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}