////////////////////////////////////////
// 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.Button;
import com.simsilica.lemur.Checkbox;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.dialog.StateCheckboxModel;
import pp.dialog.TextInputDialog;

import java.io.File;
import java.io.IOException;
import java.util.prefs.Preferences;

import static pp.battleship.Resources.lookup;
import static pp.util.PreferencesUtils.getPreferences;

/**
 * The Menu class represents the main menu in the Battleship game application.
 * It extends the Dialog class and provides functionalities for loading, saving,
 * returning to the game, and quitting the application.
 */
class Menu extends Dialog {
    private static final Preferences PREFERENCES = getPreferences(Menu.class);
    private static final String LAST_PATH = "last.file.path";
    private final BattleshipApp app;
    private final Button loadButton = new Button(lookup("menu.map.load"));
    private final Button saveButton = new Button(lookup("menu.map.save"));

    /**
     * Constructs the Menu dialog for the Battleship application.
     *
     * @param app the BattleshipApp instance
     */
    public Menu(BattleshipApp app) {
        super(app.getDialogManager());
        this.app = app;
        addChild(new Label(lookup("battleship.name"), new ElementId("header"))); //NON-NLS
        addChild(new Checkbox(lookup("menu.sound-enabled"),
                              new StateCheckboxModel(app, GameSound.class)));
        addChild(loadButton)
                .addClickCommands(s -> ifTopDialog(this::loadDialog));
        addChild(saveButton)
                .addClickCommands(s -> ifTopDialog(this::saveDialog));
        addChild(new Button(lookup("menu.return-to-game")))
                .addClickCommands(s -> ifTopDialog(this::close));
        addChild(new Button(lookup("menu.quit")))
                .addClickCommands(s -> ifTopDialog(app::closeApp));
        update();
    }

    /**
     * Updates the state of the load and save buttons based on the game logic.
     */
    @Override
    public void update() {
        loadButton.setEnabled(app.getGameLogic().mayLoadMap());
        saveButton.setEnabled(app.getGameLogic().maySaveMap());
    }

    /**
     * As an escape action, this method closes the menu if it is the top dialog.
     */
    @Override
    public void escape() {
        close();
    }

    /**
     * Functional interface for file actions.
     */
    @FunctionalInterface
    private interface FileAction {
        /**
         * Executes a file action.
         *
         * @param file the file to be processed
         * @throws IOException if an I/O error occurs
         */
        void run(File file) throws IOException;
    }

    /**
     * Handles the file action for the provided dialog.
     *
     * @param fileAction the file action to be executed
     * @param dialog     the dialog providing the file input
     */
    private void handle(FileAction fileAction, TextInputDialog dialog) {
        try {
            final String path = dialog.getInput().getText();
            PREFERENCES.put(LAST_PATH, path);
            fileAction.run(new File(path));
            dialog.close();
        }
        catch (IOException e) {
            app.errorDialog(e.getLocalizedMessage());
        }
    }

    /**
     * Shows a file dialog for loading or saving files.
     *
     * @param fileAction the action to perform with the selected file
     * @param label      the label for the dialog
     */
    private void fileDialog(FileAction fileAction, String label) {
        final TextInputDialog dialog =
                TextInputDialog.builder(app.getDialogManager())
                               .setLabel(lookup("label.file"))
                               .setFocus(TextInputDialog::getInput)
                               .setTitle(label)
                               .setOkButton(lookup("button.ok"), d -> handle(fileAction, d))
                               .setNoButton(lookup("button.cancel"))
                               .setOkClose(false)
                               .build();
        final String path = PREFERENCES.get(LAST_PATH, null);
        if (path != null)
            dialog.getInput().setText(path.trim());
        dialog.open();
    }

    /**
     * Shows the load dialog for loading maps.
     */
    private void loadDialog() {
        fileDialog(app.getGameLogic()::loadMap, lookup("menu.map.load"));
    }

    /**
     * Shows the save dialog for saving maps.
     */
    private void saveDialog() {
        fileDialog(app.getGameLogic()::saveMap, lookup("menu.map.save"));
    }
}
