//
// SimObject.java
//

import java.util.Random;
import java.util.Enumeration;
import java.util.Vector;
import java.awt.Color;

abstract public class SimObject implements Runnable {
    // OVERVIEW: A SimObject is an object than represents a simulator object.
    //    It has a grid (a circular reference to the grid containing this object),
    //    location (integer row and column), initialization state (boolean
    //    that is true if it has beein initialized), and thread pause state (boolean that
    //    is true if the thread is paused).
    //    A typical SimObject is < grid, row, col, initialized, paused >.
    //
    //  Note: SimObject is an abstract class.  The executeTurn method must 
    //  be implemented by subclasses.
    
    // Rep:
    protected Grid grid;

    // subclasses have access to mrow and mcol
    protected int mrow;
    protected int mcol;

    //@ghost public boolean isInitialized;
    private boolean isPaused;

    /*@non_null@*/ private Random random; // Used to get random numbers.

    // AF(c) = < grid, mrow, mcol, isInitialized, isPaused >
    // RI(c) = 
    //         if isIntialized then 
    //              grid != null
    //              && grid.validLocation (mrow, mcol)
    //              && grid.getObjectAt (mrow, mcol) == this

    //@invariant isInitialized ==> grid != null
    //  If isInitialized is true, the grid is not null.

    static final int TURN_DELAY = 500;
    static final int TURN_RANDOM = 200;

    public SimObject ()
	// EFFECTS: Creates a new, uninitalized SimObject.
	//@ensures !isInitialized
    {
	random = new Random();
    }

    final public void init (int row, int col, /*@non_null@*/ Grid grid)
       // REQUIRES: init has not previously been called for this.  The cell is at (row, col) on grid.
       // MODIFIES: this
       // EFFECTS: Initializes the cell at row, col on grid.
       //   NOTE: This method is final, that means subtypes cannot override this method.
       //@requires !isInitialized;
       //@ensures isInitialized;
    {
        this.mrow = row;
        this.mcol = col;
        this.grid = grid;
	this.isPaused = false;
        //@set isInitialized = true;
    }

    public void run ()
	// REQUIRES: this has been initialized
	//@also_requires isInitialized
	//    We use also_requires instead of requires, because we are adding a precondition
	//    to an inherited method.  This violates the substitution principle --- subtypes
	//    should make preconditions weaker, not stronger.
	// EFFECTS: The object thread.  If the object is not paused, executes one turn by calling
	//    the executeTurn method, and sleeps for a time and repeats.  If the object is paused,
	//    does nothing (until the object is unpaused).
    {
      isPaused = false;	

      while (true) {
	  if (!isPaused) {
	      executeTurn ();
	      delay (TURN_DELAY + random.nextInt(TURN_RANDOM));
	  }	
      }
    }
    
    public void resumeObject()
	// EFFECTS: Sets paused to false.
    {
      isPaused = false;
    }

    public void pauseObject()
	// EFFECTS: Sets paused to true.
    {
      isPaused = true;
    }

    public boolean isPaused()
	// EFFECTS: Returns the value of paused.
    {
      return isPaused;
    }

    abstract public void executeTurn() 
	//@requires isInitialized
	;
	// EFFECTS: Executes one turn for this object.
    
    public /*@non_null@*/ Color getColor () 
       // EFFECTS: Returns the color that represents this SimObject, for
       //  painting the grid.
    {
	return Color.green; 
    } //@nowarn NonNullResult // ESC/Java spec doesn't know that Color.green is not null.

    final public int getRow ()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns the row number that this cell is located in.
       //@requires isInitialized;
    {
        return mrow;
    }

    final public void delay (int ms) 
	// EFFECTS: Stalls for ms milliseconds.
    {
	try {
	    Thread.sleep (ms);
	} catch (InterruptedException ie) {
	    ;
	}
    }

    final public int getColumn ()
       // REQUIRES: this is initialized.
       // EFFECTS: Returns the column number that this cell is located in.
       //@requires isInitialized;	
    {
        return mcol;
    }

    //@ensures \result.elementType == \type(SimObject)
    synchronized public /*@non_null@*/ Enumeration getNeighbors () {
	// REQUIRES: The grid must be locked as long as the result of getNeighbors is used.
	//    Otherwise, the neighbors could change if another object moves!
	// EFFECTS: Returns an Enumeration that yields all the objects adjacent to this
	//    in the grid.

	Vector objects = new Vector ();
	//@set objects.elementType = \type(SimObject)
	//@set objects.containsNull = false

	for (int row = mrow - 1; row <= mrow + 1; row++) {
	    for (int col = mcol - 1; col <= mcol + 1; col++) {
		if (row != mrow || col != mcol) { // don't count yourself
		    if (grid.validLocation (row, col)) {
			SimObject obj = grid.getObjectAt (row, col);

			if (obj != null) {
			    objects.addElement (obj);
			}
		    }
		}
	    }
	}

	return objects.elements ();
    }

    final public /*@non_null@*/ Grid getGrid ()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns the grid containing this cell.
       //    NOTE: The rep is exposed.  The Grid associated with a SimObject is part of the data abstraction.
       //@requires isInitialized == true;
    {
        return grid;
    }

}