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

package pp.battleship.client.gui;

import com.jme3.input.controls.ActionListener;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import pp.battleship.client.BattleshipAppState;
import pp.battleship.model.IntPoint;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;

import static pp.battleship.client.BattleshipApp.CLICK;

/**
 * Represents the state responsible for managing the battle interface within the Battleship game.
 * This state handles the display and interaction of the battle map, including the opponent's map.
 * It manages GUI components, input events, and the layout of the interface when this state is enabled.
 */
public class BattleAppState extends BattleshipAppState {
    private static final Logger LOGGER = System.getLogger(BattleAppState.class.getName());
    private static final float DEPTH = 0f;
    private static final float GAP = 20f;

    /**
     * A listener for handling click events in the battle interface.
     * When a click is detected, it triggers the corresponding actions on the opponent's map.
     */
    private final ActionListener clickListener = (name, isPressed, tpf) -> click(isPressed);

    /**
     * The root node for all GUI components in the battle state.
     */
    private final Node battleNode = new Node("Battle"); //NON-NLS

    /**
     * A view representing the opponent's map in the GUI.
     */
    private MapView opponentMapView;

    /**
     * Enables the battle state by initializing, laying out, and adding GUI components.
     * Attaches the components to the GUI node and registers input listeners.
     */
    @Override
    protected void enableState() {
        battleNode.detachAllChildren();
        initializeGuiComponents();
        layoutGuiComponents();
        addGuiComponents();
        getApp().getGuiNode().attachChild(battleNode);
        getApp().getInputManager().addListener(clickListener, CLICK);
    }

    /**
     * Disables the battle state by removing GUI components and unregistering input listeners.
     * Also handles cleanup of resources, such as the opponent's map view.
     */
    @Override
    protected void disableState() {
        getApp().getGuiNode().detachChild(battleNode);
        getApp().getInputManager().removeListener(clickListener);
        if (opponentMapView != null) {
            opponentMapView.unregister();
            opponentMapView = null;
        }
    }

    /**
     * Initializes the GUI components used in the battle state.
     * Creates the opponent's map view and adds a grid overlay to it.
     */
    private void initializeGuiComponents() {
        opponentMapView = new MapView(getGameLogic().getOpponentMap(), getApp());
        opponentMapView.addGrid();
    }

    /**
     * Adds the initialized GUI components to the battle node.
     * Currently, it attaches the opponent's map view to the node.
     */
    private void addGuiComponents() {
        battleNode.attachChild(opponentMapView.getNode());
    }

    /**
     * Lays out the GUI components within the window, positioning them appropriately.
     * The opponent's map view is positioned based on the window's dimensions and a specified gap.
     */
    private void layoutGuiComponents() {
        final AppSettings s = getApp().getContext().getSettings();
        final float mapWidth = opponentMapView.getWidth();
        final float mapHeight = opponentMapView.getHeight();
        final float windowWidth = s.getWidth();
        final float windowHeight = s.getHeight();

        opponentMapView.getNode().setLocalTranslation(windowWidth - mapWidth - GAP,
                                                      windowHeight - mapHeight - GAP,
                                                      DEPTH);
    }

    /**
     * Handles click events in the battle interface. If the event indicates a click (not a release),
     * it translates the cursor position to the model's coordinate system and triggers the game logic
     * for interacting with the opponent's map.
     *
     * @param isPressed whether the mouse button is currently pressed (true) or released (false)
     */
    private void click(boolean isPressed) {
        if (!isPressed || showsDialog())
            return;
        final IntPoint cursorPos = opponentMapView.mouseToModel(getApp().getInputManager().getCursorPosition());
        LOGGER.log(Level.DEBUG, "click: {0}", cursorPos); //NON-NLS
        getGameLogic().clickOpponentMap(cursorPos);
    }
}
