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

/* Testovací prostředí pro ScoreUDPServer s GUI-rozhraním.
   Klient zasílá jméno/skóre a dorazuje se na aktuální nejvyšší
   body.

   Narozdíl od TCP-verze klienta, neexistuje zde nic jako
   dlouhodobý spoj, který by bylo nutné ukončit při uzavření okna.

   Aplikace používá implicitní GUI-vlákno pro zasílání zpráv serveru 
   a aplikační vlákno pro jejich přijímání.
   
   Existuje zde malý synchronizační problém, protože dvě vlákna
   se mohou pokusit aktualizovat textovou oblast zpráv jtaMesgs 
   současně. Tento problém zde prostě ignorujeme.
*/

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;


public class ScoreUDPClient extends JFrame implements ActionListener
{
  private static final int SERVER_PORT = 1234;     // údaje o serveru
  private static final String SERVER_HOST = "localhost";

  private static final int BUFSIZE = 1024;   // max. velikost zprávy

  private DatagramSocket sock;
  private InetAddress serverAddr;

  private JTextArea jtaMesgs;
  private JTextField jtfName, jtfScore;
  private JButton jbGetScores;


  public ScoreUDPClient()
  {
     super( "UDP-klient pro práci se seznamem nejvyšších bodů" );

     initializeGUI();

     try {   // pokus se vytvořit klientův socket
       sock = new DatagramSocket();
     }
     catch( SocketException se ) {
       se.printStackTrace();
       System.exit(1);
     }

     try {  // pokus se převést název serveru na internetovou adresu
       serverAddr = InetAddress.getByName(SERVER_HOST);
     }
     catch( UnknownHostException uhe) {
       uhe.printStackTrace();
       System.exit(1);
     }

     setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
     setSize(300,450);
     setResizable(false);    // rozměry okna jsou pevně dány
     setVisible(true);

     waitForPackets();
  } // konec metody: ScoreUDPClient();


  private void initializeGUI()
  // textová oblast uprostřed a ovládací prvky dole
  {
    Container c = getContentPane();
    c.setLayout( new BorderLayout() );

    jtaMesgs = new JTextArea(7, 7);
    jtaMesgs.setEditable(false);
    JScrollPane jsp = new JScrollPane( jtaMesgs);
    c.add( jsp, "Center");

    JLabel jlName = new JLabel("Jméno: ");
    jtfName = new JTextField(10);

    JLabel jlScore = new JLabel("Skóre: ");
    jtfScore = new JTextField(5);
    jtfScore.addActionListener(this);    // stisknutí klávesy enter spustí odeslání jména/skóre

    jbGetScores = new JButton("Zobraz skóre");
    jbGetScores.addActionListener(this);

    JPanel p1 = new JPanel( new FlowLayout() );
    p1.add(jlName); p1.add(jtfName);
    p1.add(jlScore); p1.add(jtfScore);

    JPanel p2 = new JPanel( new FlowLayout() );
    p2.add(jbGetScores);

    JPanel p = new JPanel();
    p.setLayout( new BoxLayout(p, BoxLayout.Y_AXIS));
    p.add(p1); p.add(p2);

    c.add(p, "South");

  }  // konec metody: initializeGUI()


  // --------------------------------------------------------------
  // klient zpracovává příkazy uživatele, což provádí GUI-vlákno


   public void actionPerformed(ActionEvent e)
   /* Buď došlo k odeslání dvojice jméno/skóre nebo 
      bylo stisknuto tlačítko "Zobraz skóre"

      Odsud jsou odesílany serveru pouze výstupní zprávy,
      žádný vstup se nepřijímá. O odpovědi serveru se stará
      aplikační vlákno v metodě waitForPackets().
   */
   {
     if (e.getSource() == jbGetScores) {
       sendMessage(serverAddr, SERVER_PORT, "get");
       jtaMesgs.append("Odeslán příkaz \"get\"\n");
     }
     else if (e.getSource() == jtfScore)
       sendScore();
   } // konec metody: actionPerformed()



  private void sendScore()
  // Ověří, zadal-li uživatel jméno a skóre, a poté
  // odešle zprávu "score name & score &" serveru
  // Poznámka: ještě bychom měli zkontrolovat, že skóre je celé číslo
  {
    String name = jtfName.getText().trim();
    String score = jtfScore.getText().trim();
    // System.out.println("'" + name + "'   '" + score + "'");

    if ((name.equals("")) && (score.equals("")))
      JOptionPane.showMessageDialog( null, 
           "No name and score entered", "Send Score Error", 
			JOptionPane.ERROR_MESSAGE);
    else if (name.equals(""))
      JOptionPane.showMessageDialog( null, 
           "No name entered", "Send Score Error", 
			JOptionPane.ERROR_MESSAGE);
    else if (score.equals(""))
      JOptionPane.showMessageDialog( null, 
           "No score entered", "Send Score Error", 
			JOptionPane.ERROR_MESSAGE);
    else {
      sendMessage(serverAddr, SERVER_PORT, 
						"score " + name + " & " + score + " &");
      jtaMesgs.append("Sent " + name + " & " + score + "\n");
    }
  }  // konec metody: sendScore()



  private void sendMessage(InetAddress serverAddr, int serverPort, String mesg)
  // pošle zprávu socketu na specifikované adrese a portu
  {
    byte mesgData[] = mesg.getBytes();   // převeď zprávu do na bajtů
    try {
      DatagramPacket sendmacket =
          new DatagramPacket( mesgData, mesgData.length, serverAddr, serverPort);
      sock.send( sendmacket );
    }
    catch(IOException ioe)
    {  System.out.println(ioe);  }
  }  // konec metody: sendMessage()



  // ------------------------------------------------------------
  // zpracování reakcí serveru; provádí jej aplikační vlákno
  // kód je velmi podobný zpracování zpráv serverem


  private void waitForPackets()
  /* Opakovaně přijímá a zpracovává packet.
     Odsud se žádné zprávy serveru neposílají. Výstup je 
     ponechán GUI-vlánku. */
  {
    DatagramPacket receivePacket;
    byte data[];

    try {
      while (true) {
        // připrav prázdný paket
        data = new byte[BUFSIZE];
        receivePacket = new DatagramPacket(data, data.length);

        System.out.println("Čekám na paket ...");
        sock.receive( receivePacket );

        processServer(receivePacket);
      }
    }
    catch(IOException ioe)
    {  System.out.println(ioe);  }
  }  // konec metody: waitForPackets()



   private void processServer(DatagramPacket receivePacket)
   // extract údaje o serveru z přijatého paketu
   {
     InetAddress serverAddr = receivePacket.getAddress();
     int serverPort = receivePacket.getPort();
     String serverMesg = new String( receivePacket.getData(), 0,
                           receivePacket.getLength() );

     System.out.println("Paket serveru od " + serverAddr +
                                          ", " + serverPort );
     System.out.println("Zpráva serveru: " + serverMesg);
        
     showResponse(serverMesg);
   }  // konec metody: processServer()



   private void showResponse(String mesg)
   /* Jediná odezva serveru je na zprávu get.
      Reakce by měla být ve tvaru: "HIGH$$ n1 & s1 & .... nN & sN & "
   */
   {
     if ((mesg.length() >= 7) &&     // "HIGH$$ "
         (mesg.substring(0,6).equals("HIGH$$")))
       showHigh( mesg.substring(6).trim() );    
		    // odstraň klíčové slovo HIGH$$ a přilehlé mezery
     else    // nemělo by se stávat
       jtaMesgs.append( mesg + "\n");
   }  // konec metody: showResponse()



  private void showHigh(String mesg)
  // Analyzuje tabulku s nejvyšším skóre a pěkně ji zobrazí
  {
    StringTokenizer st = new StringTokenizer(mesg, "&");
    String name;
    int i, score;
    i = 1;
    try {
      while (st.hasMoreTokens()) {
        name = st.nextToken().trim();
        score = Integer.parseInt( st.nextToken().trim() );
        jtaMesgs.append("" + i + ". " + name + " : " + score + "\n");
        i++;
      }
      jtaMesgs.append("\n");
    }
    catch(Exception e)
    { 
      jtaMesgs.append("Problém při analýze tabulky s nejvyšším skóre\n");
      System.out.println("Problém při analýze tabulky s nejvyšším skóre: \n" + e);  
    }
  }  // konec metody: showHigh()



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

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

} // konec třídy: ScoreUDPClient

