import java.util.ArrayList;
import java.util.concurrent.ThreadLocalRandom;

/**
 * A creature that moves and reproduces. All prey start life with a single unit
 * of energy. Food is abundant, so energy is never lost. Prey dies only when
 * eaten by a predator or when it reaches its age limit.
 *
 * @author Drue Coles
 */
public class Prey extends Creature {

    // All prey have the same age limit and fertility threshold. When 
    // reproduction is possible, it occurs with a fixed probability. Default
    // values for these parameters can be modified using static setters.
    private static int ageLimit = 8;
    private static int fertilityThreshold = 3;
    private static double breedingProb = 0.50;

    /**
     * Sets age limit for this class to a given positive integer. Non-positive
     * values are ignored.
     */
    public static void setAgeLimit(int ageLimit) {
        Prey.ageLimit = ageLimit;
    }

    /**
     * Sets fertility threshold for this class to a given positive integer.
     * Non-positive values are ignored.
     */
    public static void setFertilityThreshold(int fertilityThreshold) {
        Prey.fertilityThreshold = fertilityThreshold;
    }

    /**
     * Sets breeding probability for this class to a given floating-point value
     * between 0 and 1 inclusive. Values outside this range are ignored.
     */
    public static void setBreedingProb(double prob) {
        if (prob >= 0 && prob <= 1) {
            Prey.breedingProb = prob;
        }
    }

    /**
     * Creates new prey with a single unit of energy.
     */
    public Prey() {
        super(1);
    }

    /**
     * Returns true if this prey has exceeded its age limit.
     */
    @Override
    public boolean isDead() {
        return age > ageLimit;
    }

    /**
     * Creates and returns a child if this prey is alive and sufficiently
     * fertile (otherwise returns null).
     */
    @Override
    public Prey reproduce() {
        if (isDead() || fertility < fertilityThreshold) {
            return null;
        }

        // Reproduces with fixed probability.
        ThreadLocalRandom rand = ThreadLocalRandom.current();
        if (rand.nextDouble() < breedingProb) {
            fertility = 0; // After reproducing, fertility clock is reset.
            return new Prey();
        }
        return null;
    }

    /**
     * Returns a randomly selected direction to an unoccupied adjacent cell if
     * one exists (or null if all adjacent cells are occupied).
     */
    @Override
    public Direction move(Creature[] neighbors) {

        if (isDead()) {
            return null;
        }

        // Whenever prey attempts to move, it ages by one unit and its fertility 
        // clock increases by one unit.
        age++;
        fertility++;

        ArrayList<Direction> directionsToUnoccupied = new ArrayList<>();

        // For each direction, check if the adjacent cell in that direction is 
        // empty. 
         for (Direction dir : Direction.values()) {
            if (neighbors[dir.index()] == null) {
                directionsToUnoccupied.add(dir);
            }
        }

        // If the list of directions to unoccupied cells is not empty, choose a 
        // direction at random from this list. 
        if (!directionsToUnoccupied.isEmpty()) {
            ThreadLocalRandom rand = ThreadLocalRandom.current();
            return directionsToUnoccupied.get(rand.nextInt(directionsToUnoccupied.size()));
        }

        // If all four neighoring cells are occupied, no movement is possible.
        return null;
    }
}