////////////////////////////////////////
// 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.math.Vector2f;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import pp.battleship.client.BattleshipAppState;

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

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

/**
 * EditorState manages the editor mode in the Battleship game,
 * allowing players to place and rotate ships.
 */
public class EditorAppState extends BattleshipAppState {
    private static final Logger LOGGER = System.getLogger(EditorAppState.class.getName());
    private static final float DEPTH = 0f;
    private static final float GAP = 20f;

    private final ActionListener clickListener = (name, isPressed, tpf) -> click(isPressed);
    private final Node editorNode = new Node("Editor"); //NON-NLS
    private Container buttonContainer;
    private Button rotateButton;
    private Button readyButton;
    private MapView ownMapView;
    private MapView harborView;
    private Vector2f oldCursorPosition;

    /**
     * Enables the editor state by attaching necessary nodes and listeners.
     */
    @Override
    protected void enableState() {
        editorNode.detachAllChildren();
        initializeGuiComponents();
        addGuiComponents();
        layoutGuiComponents();
        getApp().getGuiNode().attachChild(editorNode);
        getApp().getInputManager().addListener(clickListener, CLICK);
    }

    /**
     * Disables the editor state by detaching nodes and removing listeners.
     */
    @Override
    protected void disableState() {
        getApp().getGuiNode().detachChild(editorNode);
        getApp().getInputManager().removeListener(clickListener);
        if (ownMapView != null) {
            ownMapView.unregister();
            ownMapView = null;
        }
        if (harborView != null) {
            harborView.unregister();
            harborView = null;
        }
    }

    /**
     * Updates the editor state, handling cursor movement and enabling buttons.
     *
     * @param tpf Time per frame
     */
    @Override
    public void update(float tpf) {
        super.update(tpf);
        cursorMovement();
        enableButtons();
    }

    /**
     * Enables or disables buttons based on the logic state.
     */
    private void enableButtons() {
        readyButton.setEnabled(getGameLogic().isMapComplete());
        rotateButton.setEnabled(getGameLogic().movingShip());
    }

    /**
     * Handles cursor movement for previewing ship placement.
     */
    private void cursorMovement() {
        if (!getGameLogic().movingShip() || ownMapView == null || showsDialog())
            return;
        final Vector2f cursorPosition = getApp().getInputManager().getCursorPosition();
        if (!cursorPosition.equals(oldCursorPosition)) {
            oldCursorPosition = new Vector2f(cursorPosition);
            getGameLogic().movePreview(ownMapView.mouseToModel(cursorPosition));
        }
    }

    /**
     * Initializes the GUI components for the editor.
     */
    private void initializeGuiComponents() {
        ownMapView = new MapView(getGameLogic().getOwnMap(), getApp());
        harborView = new MapView(getGameLogic().getHarbor(), getApp());
        ownMapView.addGrid();
        rotateButton = new Button(lookup("button.rotate"));
        readyButton = new Button(lookup("button.ready"));
        rotateButton.addClickCommands(e -> {
            if (!showsDialog())
                getGameLogic().rotateShip();
        });
        readyButton.addClickCommands(e -> {
            if (!showsDialog())
                getGameLogic().mapFinished();
        });
        buttonContainer = new Container();
    }

    /**
     * Adds the GUI components to the editor node.
     */
    private void addGuiComponents() {
        buttonContainer.addChild(rotateButton, 0, 0);
        buttonContainer.addChild(readyButton, 0, 1);
        editorNode.attachChild(ownMapView.getNode());
        editorNode.attachChild(harborView.getNode());
        editorNode.attachChild(buttonContainer);
    }

    /**
     * Lays out the GUI components on the screen.
     */
    private void layoutGuiComponents() {
        final AppSettings s = getApp().getContext().getSettings();
        final float harborWidth = harborView.getWidth();
        final float harborHeight = harborView.getHeight();
        final float ownMapWidth = ownMapView.getWidth();
        final float ownMapHeight = ownMapView.getHeight();
        final float windowWidth = s.getWidth();
        final float windowHeight = s.getHeight();

        ownMapView.getNode()
                  .setLocalTranslation(0.5f * (windowWidth - harborWidth - ownMapWidth - GAP),
                                       0.5f * (windowHeight - ownMapHeight),
                                       DEPTH);

        harborView.getNode()
                  .setLocalTranslation(0.5f * (windowWidth - harborWidth + ownMapWidth + GAP),
                                       0.5f * (windowHeight - harborHeight),
                                       DEPTH);

        buttonContainer.setLocalTranslation(0.5f * (windowWidth - harborWidth - ownMapWidth - GAP),
                                            0.5f * (windowHeight - ownMapHeight - GAP),
                                            DEPTH);
    }

    /**
     * Handles click events to place or rotate ships on the maps.
     */
    private void click(boolean isPressed) {
        if (!isPressed || showsDialog()) return;
        final Vector2f cursorPos = getApp().getInputManager().getCursorPosition();
        LOGGER.log(Level.DEBUG, "click: {0}", cursorPos); //NON-NLS
        getGameLogic().clickHarbor(harborView.mouseToModel(cursorPos));
        getGameLogic().clickOwnMap(ownMapView.mouseToModel(cursorPos));
    }
}
