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