import java.util.HashMap;
import java.util.Iterator;

public class HMUnboundedEnv extends SquareEnvironment
{
  private HashMap objectMap;

  public HMUnboundedEnv()
  {
    // Construct and initialize inherited attributes.
    super();

    objectMap = new HashMap();
  }

  public int numObjects()
  {
    return objectMap.size();
  }
  public int numRows()
    {
        return -1;
    }


    public int numCols()
    {
        return -1;
    }
    
    public boolean isValid(Location loc)
    {
        // All non-null locations are valid in an unbounded environment.
        return loc != null;
    }
  

  public Locatable[] allObjects()
  {
    Locatable[] objectArray = new Locatable[objectMap.size()];

    // Put all the environment objects in the list.
    Iterator keyIterator = objectMap.keySet().iterator();
    for ( int index = 0; keyIterator.hasNext(); index++ )
    {
      Location loc = (Location) keyIterator.next();
      objectArray[index] = (Locatable) objectMap.get(loc);
    }
    return objectArray;
  }

// Note to teachers:... alternate code solutions for allObjects method

/*
  // Alternate code 1
  public Locatable[] allObjects()
  {
    Locatable[] objectArray = new Locatable[objectMap.size()];
    // values not in subset
    Iterator objectIterator = objectMap.values().iterator();
    for ( int index = 0; objectIterator.hasNext(); index++ )
    {
      objectArray[index] = (Locatable) objectIterator.next());
    }
    return objectArray;
  }
*/

/*
  // Alternate Code 2
  public Locatable[] allObjects()
  {
    Locatable[] objectArray = new Locatable[objectMap.size()];
    index = 0;
    // values not in subset
    Iterator objectIterator = objectMap.values().iterator();
    while ( objectIterator.hasNext() )
    {
      objectArray[index] = (Locatable) objectIterator.next();
      index++;
    }
  }
*/


// Note to teachers: rest of HashMap code

  public boolean isEmpty(Location loc)
  {
    return (objectAt(loc) == null);
    // OR return ! objectMap.containsKey(loc);
  }

  public Locatable objectAt(Location loc)
  {
    return (Locatable) objectMap.get(loc);
  }

  // modifier methods

  public void add(Locatable obj)
  {
    // Check precondition. Location should be empty.
    Location loc = obj.location();
    if ( ! isEmpty(loc) )
      throw new IllegalArgumentException("Location " + loc +
                                   " is not a valid empty location");

    // Add object to the environment.
    objectMap.put(loc, obj);
  }

  public void remove(Locatable obj)
  {
    // Find the index of the object to remove.
    Location loc = obj.location();
    if ( ! objectMap.containsKey(loc) )
      throw new IllegalArgumentException("Cannot remove " +
                                          obj + "; not there");

    // Remove the object.
    objectMap.remove(loc);
  }

  public void recordMove(Locatable obj, Location oldLoc)
  {
    // Simplest case: There was no movement.
    Location newLoc = obj.location();
    if ( newLoc.equals(oldLoc) )
      return;

    // Otherwise, the object should still be mapped to the old
    // location, and nothing should be mapped (yet) to the new
    // location.
    Locatable foundObject = objectAt(oldLoc);
    if ( ! (foundObject == obj && isEmpty(newLoc)) )
      throw new
            IllegalArgumentException("Precondition violation moving"
                                      + obj + " from " + oldLoc);
    // Associate the object with the proper location.
    objectMap.remove(oldLoc);
    objectMap.put(newLoc, obj);
  }
}


