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

/* Objekt typu ChatClient komunikuje s objektem typu ChatServer.
   Může poslat následující zprávy:

     who -- vrátí seznam uživatelů v následujícím formátu
            "WHO$$ cliAddr1 & port1 & ... cliAddrN & portN & "

     bye -- klient se odpojuje

	 jakýkoliv text -- který se rozešle všech klientům ve formátu
				  "(cliAddr,port): msg".
                  Adresa a port jednoznačně identifikují cílového klienta.

  První a třetí zprávy se posílají přes GUI-rozhraní:
     * tlačítkem "Kdo je přítomen"
     * textovým polem "Zpráva:"

  Klepnutí na tlačítko pro uzavření okna způsobí odeslání zprávy
  "bye" před ukončením programu klienta.

  Pro zpracování zpráv přicházejících od objektu typu ChatServer existuje
  zvláštní vlákno ChatWatcher.

  ---- Změny: 30. dubna 2004 ---

  Upravena metoda showMsg() tak, aby posunula pozici kurzoru a využila
  metodu invokeLater, čímž zabrání problémům, které vyvstávají z knihovnou
  Swing a vlákny. Více informace najdete na adrese:  
  http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
    - poděkování patří Rachel Struthersové (rmstruthers@mn.rr.com)

  Volání metody makeContact() přesunuto za otevření okna.  
*/

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


public class ChatClient extends JFrame implements ActionListener
{
  private static final int PORT = 1234;     // údaje o serveru
  private static final String HOST = "localhost";

  private Socket sock;
  private PrintWriter out;  // výstup směřující k serveru

  private JTextArea jtaMesgs;  // výstupní textová oblast
  private JTextField jtfMsg;   // pro posílání zpráv
  private JButton jbWho;       // pro posílání zprávy "who"


  public ChatClient()
  {
     super("Diskusní klient");
     initializeGUI();
     // makeContact();

     addWindowListener( new WindowAdapter() {
       public void windowClosing(WindowEvent e)
       { closeLink(); }
     });

     setSize(300,450);
     setVisible(true);

     makeContact();    // změněno: přesunuto za otevření okna
  } // konec metody: ChatClient();


  private void initializeGUI()
  /* Textová oblast ve stredu a ovládaní pod ní.
     Ovládání:
         - textové pole pro zadávání zpráv
         - tlacítko "Kdo je prítomen"
  */
  {
    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 jlMsg = new JLabel("Zpráva: ");
    jtfMsg = new JTextField(15);
    jtfMsg.addActionListener(this);    // stisknutí klávesy enter spustí odeslání jména/skóre

    jbWho = new JButton("Kdo je přítomen");
    jbWho.addActionListener(this);

    JPanel p1 = new JPanel( new FlowLayout() );
    p1.add(jlMsg); p1.add(jtfMsg);

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

    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()


  private void closeLink()
  {
    try {
      out.println("bye");    // informuj server that client is disconnecting
      sock.close();
    }
    catch(Exception e)
    {  System.out.println( e );  }

    System.exit( 0 ); 
  } // konec metody: closeLink()

    
  private void makeContact()
  // kontaktuje server a spustí vlákno ChatWatcher
  {
    try {
      sock = new Socket(HOST, PORT);
      BufferedReader in  = new BufferedReader( 
		  		new InputStreamReader( sock.getInputStream() ) );
      out = new PrintWriter( sock.getOutputStream(), true );  // autoflush

      new ChatWatcher(this, in).start();    // spusť sledování zpráv serveru
    }
    catch(Exception e)
    {  System.out.println(e);  }
  }  // konec metody: makeContact()



   public void actionPerformed(ActionEvent e)
   /* Buď se má poslat zprávat nebo bylo stisknuto tlačítko "Kdo je přítomen". */
   {
     if (e.getSource() == jbWho)
       out.println("who");   // reakci čte vlákno ChatWatcher
     else if (e.getSource() == jtfMsg)
       sendMessage();
   }


  private void sendMessage()
  /* Ověř, zadal-li uživatel zprávu, a pak ji pošli serveru. */
  {
    String msg = jtfMsg.getText().trim();
    // System.out.println("'"+msg+"'");

    if (msg.equals(""))
      JOptionPane.showMessageDialog( null, 
           "No message entered", "Send Message Error", 
			JOptionPane.ERROR_MESSAGE);
    else {
      out.println(msg);
      // showMsg("Sent: " + msg + "\n");
    }
  }  // konec metody: sendMessage()


/*
  synchronized public void showMsgFoo(String msg)
  // Metodu je nunté synchronizovat, neboť může být volána jak
     tímto objektem, tak i vláknem URLChatWatchcer.
  {  jtaMesgs.append(msg);  }
*/


  public void showMsg(final String msg)
  /* Aktualizujeme textovou oblast se zprávami, takže kód by
     mělo provést vlákno obsluhující události knihovny Swing,
     čehož docílíme zavoláním metody invokeLater().

     Parametr msg musí být finalní, aby jej bylo možné použít ve vložené
     třídě pro rozhraní Runnable.
   
     Metoda showMsg() může být volána tímto objektem a vláken URLWatcher,
     ovšem aktualizace se serializují tím, že se umísťují do fronty 
     dispečera událostí. To znamená, že tuto metodu synchronizovat nemusíme.

     Poděkování náleží Rachle Struthersové (rmstruthers@mn.rr.com)
  */
  { 
    // System.out.println("showMsg(): " + msg);
    Runnable updateMsgsText = new Runnable() {
      public void run() 
      { jtaMesgs.append(msg);  // připoj zprávu do textové oblasti
        jtaMesgs.setCaretPosition( jtaMesgs.getText().length() );
            // přesuň vkládací pozici na konec textu
      }
    };
    SwingUtilities.invokeLater( updateMsgsText );
  } // konec metody: showMsg()


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

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

} // konec třídy: ChatClient

