package com.alexrnl.gameoflife.service;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import com.alexrnl.commons.error.ExceptionUtils;
import com.alexrnl.commons.error.TopLevelError;
import com.alexrnl.gameoflife.world.World;

/**
 * TODO
 * @author barfety_a
 */
public class GameOfLifeController {
	/** Logger */
	private static final Logger			LG	= Logger.getLogger(GameOfLifeController.class.getName());
	
	/** The listeners on the world generation */
	private final List<WorldListener>	worldListeners;
	/** The current generation of the world */
	private final World					currentGeneration;
	/** The history of the generation */
	private final WorldHistory			history;
	/** The class in charge of computing the next step */
	private final WorldGrower			grower;
	
	/**
	 * Constructor #1.<br />
	 * @param initialGeneration
	 *        the initial generation of the world.
	 */
	public GameOfLifeController (final World initialGeneration) throws NoSuchAlgorithmException {
		super();
		worldListeners = new ArrayList<>();
		currentGeneration = initialGeneration;
		history = new WorldHistory(MessageDigest.getInstance("SHA-1"));
		grower = new WorldGrower();
	}
	
	/**
	 * Add a listener to notify on the evolution of the world.
	 * @param listener
	 *        the listener to notify, cannot be <code>null</code>.
	 */
	public void addWorldListener (final WorldListener listener) {
		if (listener == null) {
			throw new IllegalArgumentException("Cannot add null listener to world listeners");
		}
		synchronized (worldListeners) {
			worldListeners.add(listener);
		}
	}
	
	/**
	 * Remove the listener from the world.
	 * @param listener
	 *        the listener to remove, cannot be <code>null</code>.
	 */
	public void removeWorldListener (final WorldListener listener) {
		if (listener == null) {
			throw new IllegalArgumentException("Cannot remove null listener to world listeners");
		}
		synchronized (worldListeners) {
			worldListeners.remove(listener);
		}
	}
	
	/**
	 * Return a safe view of the world listeners.
	 * @return the world listeners.
	 */
	protected List<WorldListener> getWorldListeners () {
		synchronized (worldListeners) {
			return new ArrayList<>(worldListeners);
		}
	}
	
	/**
	 * Grow the world into the next generation.
	 */
	public void grow () {
		grower.computeNextGeneration(currentGeneration);
		final int previousOccurence = history.addHistory(currentGeneration);
		for (final WorldListener worldListener : getWorldListeners()) {
			try {
				if (previousOccurence == WorldHistory.NO_PREVIOUS_OCCURENCE) {
					worldListener.newGeneration(currentGeneration.clone());
				} else {
					worldListener.loopGeneration(currentGeneration.clone(), previousOccurence);
				}
			} catch (final CloneNotSupportedException e) {
				// This should not have happened, the world is cloneable
				LG.severe("Failed to clone world for listeners: " + ExceptionUtils.display(e));
				throw new TopLevelError("World clone failed", e);
			}
		}
	}
}

@@ -367,12 +367,44 @@
 %0A * 
-TODO
+Controller for a game of life.%3Cbr /%3E
 %0D%0A *
@@ -515,16 +515,20 @@
 er%09%09%09LG%09
+%09%09%09%09
 = Logger
@@ -577,24 +577,152 @@
 ame());%0D%0A%09%0D%0A
+%09/** The name of the default algorithm for computing hashes */%0D%0A%09private static final String%09%09%09DEFAULT_ALGORITHM%09= %22SHA-1%22;%0D%0A%09%0D%0A
 %09/** The lis
@@ -1186,16 +1186,111 @@
 world.%0D%0A
+%09 * @throws NoSuchAlgorithmException%0D%0A%09 *         if the algorithm does not exist in the JVM.%0D%0A
 %09 */%0D%0A%09p
@@ -1383,16 +1383,501 @@
 tion %7B%0D%0A
+%09%09this(initialGeneration, DEFAULT_ALGORITHM);%0D%0A%09%7D%0D%0A%0D%0A%09/**%0D%0A%09 * Constructor #2.%3Cbr /%3E%0D%0A%09 * @param initialGeneration%0D%0A%09 *        the initial generation of the world.%0D%0A%09 * @param algorithmName%0D%0A%09 *        the name of the algorithm for history hashes generation.%0D%0A%09 * @throws NoSuchAlgorithmException%0D%0A%09 *         if the algorithm does not exist in the JVM.%0D%0A%09 */%0D%0A%09public GameOfLifeController (final World initialGeneration, final String algorithmName) throws NoSuchAlgorithmException %7B%0D%0A
 %09%09super(
@@ -2016,23 +2016,29 @@
 nstance(
-%22SHA-1%22
+algorithmName
 ));%0D%0A%09%09g

Allow customization of hash algorithm in GameOfLifeController