package telbookserver1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * Einfacher multi-threaded Telefonbuchservice. Alle Eintraege sind hart
 * implementiert und werden beim Start des Servers vorgenommen.
 * 
 * @author Ralf Kunze (rkunze@uos.de), Institut fuer Informatik, Universitaet
 *         Osnabrueck
 * @date 11.06.2007
 */

public class TelBookServer {

    public static final int PORT = 12345;

    /**
     * Eine Instanz dieser Klasse kann einen einzelnen Clienten versorgen.
     */
    private class TelBookServant implements Runnable {
        /**
         * client connection
         */
        private Socket client;

        /**
         * Initializiere einen neuen TelBookServant fuer die angegebene
         * Connection.
         * 
         * @param client
         *            Verbindung zum Client
         */
        public TelBookServant(Socket client) {
            this.client = client;
        }

        /**
         * Servant-Thread der mit einem Clienten kommuniziert.
         */
        public void run() {
            try { // construct streams
                InputStream in_stream = client.getInputStream();
                OutputStream out_stream = client.getOutputStream();
                BufferedReader in;
                PrintWriter out;
                String line;

                in = new BufferedReader(new InputStreamReader(in_stream));
                out = new PrintWriter(new OutputStreamWriter(out_stream));

                while ((line = in.readLine()) != null && line.length() != 0) { // read request line
                    System.out.println("Anfrage lautete <" + line + ">");
                    
                    if (line.compareTo("HELP") == 0) {
                        out.print("Available Commands:\r\n");
                        out.print("   GET\r\n");
                        out.print("   LIKE\r\n");
                        out.print("   HELP\r\n");
                        out.flush();
                    }else if (line.startsWith("GET")) {
                        line = line.replace("GET ", "");
                        String result = telbook.get(line);
                        
                        if (result != null)
                            out.print(result + "\r\n"); // use CRLF line sep
                        else
                            out.print("UNKNOWN\r\n"); // unknown name

                        out.flush();
                    } else if (line.startsWith("LIKE")) {
                        line = line.replace("LIKE ","");
                        StringBuilder sb = new StringBuilder();

                        for(Map.Entry<String,String> entry: telbook.entrySet()) {
                            String key = entry.getKey();
                            if (key.contains(line)) {
                                sb.append(key + ": " + entry.getValue() + "\r\n");
                            }
                        }
                        if(sb.length()!=0) {
                            out.write(sb.toString());
                        } else {
                            out.write("UNKNOWN\r\n");
                        }
                        out.flush();
                    } else {
                        out.print("Unknown Command\r\n");
                        out.flush();
                    }
                }

                client.close();
            } catch (IOException ex) // generic i/o error
            {
                System.err.println(ex);
            }
        }
    }

    /**
     * listening socket
     */
    private ServerSocket socket;

    /**
     * phone book data (constant)
     */
    private final Map<String, String> telbook;

    /**
     * Initializiere einen neuen TelBookServer der auf eingehende Verbindungen
     * horcht.
     * 
     * @param port
     *            Serverport.
     * @throws IOException
     *             when an I/O error occurs on the socket
     */
    public TelBookServer(int port) throws IOException {
        socket = new ServerSocket(port); // listening socket
        telbook = new HashMap<String, String>(); // phone book

        telbook.put("Astrid Heinze", "969 - 2480");
        telbook.put("Ralf Kunze", "969 - 2534");
        telbook.put("Dorothee Langfeld", "969 - 2558");
        telbook.put("Patrick Fox", "969 - 2482");
    }

    /**
     * Starte den Server, der Connections annehmen kann. Jede Connection wird an
     * einen separaten Thread weitergeleitet.
     * 
     * @throws IOException
     *             when an I/O error occurs on the socket
     * @see TelBookServant
     */
    public void serve() throws IOException {
        for (;;) // infinite loop
        {
            Socket client = socket.accept(); // accept connection
            TelBookServant servant = new TelBookServant(client);
            Thread thread = new Thread(servant);

            thread.start(); // start servant
        }
    }

    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 TelBookServer(port).serve();
        } catch (NumberFormatException ex) { // invalid port
            System.err.println("Usage: java net.TelBookServer port");
            System.exit(1);
        } catch (IOException ex) { // generic i/o error
            System.err.println(ex);
            System.exit(1);
        }
    }
}
