import java.util.Random;

/**
 * Write a description of class Cube here.
 * 
 * @author Michael Bader
 * @version (a version number or a date)
 */
public class Cube
{
	// instance variables
	/// size of the cube
	private int sizeX;
	private int sizeY;
	private int sizeZ;
	/// cells of the cube - dimensions are x,y,z (z vertical dimension)
	private int cube[][][];
	/// Generator for random numbers:
	Random randGen;
	
	final int cheese = 0;
	final int hole = 1;
	final int water = 2;

	/**
	 * Constructor for objects of class Cube
	 * 
	 * @param   sizeX   size of the cube (x direction)
	 * @param   sizeY   size of the cube (y direction)
	 * @param   sizeZ   size of the cube (z direction)
	 * @param   probability  probability for cheese
	 */
	public Cube(int sizeX, int sizeY, int sizeZ, float probability)
	{
		// initialise instance variables
		this.sizeX = sizeX;
		this.sizeY = sizeY;
		this.sizeZ = sizeZ;
		cube = new int[sizeX][sizeY][sizeZ];
		randGen = new Random();
		
		for(int i=0;i<sizeX;i++)
		   for(int j=0;j<sizeY;j++)
		      for(int k=0;k<sizeZ;k++)
		         cube[i][j][k] = randomCell(probability);
	}

	/**
	 * Check, whether the cube is permeable
	 * 
	 * @return     true=permeable, false=non-permeable
	 */
	public boolean permeable()
	{
		boolean modified=true;
		
		// fill the cheese with water
		while(modified) {
		   // repeat until the water does not spread any more
		   modified = false;
		   for(int k=0;k<sizeZ;k++)
		      for(int i=0;i<sizeX;i++)
		      for(int j=0;j<sizeY;j++)
		         modified = modified || fill(i,j,k);
		   
		   // check whether there's water in the bottom layer
		   for(int i=0;i<sizeX;i++)
		      for(int j=0;j<sizeY;j++)
		         if ( cube[i][j][sizeZ-1] == water) {
		            return true;
		         };
		};
		
		// return false, if no water was found (=> cheese non-permeable)
		return false;
	}

	/**
	 * print layers of the cube to standard output
	 */
	public void printLayers()
	{
 		for(int k=0;k<sizeZ;k++) {
 		   System.out.println("Layer: "+k);
		   for(int i=0;i<sizeX;i++) {
		      for(int j=0;j<sizeY;j++)
		         System.out.print(cube[i][j][k]+" ");
		      System.out.println();
		   };
		};
    }


	/**
	 * randomly marks a cell as cheese or hole
	 * 
	 * @param   probability   probability that the current cell is cheese
	 * @return     code for cheese/hole
	 */
	private int randomCell(float probability)
	{
		if (randGen.nextFloat() < probability)
		   return cheese;
		else
		   return hole;
	}


	/**
	 * fills the given cell with water, if one of its neighbours
	 * is fills with water;
	 * 
	 * @param   i  coordinate in x direction
	 * @param   j  coordinate in y direction
	 * @param   k  coordinate in z direction (vertical)
	 * @return     true if cell is filled with water, false otherwise
	 */
	private boolean fill(int i, int j, int k)
	{
		// don't do anything, if cell is cheese or alread filled with water
		if (cube[i][j][k] != hole) return false;
		// fill cell, if cell is in the top layer, or if top neighbour is water
		if (k==0 || cube[i][j][k-1]==water) {
		   cube[i][j][k] = water; return true;
		};
		// fill cell, if either of the 4 neighbours contains water;
		// watch out for boundaries!
		if (i>0 && cube[i-1][j][k]==water) {
		   cube[i][j][k] = water; return true;
		};
		if (i<sizeX-1 && cube[i+1][j][k]==water) {
		   cube[i][j][k] = water; return true;
		};
		if (j>0 && cube[i][j-1][k]==water) {
		   cube[i][j][k] = water; return true;
		};
		if (j<sizeY-1 && cube[i][j+1][k]==water) {
		   cube[i][j][k] = water; return true;
		};
		return false;
	}

	/**
	 * run a test batch of cheese with given size and probability
	 * 
	 * @param   sizeX        size of the cheese cubes (x direction)
	 * @param   sizeY        size of the cheese cubes (y direction)
	 * @param   sizeZ        size of the cheese cubes (z direction)
	 * @param   probability  probability for "not hole"
	 * @param   tests        number of tests performed
	 * @return  number of permeable cheese cubes
	 */
	public static int testBatch(int sizeX, int sizeY, int sizeZ, 
                                    float probability, int tests)
	{
	   Cube cube;
	   int permeable = 0;
	   
	   for (int t=0;t<tests;t++) {
	       cube = new Cube(sizeX, sizeY, sizeZ, probability);
	       if ( cube.permeable() )
	          permeable++;
	   };
	   
	   return permeable;
    }

	/**
	 * produce a probabilty chart
	 * 
	 * @param   sizeX        size of the cheese cubes (x direction)
	 * @param   sizeY        size of the cheese cubes (y direction)
	 * @param   sizeZ        size of the cheese cubes (z direction)
	 * @param   start        start of probabilities
	 * @param   increment    probability increment after each test
	 * @param   batches      number of increments
	 * @param   tests        number of tests performed
	 */
	public static void chart(int sizeX, int sizeY, int sizeZ,
	                         float start, float increment, int batches, 
	                         int tests)
	{
	   float probability=start;
           for (int i=0; i<batches; i++) {
	       System.out.println("Probability for cheese in each cell: "+probability+"%,\t"
	                          +"number of permeable cubes: "
	                          + testBatch(sizeX,sizeY,sizeZ,probability,tests)
	                          + "\tout of " +  tests
	                         );
               probability+=increment;
	   };
    }

	public static void main(String[] args) {
	    int sizeX = 10;
	    int sizeY = 10;
	    int sizeZ = 10;
	    float start = 0.0F;
	    float increment = 0.1F;
            int batches = 10;
            int tests = 20;
            
            if (args.length != 7) {
               System.out.println("\nPlease, use the following command line call:\n");
               System.out.println("java Cube <sizeX> <sizeY> <sizeZ> "
                                 +"<startProbability> <probabilityIncrement> "
                                 +"<nmbr of increments> <tests per batch>\n");
               System.out.println("<startProbability> and <probabilityIncrement> should be given as floats (0.1 means 10%, etc.)\n");
               return;
            };
        
            sizeX = Integer.parseInt(args[0]);
            sizeY = Integer.parseInt(args[1]);
            sizeZ = Integer.parseInt(args[2]);
            start = Float.parseFloat(args[3]);
            increment = Float.parseFloat(args[4]);
            batches = Integer.parseInt(args[5]);
            tests = Integer.parseInt(args[6]);
            
            System.out.println("\nSimulation of cheese cubes with "
                               + sizeX + 'x' + sizeY +'x' + sizeZ + " cells:\n");
            
            chart(sizeX,sizeY,sizeZ,start,increment,batches,tests);
        }
}

