
// 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
 */


import java.util.*;


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


  public Field(int d, int w)
  { depth = d;
    width = w;
    field = new Object[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(Object animal, int row, int col)
  /* Place an animal at the given loc.
     If there is already an animal at the location it will be lost.
  */
  {  place(animal, new Location(row, col));  }

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

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

  public Object getObjectAt(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 loc 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 loc
          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
