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

package pp.battleship.message.server;

import com.jme3.network.serializing.Serializable;
import pp.battleship.model.Battleship;
import pp.battleship.model.IntPoint;
import pp.battleship.model.Shot;

import java.util.Collections;
import java.util.List;

import static pp.util.Util.copy;

/**
 * A message sent by the server to inform clients about the effects of a shot in the Battleship game.
 */
@Serializable
public class EffectMessage extends ServerMessage {
    private boolean ownShot;
    private Shot shot;
    private Battleship destroyedShip;
    private List<Battleship> remainingOpponentShips;

    /**
     * Creates an EffectMessage indicating a hit.
     *
     * @param ownShot true if the shot was fired by the player, false if by the opponent
     * @param pos     the position of the shot
     * @return an EffectMessage indicating a hit
     */
    public static EffectMessage hit(boolean ownShot, IntPoint pos) {
        return new EffectMessage(ownShot, new Shot(pos, true), null, null);
    }

    /**
     * Creates an EffectMessage indicating a miss.
     *
     * @param ownShot true if the shot was fired by the player, false if by the opponent
     * @param pos     the position of the shot
     * @return an EffectMessage indicating a miss
     */
    public static EffectMessage miss(boolean ownShot, IntPoint pos) {
        return new EffectMessage(ownShot, new Shot(pos, false), null, null);
    }

    /**
     * Creates an EffectMessage indicating a ship was destroyed.
     *
     * @param ownShot       true if the shot was fired by the player, false if by the opponent
     * @param pos           the position of the shot
     * @param destroyedShip the ship that was destroyed
     * @return an EffectMessage indicating a ship was destroyed
     */
    public static EffectMessage shipDestroyed(boolean ownShot, IntPoint pos, Battleship destroyedShip) {
        return new EffectMessage(ownShot, new Shot(pos, true), destroyedShip, null);
    }

    /**
     * Creates an EffectMessage indicating the player has won the game.
     *
     * @param pos           the position of the shot
     * @param destroyedShip the ship that was destroyed
     * @return an EffectMessage indicating the player has won
     */
    public static EffectMessage won(IntPoint pos, Battleship destroyedShip) {
        return new EffectMessage(true, new Shot(pos, true), destroyedShip, Collections.emptyList());
    }

    /**
     * Creates an EffectMessage indicating the player has lost the game.
     *
     * @param pos                    the position of the shot
     * @param destroyedShip          the ship that was destroyed
     * @param remainingOpponentShips the list of opponent's remaining ships
     * @return an EffectMessage indicating the player has lost
     */
    public static EffectMessage lost(IntPoint pos, Battleship destroyedShip, List<Battleship> remainingOpponentShips) {
        return new EffectMessage(false, new Shot(pos, true), destroyedShip, remainingOpponentShips);
    }

    /**
     * Default constructor for serialization purposes.
     */
    private EffectMessage() { /* empty */ }

    /**
     * Constructs an EffectMessage with the specified parameters.
     *
     * @param ownShot                true if the shot was fired by the player, false if by the opponent
     * @param shot                   the shot fired
     * @param destroyedShip          the ship that was destroyed by the shot, null if no ship was destroyed
     * @param remainingOpponentShips the list of opponent's remaining ships after the shot
     */
    private EffectMessage(boolean ownShot, Shot shot, Battleship destroyedShip, List<Battleship> remainingOpponentShips) {
        this.ownShot = ownShot;
        this.shot = shot;
        this.destroyedShip = destroyedShip;
        this.remainingOpponentShips = copy(remainingOpponentShips);
    }

    /**
     * Accepts a visitor to process this message.
     *
     * @param interpreter the visitor to process this message
     */
    @Override
    public void accept(ServerInterpreter interpreter) {
        interpreter.received(this);
    }

    /**
     * Checks if the shot was fired by the player.
     *
     * @return true if the shot was fired by the player, false otherwise
     */
    public boolean isOwnShot() {
        return ownShot;
    }

    /**
     * Returns the shot fired.
     *
     * @return the shot fired
     */
    public Shot getShot() {
        return shot;
    }

    /**
     * Returns the ship that was destroyed by the shot.
     *
     * @return the destroyed ship, null if no ship was destroyed
     */
    public Battleship getDestroyedShip() {
        return destroyedShip;
    }

    /**
     * Returns the list of opponent's remaining ships after the shot.
     *
     * @return the list of opponent's remaining ships, null if the game is not yet over
     */
    public List<Battleship> getRemainingOpponentShips() {
        return remainingOpponentShips;
    }

    /**
     * Checks if the game is over.
     *
     * @return true if the game is over, false otherwise
     */
    public boolean isGameOver() {
        return remainingOpponentShips != null;
    }

    /**
     * Checks if the game is won by the player.
     *
     * @return true if the game is won by the player, false otherwise
     */
    public boolean isGameWon() {
        return isGameOver() && isOwnShot();
    }

    /**
     * Checks if the game is lost by the player.
     *
     * @return true if the game is lost by the player, false otherwise
     */
    public boolean isGameLost() {
        return isGameOver() && !isOwnShot();
    }

    /**
     * Checks if it's currently the player's turn.
     *
     * @return true if it's the player's turn, false otherwise
     */
    public boolean isMyTurn() {
        return isOwnShot() == shot.isHit();
    }

    /**
     * Returns a string representation of the EffectMessage.
     *
     * @return a string representation of the EffectMessage
     */
    @Override
    public String toString() {
        return "EffectMessage{ownShot=" + ownShot + ", shot=" + shot + ", destroyedShip=" + destroyedShip + //NON-NLS
               ", remainingOpponentShips=" + remainingOpponentShips + '}'; //NON-NLS
    }

    /**
     * Returns the key for the informational text associated with this message.
     *
     * @return the key for the informational text
     */
    @Override
    public String getInfoTextKey() {
        if (isGameOver())
            return isGameWon() ? "you.won.the.game" : "you.lost.the.game";
        return isMyTurn() ? "its.your.turn" : "wait.for.opponent";
    }
}
