package visualiser.Controllers;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import network.Messages.HostGame;
import visualiser.app.MatchBrowserSingleton;
import visualiser.model.RaceConnection;
import visualiser.network.MatchBrowserLobbyInterface;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Observable;
import java.util.Observer;

/**
 * Controller for the Lobby for entering games
 */
public class LobbyController extends Controller {
    private @FXML TableView<RaceConnection> lobbyTable;
    private @FXML TableColumn<RaceConnection, String> gameNameColumn;
    private @FXML TableColumn<RaceConnection, String> hostNameColumn;
    private @FXML TableColumn<RaceConnection, String> statusColumn;
    private @FXML Button joinGameBtn;
    private @FXML TextField addressFld;
    private @FXML TextField portFld;

    private ObservableList<RaceConnection> connections;
    private ObservableList<RaceConnection> customConnections;

    //the socket for match browser
    private DatagramSocket udpSocket;
    private MatchBrowserLobbyInterface matchBrowserLobbyInterface;


    public void initialize() {
        // set up the connection table
        connections = FXCollections.observableArrayList();
        customConnections = FXCollections.observableArrayList();
        //connections.add(new RaceConnection("localhost", 4942, "Local Game"));

        lobbyTable.setItems(connections);
        gameNameColumn.setCellValueFactory(cellData -> cellData.getValue().gamenameProperty());
        hostNameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty());
        statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty());

        lobbyTable.getSelectionModel().selectedItemProperty().addListener((obs, prev, curr) -> {
            if (curr != null && curr.statusProperty().getValue().equals("Ready")) {
                joinGameBtn.setDisable(false);
            } else {
                joinGameBtn.setDisable(true);
            }
        });
        joinGameBtn.setDisable(true);

        this.udpSocket = MatchBrowserSingleton.getInstance().getUdpSocket();
        receiveMatchData();
    }

    /**
     * Refreshes the connections in the lobby
     */
    public void refreshBtnPressed(){
        addServerGames();
        for(RaceConnection connection: connections) {
            connection.check();
        }
        try {
            if (lobbyTable.getSelectionModel().getSelectedItem().statusProperty().getValue().equals("Ready")) {
                joinGameBtn.setDisable(false);
            } else {
                joinGameBtn.setDisable(true);
            }
        } catch (Exception ignored){}
    }

    /**
     * Connect to a connection.
     * @throws IOException socket error
     */
    public void connectSocket() throws IOException {
        RaceConnection connection = lobbyTable.getSelectionModel().getSelectedItem();
        Socket socket = new Socket(connection.getHostname(), connection.getPort());
        InGameLobbyController iglc = (InGameLobbyController)loadScene("gameLobby.fxml");
        iglc.enterGameLobby(socket, false);
    }

    public void menuBtnPressed() throws IOException {
        matchBrowserLobbyInterface.closeSocket();
        loadScene("title.fxml");
    }

    /**
     * adds a new connection
     */
    public void addConnectionPressed(){
        String hostName = addressFld.getText();
        String portString = portFld.getText();
        try {
            int port = Integer.parseInt(portString);
            customConnections.add(new RaceConnection(hostName, port, "Boat Game"));
            connections.addAll(customConnections);
            addressFld.clear();
            portFld.clear();
        } catch (NumberFormatException e) {
            System.err.println("Port number entered is not a number");
        }
    }

    public void receiveMatchData(){
        matchBrowserLobbyInterface = new MatchBrowserLobbyInterface();
        try {
            matchBrowserLobbyInterface.startReceivingHostData(new DatagramSocket(4941));
            Observer o = new Observer() {
                @Override
                public void update(Observable o, Object arg) {
                    refreshBtnPressed();
                }
            };
            matchBrowserLobbyInterface.addObserver(o);
        } catch (SocketException e) {
            System.err.println("Socket 4941 in use");
        }
    }

    /**
     * Adds the games received from the server
     */
    private void addServerGames() {
        connections.clear();
        connections.addAll(customConnections);
        for (HostGame game : matchBrowserLobbyInterface.getGames()) {
            connections.add(new RaceConnection(game.getIp(), 4942, "Boat Game"));
        }
    }
}
