import java.io.*;
import java.net.*;
import java.util.Date;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

public class MultiThreadServer extends Application {
  // Obszar tekstowy do wyświetlania zawartości
  private TextArea ta = new TextArea();
  
  // Numerowanie klientów
  private int clientNo = 0;

  @Override // Przesłanianie metody start w klasie Application
  public void start(Stage primaryStage) {
    // Tworzenie sceny i umieszczanie jej w oknie
    Scene scene = new Scene(new ScrollPane(ta), 450, 200);
    primaryStage.setTitle("MultiThreadServer"); // Ustawianie nagłówka okna
    primaryStage.setScene(scene); // Umieszczanie sceny w oknie
    primaryStage.show(); // Wyświetlanie okna

    new Thread( () -> {
      try {
        // Tworzenie gniazda serwera
        ServerSocket serverSocket = new ServerSocket(8000);
        ta.appendText("Czas uruchomienia serwera MultiThreadServer: " 
          + new Date() + '\n');
    
        while (true) {
          // Oczekiwanie na żądanie nawiązania nowego połączenia
          Socket socket = serverSocket.accept();
    
          // Zwiększanie wartości zmiennej clientNo
          clientNo++;
          
          Platform.runLater( () -> {
            // Wyświetlanie numeru klienta
            ta.appendText("Uruchamianie wątku dla klienta nr " + clientNo +
              ". Czas: " + new Date() + '\n');

            // Pobieranie nazwy i adresu IP hosta klienta
            InetAddress inetAddress = socket.getInetAddress();
            ta.appendText("Klient nr " + clientNo + ". Nazwa hosta: "
              + inetAddress.getHostName() + "\n");
            ta.appendText("Klient nr " + clientNo + ". Adres IP: "
              + inetAddress.getHostAddress() + "\n");
          });
          
          // Tworzenie i uruchamianie nowego wątku na potrzeby połączenia
          new Thread(new HandleAClient(socket)).start();
        }
      }
      catch(IOException ex) {
        System.err.println(ex);
      }
    }).start();
  }
  
  // Definiowanie klasy wątku do obsługi nowego połączenia
  class HandleAClient implements Runnable {
    private Socket socket; // Gniazdo połączenia

    /** Tworzenie wątku */
    public HandleAClient(Socket socket) {
      this.socket = socket;
    }

    /** Uruchamianie wątku */
    public void run() {
      try {
        // Tworzenie strumieni wejściowego i wyjściowego
        DataInputStream inputFromClient = new DataInputStream(
          socket.getInputStream());
        DataOutputStream outputToClient = new DataOutputStream(
          socket.getOutputStream());

        // Stała obsługa klienta
        while (true) {
          // Pobieranie promienia od klienta
          double radius = inputFromClient.readDouble();

          // Obliczanie powierzchni
          double area = radius * radius * Math.PI;

          // Odsyłanie powierzchni do klienta
          outputToClient.writeDouble(area);
          
          Platform.runLater(() -> {
            ta.appendText("Promień od klienta: " +
              radius + '\n');
            ta.appendText("Powierzchnia: " + area + '\n');
          });
        }
      }
      catch(IOException ex) {
        ex.printStackTrace();
      }
    }
  }
  
  /**
   * Metoda main jest potrzebna tylko w środowiskach IDE z ograniczoną obsługą platformy JavaFX.
   * Nie jest potrzebna przy uruchamianiu kodu w wierszu poleceń.
   */
  public static void main(String[] args) {
    launch(args);
  }
}
