import java.util.NoSuchElementException;

/**
 * CountableMatrix is a matrix of Countable objects
 * Your job is to fill in the method below.
 * Other methods have been provided for you.
 * 
 * @author Nancy Harris
 * @version V1 04/22/2013
 *
 */
public class SizableMatrix {

	private Sizable[][] matrix;
	
	/** Constructor accepts an array of Countable objects
	 * and creates this CountableMatrix object
	 * 
	 * @param inMatrix The array to use
	 */
	public SizableMatrix(Sizable [][] inMatrix)
	{
		this.matrix = inMatrix;
	}
	
	/** getLargestObject finds the largest object in the matrix
	 * based on the calcSize method.
	 * 
	 * If the matrix is null or empty (no Sizable objects), 
	 * throw a NoSuchElement exception.
	 * 
	 * @return the description from the showValue of 
	 *     the object with the largest size
	 */
	public String getLargestObject()
	{
		String result;
		Sizable largest = null;
		checkMatrix();
		
		for (int row = 0; row < matrix.length; row++)
			if (matrix[row] != null)
				for (int col = 0; col < matrix[row].length; col++)
					if (matrix[row][col] != null)
						if (largest == null || largest.calcSize() < 
							matrix[row][col].calcSize())
							largest = matrix[row][col];
		if(largest != null)	
			result = largest.showValue();
		else
			result = "";
		return result;
	}
	/**
	 * checks to see if the matrix is null or empty
	 */
	private void checkMatrix() {
		
		boolean empty = true;
		
		if (matrix == null || matrix.length == 0)	
			throw new NoSuchElementException("Empty matrix");
		
		// check to see if there are any objects
		for (int row = 0; row <= matrix.length && empty; row++)
			if (matrix[row] != null)
				for (int col = 0; col < matrix[row].length && empty; col++)
					if (matrix[row][col] != null)
						empty = false;
		if (empty)
			throw new NoSuchElementException("Empty matrix");
		
	}

	/** getSmallest finds the smallest object in the matrix
	 *  based on the calcSize method.
	 *  
	 * If the matrix is null or empty (no Sizable objects), 
	 * throw a NoSuchElement exception with an appropriate message.
	 *  
	 * @return the description from the showValue method
	 *     of the object with the smallest size.
	 */
	public String getSmallestObject()
	{
		String result;
		Sizable smallest = null;
		checkMatrix();
		
		for (int row = 0; row < matrix.length; row++)
			if (matrix[row] != null)
				for (int col = 0; col < matrix[row].length; col++)
					if (matrix[row][col] != null)
						if (smallest == null || smallest.calcSize() > 
							matrix[row][col].calcSize())
							smallest = matrix[row][col];
		if(smallest != null)	
			result = smallest.showValue();
		else
			result = "";
		return result;
	}
	
	/** 
	 * getAverageObjectSize calculates the average size
	 * of objects in the matrix.
	 * 
	 * If the matrix is null or empty (no Sizable objects), 
	 * throw a NoSuchElement exception. 
	 * 
	 * @return the average object size
	 */
	public double getAverageObjectSize()
	{
		double average = 0;
		double sum = 0;
		double count = 0;
		
		checkMatrix();
		
		for (int row = 0; row < matrix.length; row++)
			if (matrix[row] != null)
				for (int col = 0; col < matrix[row].length; col++)
					if (matrix[row][col] != null)
					{
						sum = sum + matrix[row][col].calcSize();
						count++;
					}
		
		average = sum / count;
		
		return average;
	}
	/**
	 * getTotalObjectSize calculates the total size of all objects 
	 * in the matrix.
	 * 
	 * If the matrix is null or empty (no Sizable objects),
	 *  throw a NoSuchElement exception.
	 * 
	 * @return the total size of all objects in the matrix
	 */
	public int getTotalObjectSize()
	{
		int sum = 0;
		
		checkMatrix();
		
		for (int row = 0; row < matrix.length; row++)
			if (matrix[row] != null)
				for (int col = 0; col < matrix[row].length; col++)
					if (matrix[row][col] != null)
					{
						sum = sum + matrix[row][col].calcSize();
					}
		return sum;
	}
	
	/**
	 * getLargestinRow returns the size of the largest object in 
	 * the given row. If the row is negative or is not in the matrix
	 * throw an ArrayIndexOutOfBoundsException with the message:
	 * "Row: " followed by the row number followed by " not valid."
	 * If the matrix is null or empty (no Sizable objects) or the row contains no 
	 *  Sizable objects, throw a NoSuchElement exception.
	 * 
	 * @param row The row you are interested in
	 * @return the description from the showValue method
	 *    of the largest object in the row
	 */
	public String getLargestinRow(int row)
	{
		String result = "";
		Sizable largest = null;
		
		checkMatrix();
		
		if (!isRow(row))
			throw new ArrayIndexOutOfBoundsException("Row: " + row + " not valid.");
		else
			if (matrix[row].length > 0)
				for (int col = 0; col < matrix[row].length; col++)
					if (matrix[row][col] != null)
						if (largest == null || matrix[row][col].calcSize() > largest.calcSize())
							largest = matrix[row][col];
		if (largest == null)
			throw new NoSuchElementException("No objects in row");
		else
			result = largest.showValue();
		
		return result;
	}
	
		
	/************************** private helper methods **************
	/** 
	 * isRow returns true if this row exists in the matrix.
	 * @param row The row number to check
	 * @return true if the row exists in this matrix, false otherwise
	 */
	private boolean isRow(int row)
	{
		boolean result = false;
		if (row >= 0 && matrix != null && 
				row < this.matrix.length && 
				this.matrix[row] != null)
			result = true;
		return result;
	}
	/** 
	 * isColumn returns true if the column exists in at least one
	 * row and false otherwise
	 * @param column The column number to check
	 * @return true if the column exists in at least one row in the
	 * matrix, false otherwise.
	 */
	private boolean isColumn(int column)
	{
		boolean result = false;
		if (column >= 0 && matrix != null)
			for (int row = 0; row < matrix.length && !result; row++)
			{
				if (matrix[row].length > column)
					result = true;
			}
		return result;
	}
}
