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

/* Síťová verze souboru FourByFour.java

  Zprávy zasílané klientem:
        -- počáteční otevření spojení
            * přijme zpět     ok <id>   nebo   je plno
        -- try <posn>
            * může přijmout tooFewPlayers
        -- odpojení

   Změny:
      - playerID je 1 nebo 2
      - upravena třída Board, aby neměnila proměnnou playerID

      - odebráno tlačítko GUI button (jbDisconnect) a dvě textová pole
        pro hlášení ID a stavu hry
      - textová pole nahrazena překryvy canvasu
      - tlačítko pro odpojení nepoužito
      - proměnná gameOver třídy Board nahrazena proměnnou isDisabled třídy NetFourByFour

    Když hráč (např. p1) chce provést tah:
       1. PickDragBehaviour zavolá metodu tryMove() této třídy.
       2. PlayerServerHandler je informován zprávou "try <posn>"
             - p1 může obdržet zprávu "tooFewPlayers" nebo
             - obslužný objekt pošle zprávu "otherTurn <playerID><posn>"
               druhému hráči (p2)
       3. Bez čekání na jakoukoliv odpověď vrací volá metoda tryMove() metodu doMove(),
          která volá tryPosn() třídy WrapNetFBF a poté metodu tryPosn() třídy Board.

       4. Třída Board aktualizuje datové struktury a volá metodu set třídy Positions()
          pro aktualizaci 3D-reprezentace.

    Pro druhého hráče (p2):
       1. Objekt typu FBFServer pošle zprávu "otherTurn <playerID><posn>"
          vláknu FBFWatcher hráče p2.
       2. Vlákno FBFWatcher zavolá metodu doMove() svého objektu typu NetFourByFour.
       3 & 4. Stejné jako výše.

    Když třída Board detekuje, že hráč vyhrál:
       1. Zavolá svou metodu reportWinner(), která poté zavolá disable().       
       2. Metoda disable() přeruší síťové spojení, aktualizuje text stavu
          hry a nastaví proměnnou isDisabled na true. Ta zabrazní jakýmkoliv
          následujícím voláním metdy tryMove() třídy NetFourByFour.
*/

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



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

  private static final int MAX_PLAYERS = 2;
  private final static int PLAYER1    = 1;
  private final static int PLAYER2    = 2;   // dříve šlo o počítač


  private WrapNetFBF wrapFBF;

  private Socket sock;
  private PrintWriter out;  

  // týká se hry
  private int playerID;
  private String status;         // pro umístění infa do 3D-canvasu
  private int numPlayers;
  private int currPlayer;        // který hráč je na řadě?
  private boolean isDisabled;    // říká, zda-li hra skončila



  public NetFourByFour() 
  {
    super("Piškvorky po síti");

    playerID = 0;     // dosud žádné ID
    status = null;    // žádné hlášení stavu
    numPlayers = 0;
    currPlayer = 1;    // hráč 1 začíná hru
    isDisabled = false;

    makeContact();

    Container c = getContentPane();
    c.setLayout( new BorderLayout() );
    wrapFBF = new WrapNetFBF(this);
    c.add(wrapFBF, BorderLayout.CENTER);

    addWindowListener( new WindowAdapter() {
      public void windowClosing(WindowEvent e)
      { disable("končíme");
        System.exit( 0 ); 
      }
    });

    pack();
    setResizable(false);    // rozměry okna jsou pevně dány
    setVisible(true);
  } // konec metody: NetFourByFour()


    
  private void makeContact()
  // kontaktuje FBFServer a poté jeden z jeho obslužných vláken
  {
    try {
      sock = new Socket(HOST, PORT);
      BufferedReader in  = new BufferedReader( 
		  		new InputStreamReader( sock.getInputStream() ) );
      out = new PrintWriter( sock.getOutputStream(), true );  // autoflush

      new FBFWatcher(this, in).start();    // spusť sledování zpráv serveru
    }
    catch(Exception e)
    {  // System.out.println(e);
       System.out.println("Nemohu kontaktovat server NetFourByFour");
       System.exit(0);
     }
  }  // konec metody: makeContact()


  public void tryMove(int posn)
  // volá ji objekt typu PickDragBehavior pro zahájení nového tahu
  {
    if (!isDisabled) {
      if (numPlayers < MAX_PLAYERS)
        setStatus("Čekám na hráče " + otherPlayer(playerID) );
      else if (playerID != currPlayer)
        setStatus("Promiň, ale na tahu je hráč " + currPlayer);
      else if (numPlayers == MAX_PLAYERS) {
        out.println( "try " + posn );   // oznam serveru
        doMove(posn, playerID);         // proveď, neček na žádnou odpověď
      }
      else
       System.out.println("Chyba při zpracování zvolené pozice");
    }
  }  // konec metody: tryMove()


  synchronized public void doMove(int posn, int pid)
  /* Voláno tímto objektem pro provedení tahu tohoto hráče a 
     vláknem FBFWatcher pro provedení tahu druhého hráče. 
     Ovšem dle klíčového slova synchronized, nikdy ne současně. */
  { 
    wrapFBF.tryPosn(posn, pid);   // a dále objektu typu Board
    if (!isDisabled) {
      currPlayer = otherPlayer( currPlayer );    // tah hráče je dokončen
      if (currPlayer == playerID)  // nyní je na tahu tento hráč
        setStatus("Jsi na tahu");
      else    // tah toho druhého hráče
        setStatus("Na tahu je hráč " + currPlayer);
    }
  }  // konec metody: doMove()



  private int otherPlayer(int id)
  // Předpokládáme, že máme pouze dva hráče
  { int otherID = ((id == PLAYER1) ? PLAYER2 : PLAYER1 );
    return otherID;
  } 


  synchronized public void disable(String msg)
  /* Odpojí ze sítě, ale neukončí program.
     Další tahy nejsou možné, ovšem s 3D-obrázkem lze otáčet.
 
     Voláno z: uzavíracího tlačítka okna, vlákna FBFWatcher 
     a objektu typu Board (přes metodu gameWon()).
  */
  { if (!isDisabled) {    // the klienta lze vyřadit pouze jednou
      try {
        isDisabled = true;
        out.println("disconnect");  // informuj server
        sock.close();
        setStatus("Hra skončila: " + msg);
        // System.out.println("Vyřazen: " + msg);
      }
      catch(Exception e)
      {  System.out.println( e );  }
    }
  }  // konec metody: disable()


  public void gameWon(int pid, int score)
  /* Volá se z metody reportWinner() třídy Board pro oznámení vítězství
     hráče pid s dosaženým počtem bodů score. */
  {
    if (pid == playerID)   // tento klient vyhrál
      disable("Vyhrál jsi s počtem bodů: " + score);
    else 
      disable("Hráč " + pid + " vyhrál s počtem bodů: " + score);
  }  // konec metody: gameWon()


  synchronized public void setStatus(String msg)
  { // System.out.println("Status: " + msg);
    status = msg;
  }

  synchronized public String getStatus()
  // pravidelně volaná metoda ze třídy OverlayCanvas pro aktualizaci zobrazení
  {  return status; }


  // -------------------------------------------------
  // následující metody jsou volány pouze z vlákna FBFWatcher


  public void addmlayer()
  // přidej druhého hráče
  { numPlayers++; 
    if (numPlayers == MAX_PLAYERS) {   // je čas zahájit hru
      if (currPlayer == playerID)  // tento hráč začíná
        setStatus("Please start");
      else    // začíná druhý hráč
        setStatus("Hráč " + currPlayer + " zahajuje hru");
    }
  } // konec metody: addmlayer()


  public void removePlayer()
  // odeber druhého hráče
  { numPlayers--;
    if (numPlayers < MAX_PLAYERS)
      disable("Hráč " + otherPlayer(playerID) + " odešel");    // deaktivuj klienta
  } // konec metody: removePlayer()


  public void setPlayerID(int id)
  /* Přiřadí tomuto hráči dané id.
     Aktualizuje stavový řádek okna.
     Id je také číslo aktuálního hráče, takže
     může být použito ke kontrole, zda-li může být hra zahájena.
  */
  { System.out.println("ID mého hráče: " + id);
    playerID = id;
    if (playerID == PLAYER1)
      setTitle("Piškvorky po síti: Hráč 1 (červené kuličky)");
    else   // PLAYER2
      setTitle("Piškvorky po síti: Hráč 2 (modré kostičky)");

    numPlayers = id; 
    if (numPlayers == MAX_PLAYERS)
      setStatus("Hru zahajuje hráč 1");
    else if (numPlayers < MAX_PLAYERS)
      setStatus("Čekám na hráče " + otherPlayer(playerID) );
  } // konec metody: setPlayerID()

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

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

} // konec třídy: NetFourByFour
