
// Field.java
// Andrew Davison, Nov 2018, ad@fivedots.coe.psu.ac.th

/* Represent a rectangular grid of field positions.
 * Each position is able to store a single animal.
 * 
 * @author David J. Barnes and Michael Kolling
 * @version 2006.03.30

   This version uses the Animal class rather than Object.
   Methods talk about Animal rather than Object.
 */


import java.util.*;


public class Field
{
  private static final Random rand = new Random();
    
  private int depth, width;
  private Animal[][] field; // for storing animals (was Object)

  public Field(int d, int w)
  { depth = d;
    width = w;
    field = new Animal[depth][width];
  }  // end of Field()
    

  public void clear()
  // empty the field.
  { for (int row = 0; row < depth; row++) {
      for (int col = 0; col < width; col++)
        field[row][col] = null;
    }
  }  // end of clear()

    

  public void place(Animal animal)
  /* Place an animal in the field.
     If there is already an animal at the location it will be lost. 
  */
  { Location loc = animal.getLocation();
    field[loc.getRow()][loc.getCol()] = animal;
  }
    

  public Animal getAnimalAt(Location loc)
  // return the animal at the given location, if any.
  {  return getAnimalAt(loc.getRow(), loc.getCol());  }
    

  public Animal getAnimalAt(int row, int col)
  // return the animal at the given location, if any.
  {  return field[row][col]; }
    

  public Location randomAdjLoc(Location loc)
  /* Generate a random location that is adjacent to the
     given location, or is the same location.
     The returned location will be within the valid bounds of the field. 
  */
  { int row = loc.getRow();
    int col = loc.getCol();

    // Generate an offset of -1, 0, or +1 for both the current row and col.
    int nextRow = row + rand.nextInt(3) - 1;
    int nextCol = col + rand.nextInt(3) - 1;

    // Check in case the new location is outside the bounds.
    if (nextRow < 0 || nextRow >= depth || nextCol < 0 || nextCol >= width)
      return loc;
    else if (nextRow != row || nextCol != col)
      return new Location(nextRow, nextCol);
    else
      return loc;
  }  // end of randomAdjLoc()

    
  public Location freeAdjLoc(Location loc)
  /* Try to find a free location that is adjacent to the
     given location. If there is none, then return the current
     location if it is free. If not, return null.
     The returned location will be within the valid bounds of the field. 
  */
  { Iterator<Location> adjacent = getAdjLocs(loc);
    while (adjacent.hasNext()) {
      Location next = adjacent.next();
      if (field[next.getRow()][next.getCol()] == null)
        return next;
    }
    // check whether current loc is free
    if (field[loc.getRow()][loc.getCol()] == null)
      return loc;
    else
      return null;
  }  // end of freeAdjLoc()


  public Iterator<Location> getAdjLocs(Location loc)
  /* Generate an iterator over a shuffled list of locations adjacent
     to the given one. The list will not include the location itself.
     All locations will lie within the grid. */
  {
    int row = loc.getRow();
    int col = loc.getCol();
    List<Location> locs = new LinkedList<Location>();

    for (int roffset = -1; roffset <= 1; roffset++) {
      int nextRow = row + roffset;
      if ((nextRow >= 0) && (nextRow < depth)) {
        for (int coffset = -1; coffset <= 1; coffset++) {
          int nextCol = col + coffset;
          // Exclude invalid locations and the original location.
          if ((nextCol >= 0) && (nextCol < width) && (roffset != 0 || coffset != 0))
            locs.add(new Location(nextRow, nextCol));
        }
      }
    }
    Collections.shuffle(locs, rand);
    return locs.iterator();
  }  // end of getAdjLocs()


  public int getDepth()
  {  return depth;  }
    

  public int getWidth()
  {  return width;  }

}  // end of Field class
