
// NameServer.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Server akceptuje datagramz od užívatelů zapojených do
   diskusní multicastové skupiny.

   Uživatel pošle při pokusu o přihlášení do skupiny zprávu 
    "hi <name>". Je-li jméno unikátní, pak server pošle zpět
   řetězec s adresou multicastové skupiny a přidá jméno do seznamu
   členů skupiny.

   Zpráva "bye <name>" způsobí odebrání klienta z tohoto seznamu.
   Kontrola, která by ověřila, zda-li klient skutečně opustil
   multicastovou skupinu, se neprovádí.

   Reakcí na zprávu "who" je seznam jmen aktuálních členů skupiny.
*/

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


public class NameServer
{
  private static final String GROUP_HOST = "228.5.6.7";
      // multicastová adresa skupiny posílaná novým členům

  private static final int PORT = 1234;      // pro tento server
  private static final int BUFSIZE = 1024;   // max. velikost zprávy

  private DatagramSocket serverSock;

  private ArrayList groupMembers;  
    // uchovává jména současných členů multicastové skupiny

  
  public NameServer()
  {
    try {  // zkus pro daný server vytvořit socket
      serverSock = new DatagramSocket(PORT);
    }
    catch(SocketException se)
    {  System.out.println(se);
       System.exit(1);
    }

    groupMembers = new ArrayList();
    waitForPackets();
  } // konec metody: NameServer()


  private void waitForPackets()
  // počkej na paket, process it, repeat
  {
    DatagramPacket receivePacket;
    byte data[];

    System.out.println("Připraven na zprávy klientů");
    try {
      while (true) {
        data = new byte[BUFSIZE];  // připrav prázdný paket
        receivePacket = new DatagramPacket(data, data.length);
        serverSock.receive( receivePacket );  // počkej na paket

        // extrahuj adresu klienta, jeho port a zprávu
        InetAddress clientAddr = receivePacket.getAddress();
        int clientPort = receivePacket.getPort();

        String clientMsg = new String( receivePacket.getData(), 0,
                           receivePacket.getLength() ).trim();
        System.out.println("Přijato: " + clientMsg);

        processClient(clientMsg, clientAddr, clientPort);
      }
    }
    catch(IOException ioe)
    {  System.out.println(ioe);  }
  }  // konec metody: waitForPackets()


  private void processClient(String msg, InetAddress addr, int port)
  /* Existují tři možné typy zpráv:
       hi <name>   // přidá jméno name do seznamu skupiny (je-li unikátní)
                      a oznámí její multicastovou adresu

       bye <name>  // smaže jméno name ze seznamu skupiny

       who         // pošle zpět seznam všech aktuálních členů
  */
  {
    if (msg.startsWith("hi")) {
      String name = msg.substring(2).trim();
      if (name != null && isUniqueName(name)) {
          groupMembers.add(name);
          sendMessage(GROUP_HOST, addr, port);  // pošli multicastovou adresu
      }
      else
        sendMessage("no", addr, port);
    }
    else if (msg.startsWith("bye")) {
      String name = msg.substring(3).trim();
      if (name != null)
          removeName(name);  // odeber jméno ze seznamu
      // všimněte si: multicastovou skupinu ani odesílatele nekontrolujeme
    }
    else if (msg.equals("who"))
      sendMessage( listNames(), addr, port);
    else
      System.out.println("Nerozumín zprávě");

  }  // konec metody: processClient()


  private void sendMessage(String msg, InetAddress addr, int port)
  // pošle zpráv zpět klientovi
  {
    try {
      DatagramPacket sendmacket =
          new DatagramPacket( msg.getBytes(), msg.length(), 
   						addr, port);
      serverSock.send( sendmacket );
    }
    catch(IOException ioe)
    {  System.out.println(ioe);  }
  } // konec metody: sendMessage()


  // ------------------- zpracování jmen -------------------


  private boolean isUniqueName(String nm)
  // vrátí true pokud se nm nenachází v seznamu členů skupiny
  {
    String name;
    for(int i=0; i < groupMembers.size(); i++) {
      name = (String) groupMembers.get(i);
      if (name.equals(nm))
        return false;
    }
    return true;
  } // konec metody: isUniqueName()


  private void removeName(String nm)
  // odebere nm ze seznamu členů skupiny
  {
    String name;
    for(int i=0; i < groupMembers.size(); i++) {
      name = (String) groupMembers.get(i);
      if (name.equals(nm)) {
        groupMembers.remove(i);
        break;
      }
    }
  }  // konec metody: removeName()

  
  private String listNames()
  /* Vrátí sznam členů skupiny jako řetězec ve tvaru:
        č1 jméno1 \n č2. jméno2 \n ...
  */
  { String list = new String();
    String name;
    for(int i=0; i < groupMembers.size(); i++) {
      name = (String) groupMembers.get(i);
      list += "" + (i+1) + " " + name + "\n";
    }
    return list;
  } // konec metody: listNames()


  // ------------------------------------

  public static void main(String args[]) 
  {  new NameServer();  }


} // konec třídy: NameServer
