/**
 * MapGenerator Sample Extensions
 */
package mycompany.map.modificator;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.w3c.dom.Element;

import idea.map.generator.MapGenerator;
import idea.map.matrix.AbstractMatrix;
import idea.map.matrix.Matrixes;
import idea.map.modificator.AbstractModificator;
import idea.mapgen.util.StatisticsCollector;


/**
 * Modify matrix - replace every value with result of specified formula
 * 
 *  Usage:
 *  
 *  <Modificator class="mycompany.map.modificator.UserModificator"/>
 *  
 *  
 * @author Lumir Vanek, vanek@idea-envi.cz
 *
 */
public class UserModificator extends AbstractModificator 
{
	/**
	 * ID of the matrix to modify
	 */
	private final String matrixId;
		
	/**
	 * Modified matrix
	 */
	private AbstractMatrix matrix;
	
	/**
	 * The value, used in modification
	 */
	private final float modificator;
	
	/**
	 * Constructor
	 * 
	 * @param matrixId ID of the matrix to modify
 	 * @param modificator The value, used in modification
 	 */
	public UserModificator(String matrixId, float modificator)
	{
		super();
		this.matrixId = matrixId;
		this.modificator = modificator;		
	}
	
	/**
	 * Full constructor
	 * 
	 * @param matrixId ID of the matrix to modify
 	 * @param limit The value, used in modification
 	 * @param threads Number of threads used for computing speed-up
	 * @param timeout Timeout for thread, in seconds
 	 */
	public UserModificator(String matrixId, float modificator, int threads, int timeout)
	{
		super(threads, timeout);
		this.matrixId = matrixId;
		this.modificator = modificator;	
	}
	
	/**
	 * XML Runset constructor, initialize yourself from given DOM element
	 * 
	 * @param domElement Runset DOM element with configuration in attributes
	 */
	public UserModificator(Element domElement)
	{
		super(domElement);

		if(!domElement.hasAttribute("matrix"))
		{
			throw new RuntimeException("Missing matrix attribute for " + getXmlElementName());
		}
		matrixId = domElement.getAttribute("matrix");	
		
		if(!domElement.hasAttribute("modificator"))
		{
			throw new RuntimeException("Missing modificator attribute for " + getXmlElementName());
		}
		modificator = Float.parseFloat(domElement.getAttribute("modificator"));	
	}
	
	/**
	 * Modify matrix, specified in configuration
	 * 
	 * @param matrixes Matrixes list
	 * @param collector The statistics collector
	 */
	public void modify(Matrixes matrixes, StatisticsCollector collector)
	{
		matrix = matrixes.get(matrixId);
		if(matrix == null) throw new RuntimeException("Matrix " +  matrixId + " not found in " + getXmlElementName());
		
		if(matrixes.getRows() < getThreads())
		{
			throw new RuntimeException("Matrix rows number must be less than number of threads. Matrix: " + matrix.getId());
		}
		
		try
		{
			float elapsedTime = runThreads(matrix.getRows(), collector);
			Logger.getLogger(MapGenerator.loggerName).fine("User Modification of matrix " +
					matrix.getId() + " finished in " + elapsedTime + " sec.");
		}
		catch(InterruptedException e)
		{
			Logger.getLogger(MapGenerator.loggerName).log(Level.SEVERE, 
					"UserModificatorThread interrupted", e);
			
			throw new RuntimeException("UserModificatorThread interrupted");
		}
		finally
		{
			this.matrix = null;
		}
	}

	/**
	 * Create thread 
	 * 
	 * @param startRow First matrix row, computed by created thread 
	 * @param endRow Last matrix row, computed by created thread 
	 * @param nameAppendix Appendix for thread name
	 * @return Thread, that make required work
	 */
	protected Runnable createThread(int startRow, int endRow, String nameAppendix)
	{
		return new UserModificatorThread("UserModificator " + nameAppendix, 
				matrix, 
				modificator,
				startRow, 
				endRow);
	}
}