////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////

package pp.battleship.client;

import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.dialog.Dialog;
import pp.dialog.DialogBuilder;
import pp.dialog.SimpleDialog;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import static pp.battleship.Resources.lookup;

/**
 * Represents a dialog for setting up a network connection in the Battleship game.
 * Allows users to specify the host and port for connecting to a game server.
 */
class NetworkDialog extends SimpleDialog {
    private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName());
    private static final String LOCALHOST = "localhost"; //NON-NLS
    private static final String DEFAULT_PORT = "1234"; //NON-NLS
    private final NetworkSupport network;
    private final TextField host = new TextField(LOCALHOST);
    private final TextField port = new TextField(DEFAULT_PORT);
    private String hostname;
    private int portNumber;
    private Future<Object> connectionFuture;
    private Dialog progressDialog;

    /**
     * Constructs a new NetworkDialog.
     *
     * @param network The NetworkSupport instance to be used for network operations.
     */
    NetworkDialog(NetworkSupport network) {
        super(network.getApp().getDialogManager());
        this.network = network;
        host.setSingleLine(true);
        host.setPreferredWidth(400f);
        port.setSingleLine(true);

        final BattleshipApp app = network.getApp();
        final Container input = new Container(new SpringGridLayout());
        input.addChild(new Label(lookup("host.name") + ":  "));
        input.addChild(host, 1);
        input.addChild(new Label(lookup("port.number") + ":  "));
        input.addChild(port, 1);

        DialogBuilder.simple(app.getDialogManager())
                     .setTitle(lookup("server.dialog"))
                     .setExtension(d -> d.addChild(input))
                     .setOkButton(lookup("button.connect"), d -> connect())
                     .setNoButton(lookup("button.cancel"), app::closeApp)
                     .setOkClose(false)
                     .setNoClose(false)
                     .build(this);
    }

    /**
     * Handles the action for the connect button in the connection dialog.
     * Tries to parse the port number and initiate connection to the server.
     */
    private void connect() {
        LOGGER.log(Level.INFO, "connect to host={0}, port={1}", host, port); //NON-NLS
        try {
            hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
            portNumber = Integer.parseInt(port.getText());
            openProgressDialog();
            connectionFuture = network.getApp().getExecutor().submit(this::initNetwork);
        }
        catch (NumberFormatException e) {
            network.getApp().errorDialog(lookup("port.must.be.integer"));
        }
    }

    /**
     * Creates a dialog indicating that the connection is in progress.
     */
    private void openProgressDialog() {
        progressDialog = DialogBuilder.simple(network.getApp().getDialogManager())
                                      .setText(lookup("label.connecting"))
                                      .build();
        progressDialog.open();
    }

    /**
     * Tries to initialize the network connection.
     *
     * @throws RuntimeException If an error occurs when creating the client.
     */
    private Object initNetwork() {
        try {
            network.initNetwork(hostname, portNumber);
            return null;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * This method is called by {@linkplain pp.dialog.DialogManager#update(float)} for periodically
     * updating this dialog. T
     */
    @Override
    public void update(float delta) {
        if (connectionFuture != null && connectionFuture.isDone())
            try {
                connectionFuture.get();
                success();
            }
            catch (ExecutionException e) {
                failure(e.getCause());
            }
            catch (InterruptedException e) {
                LOGGER.log(Level.WARNING, "Interrupted!", e); //NON-NLS
                Thread.currentThread().interrupt();
            }
    }

    /**
     * Handles a successful connection to the game server.
     */
    private void success() {
        connectionFuture = null;
        progressDialog.close();
        this.close();
        network.getApp().setInfoText(lookup("wait.for.an.opponent"));
    }

    /**
     * Handles a failed connection attempt.
     *
     * @param e The cause of the failure.
     */
    private void failure(Throwable e) {
        connectionFuture = null;
        progressDialog.close();
        network.getApp().errorDialog(lookup("server.connection.failed"));
        network.getApp().setInfoText(e.getLocalizedMessage());
    }
}
