
// Simulator.java
// Andrew Davison, Nov 2018, ad@fivedots.coe.psu.ac.th

/* A simple predator-prey simulator, based on a field containing
 * rabbits and foxes. The lists of animals use Animal.
 * 
 * @author David J. Barnes and Michael Kolling
 * @version 2006.03.30
 */


import java.util.*;
import java.awt.Color;


public class Simulator
{
  // Constants representing configuration information for the simulation.
  private static final int DEFAULT_WIDTH = 50;   // grid depth and width
  private static final int DEFAULT_DEPTH = 50;

  private static final double FOX_CREATION_PROBABILITY = 0.02;
  private static final double RABBIT_CREATION_PROBABILITY = 0.08;    


  private List<Animal> animals;
  private List<Animal> newAnimals; // for newly created animals
     // the Lists refer to Animal, not Rabbit and Fox

  private Field field;
  private Field updatedField;    // for holding the changed field

  private int step;   // current simulation step
  private SimulatorView view;   // simulation GUI
 

  public Simulator()
  {  this(DEFAULT_DEPTH, DEFAULT_WIDTH);  }
    

  public Simulator(int depth, int width)
  {
    if (width <= 0 || depth <= 0) {
      System.out.println("The dimensions must be greater than zero.");
      System.out.println("Using default values.");
      depth = DEFAULT_DEPTH;
      width = DEFAULT_WIDTH;
    }
    animals = new ArrayList<Animal>();
    newAnimals = new ArrayList<Animal>();
    field = new Field(depth, width);
    updatedField = new Field(depth, width);

    // create a GUI view of the field
    view = new SimulatorView(depth, width);
    view.setColor(Rabbit.class, Color.orange);
    view.setColor(Fox.class, Color.blue);
        
    reset();       // initialize animals and field
  }  // end of Simulator()

    
  public void simulate(int numSteps)
  /* Run the simulation for the given number of steps.
     Stop if it ceases to be viable. */
  {
    for (int step = 1; step <= numSteps && view.isViable(field); step++) {
      simulateOneStep();
      wait(50);
    }
  }  // end of simulate()
    

  private void simulateOneStep()
  /* Run the simulation for a single step.
     Iterate over the whole field updating the state of every
     fox and rabbit. */
  {
    step++;
    newAnimals.clear();
        
    // update all the animals
    for (Iterator<Animal> it = animals.iterator(); it.hasNext();) {
      Animal animal = it.next();
      animal.act(field, updatedField, newAnimals);
      if (!animal.isAlive())      // remove dead animals
        it.remove();
    }
    // add new animals to the list of animals
    animals.addAll(newAnimals);
        
    // swap the field and updatedField info
    Field temp = field;
    field = updatedField;
    updatedField = temp;
    updatedField.clear();

    // display the 'new' (updated) field on screen
    view.showStatus(step, field);
  }  // end of simulateOneStep()
        


  private void wait(int milliseconds)
  // waits for a specified number of milliseconds
  {
    try {
      Thread.sleep(milliseconds);
    } 
    catch (InterruptedException e) { }   // ignore exception
  } // end of wait()



  private void reset()
  // reset the simulation
  {
    step = 0;
    animals.clear();
    newAnimals.clear();
    field.clear();
    updatedField.clear();
    populate(field);

    // show the starting state in the view
    view.showStatus(step, field);
  }  // end of reset()

    
  private void populate(Field field)
  // populate a field with foxes and rabbits
  {
    Random rand = new Random();
    field.clear();
    for (int row = 0; row < field.getDepth(); row++) {
      for (int col = 0; col < field.getWidth(); col++) {
        if (rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
          Fox fox = new Fox(true);
          fox.setLocation(row, col);
          animals.add(fox);
          field.place(fox);
        }
        else if (rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
          Rabbit rabbit = new Rabbit(true);
          rabbit.setLocation(row, col);
          animals.add(rabbit);
          field.place(rabbit);
        }
        // else leave the location empty.
      }
    }
    Collections.shuffle(animals);
  }  // end of populate()


  // -------------------------------------------------------

  public static void main(String[] args) 
  {  
    Simulator sim = new Simulator(100, 100);     // default is 50x50
    sim.simulate(500);   // run for 500 steps
  }  // end of main()

}  // end of Simulator class
