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

/* Vlákno TourServerHandler řeší požadavky klienta.


   Údaje o každém klientovi udržuje objekt typu TourGroup, na který
   má každé vlákno odkaz.  

   Objekt typu TourGroup obsluhuje zprávy rozesílané skrze metodu
   broadcast().

   Možné zprávy klienta:
      * create name xPosn zPosn   -- nový klient si přeje připojení k virtuálnímu světu

	  * detailsFor toAddr toPort xPosn zPosn rotRadians
            -- předává údaje o spritu tohoto klienta objektu typu Client na 
                (toAddr,toPort)

      * bye           -- klient se odpojuje

      * <text>        -- rozešle text, před který připojí jméno klienta 

   Třída je velmi podobná třídě ChatServerHandler v multi-vláknovém diskusním serveru.
*/

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


public class TourServerHandler extends Thread
{
  private Socket clientSock;
  private String cliAddr;
  private String userName;
  private int port;

  private TourGroup tg;    // sdíli všechna vlákna

  public TourServerHandler(Socket s, TourGroup tg)
  {
    this.tg = tg;
    clientSock = s;
    userName = "?";
    cliAddr = clientSock.getInetAddress().getHostAddress();
    port = clientSock.getPort();
    System.out.println("Spojení od klienta z adresy (" + 
                 cliAddr + ", " + port + ")");
  } // konec metody: TourServerHandler()


  public void run()
  // zpracuje zprávy od klienta
  {
    try {
      // Získej V/V proudy ze socket
      BufferedReader in  = new BufferedReader( 
	  		new InputStreamReader( clientSock.getInputStream() ) );
      PrintWriter out = 
			new PrintWriter( clientSock.getOutputStream(), true );  // autoflush

      tg.addmerson(cliAddr, port, out);   // přidej údaje o klientovi do objektu typu TourGroup

      processClient(in, out);        // komunikuj s klientem
 
      // tady již klient svou komunikaci ukončil
      tg.delPerson(cliAddr, port, userName + " bye");         // odeber údaje o klientovi
      clientSock.close();
      System.out.println("Client " + userName + " (" + 
                 cliAddr + ", " +  port +  ") spojení uzavřeno");
    }
    catch(Exception e)
    {  System.out.println(e);  }
  }  // konec metody: run()


   private void processClient(BufferedReader in, PrintWriter out)
   // Zastaví, když se uzavře vstupní proud (je null) nebo když přišla zpráva "bye"
   // Jinak předá vstupní zprávu metodě doRequest()
   {
     String line;
     boolean done = false;
     try {
       while (!done) {
         if((line = in.readLine()) == null)
           done = true;
         else {
           // System.out.println(userName + " přijal zprávu: " + line);
           if (line.trim().equals("bye"))
             done = true;
           else 
             doRequest(line.trim(), out);
         }
       }
     }
     catch(IOException e)
     {  System.out.println(e);  }
   }  // konec metody: processClient()


  private void doRequest(String line, PrintWriter out)
  /*  Vstupní řádek může obsahovat:
            create ...      -- nový klient
			detailsFor ...
     nebo	text		    -- což je broadcast, se jménem klienta před zprávou
  */
  {
    if (line.startsWith("create"))
      sendCreate(line);
    else if (line.startsWith("detailsFor"))
      sendDetails(line);
    else  // pro rozeslání zprávy použij objekt typu TourGroup
      tg.broadcast(cliAddr, port, userName + " " + line);
  }  // konec metody: doRequest()


  private void sendCreate(String line)
  /* Nový klient si přeje připojit se k virtuálnímu světu.

     K tomu se potřebuje dozvědět o ostatních klientech a
     přidat sám sebe do jejich světa.

     Formát zprávy: create name xPosn zPosn

     Uloží jméno uživatele, extrahuje ze zprávy "create"
  */
  { StringTokenizer st = new StringTokenizer(line);
    st.nextToken();                  // skip 'create' word
    userName = st.nextToken();
    String xPosn = st.nextToken();   // neanalyzuj
    String zPosn = st.nextToken();   // neanalyzuj

    // pošli požadavek na údaje od ostatních klientů
    tg.broadcast(cliAddr, port, "wantDetails " + cliAddr + " " + port);

    // řekni ostatním klientům o nově připojeném
    tg.broadcast(cliAddr, port, "create " + userName + " "+xPosn+" "+zPosn);
  } // konec metody: sendCreate()



  private void sendDetails(String line)
  /* Odešle údaje o řádku line pojmenovanému klientovi
     Vstupní fomrát zprávy:   
          detailsFor toAddr toPort xPosn zPosn rotRadians
     Výstup: 
          detailsFor userName xPosn zPosn rotRadians
  */
  { StringTokenizer st = new StringTokenizer(line);
    st.nextToken();                       // přeskoč slovo 'detailsFor'
    String toAddr = st.nextToken();
    int toPort = Integer.parseInt( st.nextToken() );
    String xPosn = st.nextToken();        // neanalyzuj
    String zPosn = st.nextToken();        // neanalyzuj
    String rotRadians = st.nextToken();   // neanalyzuj

    tg.sendTo(toAddr, toPort, 
			"detailsFor " + userName + " " + xPosn + " " + zPosn + 
								" " + rotRadians);
  } // konec metody: sendDetails()

}  // konec třídy: TourServerHandler
