// Filename CellGrid.java
// Daniel Farinha
// January 1999

import java.awt.*;
import java.awt.event.*;

public class CellGrid extends Object
                      implements ActionListener
{	
  private WorldArray theWorld;
  private LastCellInfo aLastCellInfo;
  private Timer aTimer;
  private boolean isRunning;
  private ActionListener itsListener = null;
	
  public CellGrid()
  {
    super();
    theWorld      = new WorldArray();
    aLastCellInfo = new LastCellInfo(0, false);
		
    aTimer = new Timer();
    try
    {
      aTimer.addActionListener( this);
    }
    catch( java.util.TooManyListenersException exception)
    { // do nothing
    }
    aTimer.start();
  } // End CellGrid constructor
	
	
  public void modifyCell(int cellID)  // this is called from outside class only
  {
    if( theWorld.array[cellID])       // If cell is alive
    {
      setCellState(cellID, false);    // kill it
      aLastCellInfo = new LastCellInfo(cellID, false);
    }
    else
    {
      setCellState(cellID, true);     // else is born
      aLastCellInfo = new LastCellInfo(cellID, true);
    }				
    String theCommand = new String("getcellinfo");
    sendAction(theCommand);

  }
	
	
  public void setRunning(boolean state)        // sets the isRunning member variable
  {
    isRunning = state;
  }
	

  public WorldArray getCellGrid()             // returns whole world info (array + pop size)
  {
    return theWorld;
  }
	
	
  private void setCellState(int cellID, boolean newState)   // this is called from this class only
  {
    if(theWorld.array[cellID] != newState)                // if new cell state is different
      {
        theWorld.array[cellID] = newState;                // change it
        if(newState)                                        // if new cell
          theWorld.alive++;                                 // increment pop size
        else
          theWorld.alive--;                                 // else decrement
				
        checkLife();                                        // check for living cells
      }
  }
	
	
  private void checkLife()       // checks if there is any living cells
  {
    if( theWorld.alive == 0)
    {
      String theCommand = new String("reset");
      sendAction(theCommand);
    }
    else if ( !isRunning)       // only if it is not running set to edit state
    {
      String theCommand = new String("edit");
      sendAction(theCommand);
    }
  }
				
	
  public LastCellInfo getLastCellInfo()   // info about last cell pressed bu user
  {
    return aLastCellInfo;
  }
	
	
  public void resetGrid()                 // resets world (kills all cells)
  {
    theWorld.resetArray();
  }
		
	

  public void addActionListener( ActionListener listener) 
                  throws java.util.TooManyListenersException
  { 
    if ( itsListener == null)
    { 
      itsListener = listener;
    }
    else
    { 
      throw new java.util.TooManyListenersException();
    } // End if.
  } // End addActionListener.


  public void removeActionListener( ActionListener listener)
  { 
    if ( itsListener == listener)
    { 
      itsListener = null;
    }
  }
	
	
  public synchronized void actionPerformed( ActionEvent event)    // only event is from Timer
  {
    if( isRunning)
    {
      calculateNextGeneration();                        // calculate next generation
			
      String theCommand = new String("newgeneration");  // notify GameofLife of new generation
      sendAction(theCommand);
			
      checkLife();                                      // check for living cells
    }
  }
	
	
  private void calculateNextGeneration()
  {
    WorldArray nextGeneration = new WorldArray();
		
    for( int i = 0; i <= 99; i++)                    // for all cells
    {
      int neighbours = calculateNeighbours(i);
      if( theWorld.array[i])                      // if cell is alive
      {
        if( neighbours < 2 || neighbours > 3)       // and if too few or too many neighbours
        {
          nextGeneration.array[i] = false;          // in next generation it will be dead
        }
        else
        {
          nextGeneration.array[i] = true;           // else it will stay alive
          nextGeneration.alive++;                   // increment pop size
        }
      }
      else                                       // else it must be presently dead
      {
        if( neighbours == 3)                        // if it has exactly 3 neighbours
        {
          nextGeneration.array[i] = true;           // in next generation it will be born
          nextGeneration.alive++;                   // increment pop size
        }
      }
    } // end for
		
    theWorld = nextGeneration;                      // copy newGeneration back to working variable
										
  }
	
	
  private int calculateNeighbours(int i)
  {
    int neighboursCount = 0;
			
    int nN	 = i - 10;
    int nS	 = i + 10;
    int nE	 = i + 1;
    int nW	 = i - 1;
    int nNE	 = i - 9;   // -10 +1
    int nNW	 = i - 11;  // -10 -1
    int nSE	 = i + 11;  // +10 +1
    int nSW	 = i + 9;   // +10 -1
		
    if ( i >= 0 && i <= 9)       // check if top row
    {
      nN 	+= 100;
      nNE += 100;
      nNW += 100;
    }
		
    if ( i >= 90 && i <= 99)    // check if bottom row
    {
      nS  -= 100;
      nSE -= 100;
      nSW -= 100;
    }
		
    if ( i == 9 ||              // check if rightmost column
         i == 19 ||
         i == 29 ||
         i == 39 ||
         i == 49 ||
         i == 59 ||
         i == 69 ||
         i == 79 ||
         i == 89 ||
         i == 99)
    {
      nE  -= 10;
      nNE -= 10;
      nSE -= 10;
    }
		
    if ( i == 0 ||              // check if leftmost column
         i == 10 ||
         i == 20 ||
         i == 30 ||
         i == 40 ||
         i == 50 ||
         i == 60 ||
         i == 70 ||
         i == 80 ||
         i == 90)
    {
      nW  += 10;
      nNW += 10;
      nSW += 10;
    }
		
    if ( i == 9)              // check if top right cell
      nNE = 90;
		
    if ( i == 0)              // check if top left cell
      nNW = 99;

    if ( i == 99)             // check if bottom right cell
      nSE = 0;

    if ( i == 90)             // check if bottom left cell
      nSW = 9;
		
		
    if (theWorld.array[nN])
      neighboursCount++;
    if (theWorld.array[nS])
      neighboursCount++;
    if (theWorld.array[nE])
      neighboursCount++;
    if (theWorld.array[nW])
      neighboursCount++;
    if (theWorld.array[nNE])
      neighboursCount++;
    if (theWorld.array[nNW])
      neighboursCount++;
    if (theWorld.array[nSE])
      neighboursCount++;
    if (theWorld.array[nSW])
      neighboursCount++;
				
    return neighboursCount;
  }			
	
	
  private void sendAction(String aCommand)            // custom function to send event messages
  {
    ActionEvent theEvent = new ActionEvent( this,
                                            ActionEvent.ACTION_PERFORMED,
                                            aCommand);
    itsListener.actionPerformed( theEvent);
  }

}