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

/**
 * Hunts for prey, breeds, and eventually dies (what a life).
 *
 * @author Drue Coles
 */
public class Predator extends Creature {

    // All predators have the same age limit and they start life at the same 
    // energy level. They also have the same fertility threshold. Default values for 
    // these parameters can be checked or modified using static getters and setters.
    private static int ageLimit = 9;
    private static int energyAtBirth = 4;
    private static int fertilityThreshold = 4;

    /**
     * Sets energy level for this class to a given positive integer.
     * Non-positive values are ignored.
     */
    public static void setEnergyAtBirth(int energy) {
        if (energy > 0) {
            Predator.energyAtBirth = energy;
        }
    }

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

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

    /**
     * Creates a new predator.
     */
    public Predator() {
        super(energyAtBirth);
    }

    /**
     * Returns true if this predator has died.
     */
    @Override
    public boolean isDead() {
        return energy < 1 || age > ageLimit;
    }

    /**
     * Returns a new predator if this one is able to reproduce, or null if not
     * able to reproduce.
     */
    @Override
    public Predator reproduce() {
        if (isDead() || fertility < fertilityThreshold) {
            return null;
        }

        // After reproducing, fertility clock is reset.
        fertility = 0;
        return new Predator();
    }

    /**
     * Returns preferred direction of movement according to the following rule:
     *
     * 1. If there is prey in at least one neighboring cell, choose at random
     * among directions leading to prey.
     *
     * 2. If there is no prey in neighboring cells, choose at random an empty
     * cell if one is available.
     *
     * 3. If there are no empty cells, then all neighbors are predators. In this
     * case, return null to signify no movement.
     */
    @Override
    public Direction move(Creature[] neighbors) {

        if (isDead()) {
            return null;
        }

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

        // lists of directions to unoccupied cells and to cells with prey
        ArrayList<Direction> directionsToUnoccupied = new ArrayList<>();
        ArrayList<Direction> directionsToPrey = new ArrayList<>();

        // For each direction, check if the adjacent cell in that direction is empty
        // or contains prey, and update lists accordingly. 
        for (Direction dir : Direction.values()) {
            if (neighbors[dir.index()] == null) {
                directionsToUnoccupied.add(dir);
            }
            if (neighbors[dir.index()] instanceof Prey) {
                directionsToPrey.add(dir);
            }
        }
      
        // If the list of directions to cells with prey is not empty, choose a
        // direction at random from this list. In this case, this predator eats
        // and gains a unit of energy.
        ThreadLocalRandom rand = ThreadLocalRandom.current();
        if (!directionsToPrey.isEmpty()) {
            energy++;
            return directionsToPrey.get(rand.nextInt(directionsToPrey.size()));
        }

        // If this predator does not eat, it loses a unit of energy. 
        energy--;

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

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