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

	// Constants (in milliseconds)
	static final int TURN_DELAY = 500;
	static final int TURN_RANDOM = 200;

	// Rep:
	protected Grid grid;
	
	//@ghost public boolean isInitialized;
	private boolean isPaused;

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

	/*@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 (mx, my)
	//              && grid.getObjectAt (mx, my) == this

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

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

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

	//@requires isInitialized
	abstract public void executeTurn();
	// EFFECTS: Executes one turn for this object.

	public Color getColor()
	//@ensures \result != null;
	// EFFECTS: Returns the color that represents this SimObject, for
	//  painting the grid.
	{
		return Color.green;
	} //@nowarn Post // ESC/Java spec doesn't know that Color.green is not null.

	final public 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;
	//@ensures \result != null;
	{
		return grid;
	}

	//@ensures \result.elementType == \type(SimObject)
	//@ensures \result != null;
	//@requires isInitialized;
	synchronized public Enumeration getNeighbors() { //@nowarn Deadlock
		// 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 x = mx - 1; x <= mx + 1; x++) {
			for (int y = my - 1; y <= my + 1; y++) {
				if (x != mx || y != my) { // don't count yourself
					if (grid.validLocation(x, y)) {
						try {
						SimObject obj = grid.getObjectAt(x, y);

						if (obj != null) {
							objects.addElement(obj);
						}
						} catch (Exception e) {
							throw new RuntimeException ("BUG: Invalid exception: " + e);
						}
					}
				}
			}
		}

		return objects.elements();
	} //@nowarn Exception

	final public int getX()
	// REQUIRES: this is initialized.
	// EFFECTS: Returns the x location that this cell is located in.
	//@requires isInitialized;	
	{
		return mx;
	}

	final public int getY()
	// REQUIRES: init has previously been called for this.
	// EFFECTS: Returns the y location that this cell is located in.
	//@requires isInitialized;
	{
		return my;
	}

	final public void init(int x, int y, 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 with isPaused = true.
	//   NOTE: This method is final, that means subtypes cannot override this method.
	//@requires !isInitialized;
	//@requires grid != null;
	//@ensures isInitialized;
	{
		this.mx = x;
		this.my = y;
		this.grid = grid;
		this.isPaused = true;
		Thread thread = new Thread(this);
		thread.setPriority(Thread.MIN_PRIORITY);
		thread.start();
		//@set isInitialized = true;
	}

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

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

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

	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).
	{
		while (true) {
			if (!isPaused) {
				executeTurn();
			}
			delay(TURN_DELAY + random.nextInt(TURN_RANDOM));
		}
	}
}