package pp.battleship.game.client;

import pp.battleship.message.client.AnimationFinishedMessage;
import pp.battleship.message.server.EffectMessage;
import pp.battleship.model.Battleship;
import pp.battleship.model.Shell;
import pp.battleship.model.ShipMap;
import pp.battleship.notification.Sound;

/**
 * Represents the shooting state of the game where a shell is fired at the opponent.
 */
public class ShootingState extends ClientState {
    private float shootValue;
    private final static float SHELL_SPEED = 0.3f;
    private final Shell shell;
    private final boolean myTurn;
    private final EffectMessage msg;

    /**
     * Constructs a shooting state with the specified game logic.
     *
     * @param logic  the game logic
     * @param shell  the shell being shot
     * @param myTurn indicates if it is the player's turn
     * @param msg    the effect message associated with the shooting action
     */
    public ShootingState(ClientGameLogic logic, Shell shell, boolean myTurn, EffectMessage msg) {
        super(logic);
        this.msg = msg;
        this.myTurn = myTurn;
        this.shell = shell;
        this.shootValue = 0;
        shell.move(shootValue);
    }

    @Override
    public boolean showBattle() {
        return true;
    }

    /**
     * Updates the shooting state by moving the shell based on the elapsed time.
     *
     * @param delta the time in seconds since the last update
     */
    @Override
    void update(float delta) {
        super.update(delta);
        if (shootValue > 1) {
            endState();
        }
        else {
            shootValue += delta * SHELL_SPEED;
            shell.move(shootValue);
        }
    }

    /**
     * Ends the shooting state and processes the effects of the shot.
     */
    private void endState() {
        playSound(msg);
        affectedMap(msg).add(msg.getShot());
        affectedMap(msg).remove(shell);

        if (destroyedOpponentShip(msg))
            logic.getOpponentMap().add(msg.getDestroyedShip());
        if (msg.isGameOver()) {
            for (Battleship ship : msg.getRemainingOpponentShips()) {
                logic.getOpponentMap().add(ship);
            }
            logic.setState(new GameOverState(logic));
            return;
        }
        logic.send(new AnimationFinishedMessage());
        logic.setState(new BattleState(logic, myTurn));
    }

    /**
     * Checks if an opponent's ship was destroyed by the shot.
     *
     * @param msg the effect message containing the shot details
     * @return true if an opponent's ship was destroyed, false otherwise
     */
    private boolean destroyedOpponentShip(EffectMessage msg) {
        return msg.getDestroyedShip() != null && msg.isOwnShot();
    }

    /**
     * Retrieves the affected map based on whether the shot was owned by the player or the opponent.
     *
     * @param msg the effect message containing shot details
     * @return the ShipMap that was affected by the shot
     */
    private ShipMap affectedMap(EffectMessage msg) {
        return msg.isOwnShot() ? logic.getOpponentMap() : logic.getOwnMap();
    }

    /**
     * Plays a sound based on the outcome of the shot. Different sounds are played for a miss, hit,
     * or destruction of a ship.
     *
     * @param msg the effect message containing the result of the shot
     */
    private void playSound(EffectMessage msg) {
        if (!msg.getShot().isHit())
            logic.playSound(Sound.SPLASH);
        else if (msg.getDestroyedShip() == null)
            logic.playSound(Sound.EXPLOSION);
        else
            logic.playSound(Sound.DESTROYED_SHIP);
    }
}
