//
// Cell.java
//

public class Cell {
    // OVERVIEW: A Cell is an object than represents a cellular automaton.  A cell
    //     has a current state.  
    //
    // Abstraction Function
    //
    // Rep Invariant
    //     
    //     This cell must exist in its grid at its location:
    //        grid == NULL or this == grid.getCellAt (mrow, mcol)
    //    
    //

    /*@spec_public@*/ private Grid grid;
    private int mrow;
    private int mcol;
    /*@non_null@*/ private CellState state;
    //@ghost public boolean isInitialized;

    public Cell()
       // EFFECTS: Initializes this to a dead cell.
    {
        state = CellState.createDead ();
    }

    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.
       //@ensures isInitialized == true;
    {
        this.mrow = row;
        this.mcol = col;
        this.grid = grid;
        //@assume isInitialized == true;
    }
    
    public boolean setState (/*@non_null@*/ CellState s)
       // MODIFIES: this
       // EFFECTS: Sets the cell's current state to s.  Returns true iff the new state is different from the current state.
    {
        if (state.equals (s)) {
            return false;
        } else {
            state = s;
            return true;
        }
    }
    
    public /*@non_null@*/ CellState getState ()
       // EFFECTS: Returns the cell's current state.
    {
        return state;
    }
    
    boolean isAlive()
       // EFFECTS: Returns true if the cell is alive, false otherwise.
    {
        return state.isAlive();
    }

    public int getRow ()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns the row number that this cell is located in.
    {
        return mrow;
    }
    
    public int getColumn ()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns the column number that this cell is located in.
    {
        return mcol;
    }
    
    public /*@non_null@*/ Grid getGrid()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns the grid containing this cell.
       //@requires this.grid != null;
    {
        return grid;
    }
    
    public int countAliveNeighbors ()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns a count of the cell's live neighbors.
       //@requires this.grid != null;
    {
        int neighbors = 0;
        
        for (int row = Math.max (0, mrow - 1); row <= Math.min (grid.numRows () - 1, mrow + 1); row++) {
            for (int col = Math.max (0, mcol - 1); col <= Math.min (grid.numColumns () - 1, mcol + 1); col++) {
                if (row != mrow || col != mcol) { // Don't count yourself
                    if (grid.getCellAt (row, col).isAlive ()) {
                        neighbors++;
                    } 
                }
            }
        }
        
        return neighbors;
    }
    
    public /*@non_null@*/ CellState getNextState ()
       // REQUIRES: init has previously been called for this.
       // EFFECTS: Returns next state value for this.
       //    For the underlying Cell class, the next state is always the
       //    current state.
       // This method will be overloaded by cell implementations
       // for various cell automata rules.
    {
        return state;
    }
}

