//
//	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;
	boolean dead = false;
	//@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.red;
	} //@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;
	}
	
	public void die() {
		dead = true;
	}

	//@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;
	}

}