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

package pp.battleship.model;

import com.jme3.network.serializing.Serializable;

import java.util.Objects;

/**
 * Represents a shot in the Battleship game.
 * A shot is defined by its coordinates and whether it was a hit or miss.
 */
@Serializable
public final class Shot implements Item, IntPosition {
    private int x;
    private int y;
    private boolean hit;

    // Private no-arg constructor for serialization purposes
    private Shot() {}

    /**
     * Creates a new shot.
     *
     * @param x   the x-coordinate of the shot
     * @param y   the y-coordinate of the shot
     * @param hit indicates whether the shot was a hit
     */
    public Shot(int x, int y, boolean hit) {
        this.x = x;
        this.y = y;
        this.hit = hit;
    }

    /**
     * Creates a new shot.
     *
     * @param pos the position of the shot
     * @param hit indicates whether the shot was a hit
     */
    public Shot(IntPosition pos, boolean hit) {
        this(pos.getX(), pos.getY(), hit);
    }

    /**
     * Gets the x-coordinate of the shot.
     *
     * @return the x-coordinate of the shot
     */
    @Override
    public int getX() {
        return x;
    }

    /**
     * Gets the y-coordinate of the shot.
     *
     * @return the y-coordinate of the shot
     */
    @Override
    public int getY() {
        return y;
    }

    /**
     * Checks if the shot was a hit.
     *
     * @return true if the shot was a hit, false otherwise
     */
    public boolean isHit() {
        return hit;
    }

    /**
     * Checks if this shot is equal to another object.
     * Two shots are considered equal if they have the same coordinates and hit status.
     *
     * @param obj the object to compare with
     * @return true if the objects are equal, false otherwise
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null || obj.getClass() != this.getClass()) return false;
        var that = (Shot) obj;
        return this.x == that.x &&
               this.y == that.y &&
               this.hit == that.hit;
    }

    /**
     * Computes the hash code of this shot.
     *
     * @return the hash code of this shot
     */
    @Override
    public int hashCode() {
        return Objects.hash(x, y, hit);
    }

    /**
     * Returns a string representation of the shot.
     *
     * @return a string representation of the shot
     */
    @Override
    public String toString() {
        return "Shot[" + //NON-NLS
               "x=" + x + ", " + //NON-NLS
               "y=" + y + ", " + //NON-NLS
               "hit=" + hit + ']'; //NON-NLS
    }

    /**
     * Accepts a visitor with a return value.
     *
     * @param visitor the visitor to accept
     * @param <T>     the type of the return value
     * @return the result of the visitor's visit method
     */
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }

    /**
     * Accepts a visitor without a return value.
     *
     * @param visitor the visitor to accept
     */
    @Override
    public void accept(VoidVisitor visitor) {
        visitor.visit(this);
    }
}
