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

/* Sleduje a  reaguje na zprávy přicházející ze serveru.
   Možné typy zpráv:

    * create <name> xPosn zPosn
         // vytvoří návštěvníka virtuálního světa na tomto počítači

    * wantDetails cliAddr port
         // klienta na adrese (cliAddr, port) požaduje údaje o tomto klientovi

    * detailsFor userName xPosn zPosn rotRadians
         // jiný klient poslal údaje o sobě tomuto klientovi

    * <name> <command>
         // <command> může být jedno z následujícího:
            forward | back | left | right | rotCClock | rotClock | bye


   Zpráva "create" přichází od nového klienta při jeho prvním vytvoření.
   Nový klient požaduje své přidání do lokálního světa.

   Zpráva "wantDetails" přichází od nového klienta, když se chce dozvědět
   více údajů o tomto klientovi.
   V reakci na to je odeslána zpráva "detailsFor".

   Zprává "detailsFor" přichází od existujícího klienta poté, co tento
   klienta odeslal serveru zprávu "wantDetails".
   Pro každou přijatou zprávu "detailsFor" bude vytvořen jeden nový sprite.

   Ostatní příkazy slouží vesměs pro pohyb spritů reprezentujících ostatní
   klienty.
*/

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


public class TourWatcher extends Thread
{
  private HashMap visitors;   // uchovává dvojice (jméno uživatele, objekt spritu)
  private WrapNetTour3D w3d;
  private BufferedReader in;
  private Obstacles obs;


  public TourWatcher(WrapNetTour3D w3d, BufferedReader i, Obstacles obs)
  { this.w3d = w3d;
    in = i;  
    this.obs = obs;
    visitors = new HashMap();   // dosud žádní návštěvníci
  }


  public void run()
  // čte zprávy serveru a reaguje na ně
  { String line;
    try {
      while ((line = in.readLine()) != null) {
        // System.out.println( line);
        if (line.startsWith("create"))
          createVisitor( line.trim() );
        else if (line.startsWith("wantDetails"))  // další klient chce informace
          sendDetails( line.trim() );
       else if (line.startsWith("detailsFor"))  // info pro tohoto klienta
          receiveDetails( line.trim() );
        else 
          doCommand( line.trim() );
      }
    }
    catch(Exception e)    // uzavření socketu způsobí ukončení cyklu while
    { System.out.println("Spojení se serverem ztraceno"); 
      System.exit( 0 );
    }
  }  // konec metody: run()



  private void createVisitor(String line)
  /* Vytvoří návštěvníka virtuálního světa na tomto stroji.
     Formát zprávy:
		create <name> xPosn zPosn

     Návštěvník se jmenuje <name> a jeho sprite bude umístěn
     na pozici (xPosn ,zPosn). Spritem bude robot otočený ve směru
     kladné osy z.

     Řetězec <name> a s ním spojený sprite se uloží do hašovací
     tabulky typu HashMap
  */
  { StringTokenizer st = new StringTokenizer(line);

    st.nextToken();   // přeskoč slovo 'create'
    String userName = st.nextToken();
    double xPosn = Double.parseDouble( st.nextToken() );
    double zPosn = Double.parseDouble( st.nextToken() );
    System.out.println("Vytvářím: " + userName + "(" + xPosn + ", " + zPosn + ")" );

    if (visitors.containsKey(userName))
      System.out.println("Duplikátní jméno -- ignorujeme jej");   // měli bychom to oznámit serveru
    else {
      System.out.println("Vytvářím sprite");
      DistTourSprite dtSprite = w3d.addVisitor(userName, xPosn, zPosn, 0);
      visitors.put( userName, dtSprite);
    }
  }  // konec metody: createVisitor()



  private void sendDetails(String line)
  /* Formát zprávy:
		wantDetails cliAddr port
     Klient na adrese (cliAddr, port) požaduje údaje o tomto klientovi.
     Důvodem je vykreslení spritu klienta.
 
     Požádá objekt typu WrapNetTour3D (w3d) o zaslání údajů
     ve zprávě "detailsFor".    
  */
  { StringTokenizer st = new StringTokenizer(line);
    st.nextToken(); // přeskoč slovo 'wantDetails'
    String cliAddr = st.nextToken();
    String strPort = st.nextToken();   // neanalyzuj

    w3d.sendDetails(cliAddr, strPort);
  }  // konec metody: sendDetails()



  private void receiveDetails(String line)
  /* Jiný klient poslal údaje o sobě.
     Důvodem je vykreslení jeho sprite na tomto počítači.

     Formát zprávy:
		detailsFor userName xPosn zPosn rotRadians

     (xPosn,zPosn) je poloha sprite a rotRadians jeho otočení
     od směru vpřed.

     Uloží jméno návštěvníka a sprite do hašovací tabulky typu HashMap
  */
  { StringTokenizer st = new StringTokenizer(line);
    st.nextToken(); // přeskoč slovo 'detailsFor'
    String userName = st.nextToken();
    double xPosn = Double.parseDouble( st.nextToken() );
    double zPosn = Double.parseDouble( st.nextToken() );
    double rotRadians = Double.parseDouble( st.nextToken() );

    if (visitors.containsKey(userName))
      System.out.println("Duplikátní jméno -- ignorujeme jej");   // měli bychom to oznámit serveru
    else {
      System.out.println("Making sprite for " + userName);
      DistTourSprite dtSprite = w3d.addVisitor(userName, xPosn, zPosn, rotRadians);
      visitors.put( userName, dtSprite);
    }

  }  // konec metody: receiveDetails()


  private void doCommand(String line)
  /* Formát zprávy:
		<name> <command>
     kde <command> může být jedno z následujícího:
        forward | back | left | right | rotCClock | rotClock | bye

     Většinu těchto příkazů tvoří duplikáty pohybu spritu klienta
     ve zdejší kopii.

     Také ověří, je-li <name> v hašovací tabulce s návštěvníky
  */ 
  { StringTokenizer st = new StringTokenizer(line);
    String userName = st.nextToken();
    String command = st.nextToken();

    DistTourSprite dtSprite = (DistTourSprite) visitors.get(userName);
    if (dtSprite == null)
      System.out.println(userName + " zde není");
    else {
      if (command.equals("forward"))
        dtSprite.moveForward();
      else if (command.equals("back"))
        dtSprite.moveBackward();
      else if (command.equals("left"))
        dtSprite.moveLeft();
      else if (command.equals("right"))
        dtSprite.moveRight();
      else if (command.equals("rotCClock"))
        dtSprite.rotCounterClock();
      else if (command.equals("rotClock"))
        dtSprite.rotClock();
      else if (command.equals("bye")) {
        System.out.println("Odebírám údaje o " + userName);
        dtSprite.detach();
        visitors.remove(userName);
      }
      else
        System.out.println("Danému příkazu nerozumím");
    }
  }  // konec metody: doCommand()


}  // konec třídy: TourWatcher