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

/* Neblokující sekvenční server, jenž uchovává skóre (a jméno) klientů
   v seznamu 10 nejvyšších bodů.

   Rozumí vstupní zprávě ve tvaru:
		get					   -- vrací seznam nejvyšších skóre
        score name & score &   -- přidá skóre pro dané jméno
		bye					   -- ukončí spojení s klientem

   Seznam udržuje v souboru SCORFN a při startu jej načítá.
   Server lze ukončit kombinací klávec CTRL a C.

   Třída je odvozena ze souboru SelectSockets.java

   Používáme neblokující detekci nových socketových kanálu od
   klientů a používáme neblokující čtení těchto kanálů.

*/

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
// import java.nio.charset.*;
import java.util.*;


public class SelectScoreServer
{
  private static final int PORT_NUMBER = 1234;

  private HighScores hs;
  private HashMap clients;   // mapuje kanály na objekty typu ClientInfo


  public SelectScoreServer()
  {
    hs = new HighScores();
    clients = new HashMap();
    try {
      System.out.println("Naslouchám na portu " + PORT_NUMBER);

      // připrav kanál serveru a socket
      ServerSocketChannel serverChannel = ServerSocketChannel.open();
      serverChannel.configureBlocking (false);   // použij neblokující režim

      ServerSocket serverSocket = serverChannel.socket();
      serverSocket.bind( new InetSocketAddress(PORT_NUMBER) );    // nastav port pro naslouchání

      Selector selector = Selector.open();
      serverChannel.register(selector, SelectionKey.OP_ACCEPT);   // registruj kanál se selektorem

      while (true) {
        // System.out.println("Počet klientů: " + clients.size());
        selector.select();     // wait for ready channels
        Iterator it = selector.selectedKeys().iterator();   // získej iterátor pro klíče
        SelectionKey key;
        while (it.hasNext()) {       // prohlédni každý klíč
          key = (SelectionKey) it.next();   // získej klíč
          it.remove();                      // odeber jej
          if (key.isAcceptable())       // nové spojení?
            newChannel(key, selector);
          else if (key.isReadable())    // nějaká data ke čtení?
            readFromChannel(key);
          else
            System.out.println("Klíč nebyl zpracován: " + key);
        }
      }
    }
    catch(IOException e) 
    {  System.out.println(e); }
  } // konec metody: SelectScoreServer()


  private void newChannel(SelectionKey key, Selector selector)
  // Přidej socketový kanál pro nového klienta do selektoru selcetor
  {
    try {
      ServerSocketChannel server = (ServerSocketChannel) key.channel();
      SocketChannel channel = server.accept();           // verzmi kanál
      channel.configureBlocking (false);                 // použij neblokující režim
      channel.register(selector, SelectionKey.OP_READ);  // zaregistruj jej zadaným selektorem

      clients.put(channel, new ClientInfo(channel, this) );   // ulož údaje
    }
    catch (IOException e)
    {  System.out.println( e ); }
  }  // konec metody: newChannel()



  private void readFromChannel(SelectionKey key)
  // zpracuj vstup čekající na kanálu
  {
    SocketChannel channel = (SocketChannel) key.channel();
    ClientInfo ci = (ClientInfo) clients.get(channel);
    if (ci == null)
      System.out.println("Pro kanál neexistuje ClientInfo: " + channel);
    else {
      String msg = ci.readMessage();
      if (msg != null) {
        System.out.println("Přečtená zpráva: " + msg);
        if (msg.trim().equals("bye")) {
          ci.closeDown();
          clients.remove(channel);  // odeber ci z hašovací tabulky
        }
        else 
          doRequest(msg, ci);
      }
    }
  } // konec metody: readFromChannel()



  private void doRequest(String line, ClientInfo ci)
  /*  Vstupní řádek může vypadat buď takto:
             "score name & score &"
      nebo takto:
            "get"
  */
  {
    if (line.trim().toLowerCase().equals("get")) {
      System.out.println("Zpracovávám 'get'");
      ci.sendMessage( hs.toString() );
    }
    else if ((line.length() >= 6) &&     // "score "
        (line.substring(0,5).toLowerCase().equals("score"))) {
      System.out.println("Zpracovávám 'score'");
      hs.addScore( line.substring(5) );    // odřízni klíčové slovo score
    }
    else
      System.out.println("Ignoruji vstupní řádek");
  }  // konec metody: doRequest()


  public void removeChannel(SocketChannel chan)
  // volá jej objekt typu Clientinfo při uzavření kanálu
  {  clients.remove(chan);  }


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

  public static void main(String[] argv)
  {  new SelectScoreServer();  }


} // konec třídy: SelectScoreServer

