package fr.univ.lyon1.gui.controller;

import fr.univ.lyon1.common.ServerConfiguration;
import fr.univ.lyon1.gui.Dialog;
import fr.univ.lyon1.gui.handlers.ApplicationHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.net.Socket;

/**
 * Controller used by interface resources/connect_gui.fxml
 */
public class ConnectGuiController {
    @FXML
    public TextField addressTextField;
    @FXML
    public TextField portTextField;
    @FXML
    public TextField pseudoTextField;
    @FXML
    public TextField passwordTextField;
    @FXML
    public Button connectButton;

    /**
     * Called by connect button
     */
    public void connect() {
        this.connectButton.setDisable(true);

        @NotNull
        final String address = this.addressTextField.getText();
        @NotNull
        final String port = this.portTextField.getText();
        @NotNull
        final String pseudo = this.pseudoTextField.getText();
        @NotNull
        final String password = this.passwordTextField.getText();

        System.out.println("Checking if all text fields is not empty");
        // If all fields is not empty
        if (!address.isEmpty() && !port.isEmpty() && !pseudo.isEmpty() && !password.isEmpty()) {
            System.out.println("Checking if port is valid...");
            // Check if the port is a number and if it's an available port
            if (this.isNumber(port) && this.isAvailablePort(Integer.parseInt(port))) {
                final int iPort = Integer.parseInt(port);
                System.out.println("Port valid, next step...");
                System.out.println("Checking if connection is available...");

                // Check if the connection is available
                if (this.isAccessible(address, iPort)) {
                    System.out.println("Connection available, saving file...");

                    try {
                        // Save server configuration
                        new ServerConfiguration(address, iPort, pseudo, password).save();
                        Dialog.showSuccessDialog("Connecté", "Vous êtes bien connecté au serveur");
                        System.out.println("File saved, next step...");

                        // Change scene for the main application
                        @NotNull
                        final Stage stage = (Stage) this.connectButton.getScene().getWindow();
                        new ApplicationHandler().launch(stage);
                    } catch (IOException e) {
                        System.out.println(e.getMessage());

                        Dialog.showErrorDialog("Erreur", "Impossible de sauvegarder les informations de connexion au serveur");
                        this.connectButton.setDisable(false);
                        System.out.println("Failed to save file, error: " + e.getMessage());
                    }
                } else {
                    System.out.println("Connection not available");
                    Dialog.showErrorDialog("Erreur de connexion", "Impossible de se connecter au serveur, veuillez vérifier les informations saisies");
                    this.connectButton.setDisable(false);
                }
            } else {
                Dialog.showErrorDialog("Erreur", "Veuillez saisir un numéro de port valide");
                this.connectButton.setDisable(false);
            }
        } else {
            Dialog.showErrorDialog("Erreur", "Veuillez renseigner tout les champs");
            this.connectButton.setDisable(false);
        }
    }

    /**
     * Check if a string is a number
     * @param text String to check
     * @return Is number
     */
    private boolean isNumber(String text) {
        try {
            Integer.parseInt(text);
            return true;
        } catch (NumberFormatException ignored) {
            return false;
        }
    }

    /**
     * Check if a port is in correct range of ports
     * @param port Port to check
     * @return If port is available
     */
    private boolean isAvailablePort(int port) {
        return !(port < 0 || port > 0xFFFF);
    }

    /**
     * Check if an address with port is available through a socket
     * @param address Address of the server
     * @param port Port used by the server
     * @return If socket has successfully connected to the server
     */
    private boolean isAccessible(String address, int port) {
        try {
            @NotNull
            final Socket socket = new Socket(address, port);
            socket.setSoTimeout(5 * 1000);
            boolean connected = socket.isConnected();
            socket.close();

            return connected;
        } catch (IOException exception) {
            return false;
        }
    }
}