content: /*
   * Copyright (C) 2010-2014 Andreas Maier,
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  package edu.stanford.rsl.conrad.physics.absorption;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  
  import edu.stanford.rsl.conrad.physics.PhysicalObject;
  import edu.stanford.rsl.conrad.physics.detector.XRayDetector;
  import edu.stanford.rsl.conrad.physics.materials.Material;
  import edu.stanford.rsl.conrad.utils.UserUtil;
  import edu.stanford.rsl.conrad.utils.VisualizationUtil;
  
  public class PathLengthHistogramCreatingAbsorptionModel extends AbsorptionModel {
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = 2227409836615130529L;
  	private boolean configured = false;
  	private boolean debug = true;
  	private AbsorptionModel internalModel;
  	private HashMap<Material,int[]> histogramMap;
  	private boolean doesNotFit = false;
  	/**
  	 * Bin size in [mm]
  	 */
  	private int binSize = 1;
  	/**
  	 * number of bins
  	 */
  	private int numberOfBins = 250;
  	
  	
  	@Override
  	public void configure() throws Exception {
  		internalModel = null;
  		binSize = UserUtil.queryInt("Enter bin size [mm]", binSize);
  		numberOfBins = UserUtil.queryInt("Enter number of Bins:", numberOfBins);
  		AbsorptionModel [] models = AbsorptionModel.getAvailableAbsorptionModels();
  		internalModel = (AbsorptionModel) UserUtil.chooseObject("Select Absorption Model: ", "Absorption Model", models, models[0]);
  		internalModel.configure();
  		histogramMap = new HashMap<Material, int[]>();
  		configured = true;
  	}
  
  	@Override
  	public boolean isConfigured() {
  		return configured;
  	}
  	
  	private synchronized void addMaterial(Material mat){
  		if (!histogramMap.containsKey(mat)){
  			int [] newHistogram = new int[numberOfBins];
  			histogramMap.put(mat, newHistogram);
  		}
  	}
  	
  	private void increment(Material mat, double pathLength){
  		int [] histo = histogramMap.get(mat);
  		if (histo == null) {
  			addMaterial(mat);
  			histo = histogramMap.get(mat);
  		}
  		int index = (int) Math.round(pathLength / binSize);
  		if (index < numberOfBins){
  			increment(histo, index);
  		} else {
  			if (!doesNotFit){
  				System.out.println("PathLengthHistogramCreatingAbsorptionModel: Histogram does not fit " + pathLength + " mm of " + mat);
  				doesNotFit = true;
  			}
  		}
  	}
  	
  	private synchronized void increment(int [] histo, int index){
  		histo[index]++;
  	}
  	
  
  	@Override
  	public double evaluateLineIntegral(ArrayList<PhysicalObject> segments) {
  		/**
  		 * Store all path lengths in the internal histogram.
  		 * Note that this part of the code is called in parallel.
  		 * Thus, it needs to be thread-safe.
  		 */
  		HashMap<Material, Double> localMap = XRayDetector.accumulatePathLenghtForEachMaterial(segments);
  		for (Material material :localMap.keySet()){
  			increment(material, localMap.get(material));
  		}
  		// use the internal model to create the correct absorption values;
  		return internalModel.evaluateLineIntegral(segments);
  	}
  
  	@Override
  	public void notifyEndOfRendering(){
  		if (debug) System.out.println("Rendering End.");
  		/**
  		 * Rendering has ended. Now, we can show the histogram contents.
  		 */
  		double [] xValues = new double [numberOfBins];
  		for (int i = 0; i < numberOfBins; i++){
  			xValues[i]=i*binSize;
  		}
  		for (Material mat: histogramMap.keySet()){
  			int [] histo = histogramMap.get(mat);
  			double [] yValues = new double [numberOfBins];
  			for (int i = 0; i < numberOfBins; i++){
  				yValues[i]=histo[i];
  			}	
  			VisualizationUtil.createPlot(xValues, yValues, "Path lengths in " + mat, "length [mm]", "count").show();
  		}
  	}
  	
  	@Override
  	public String toString() {
  		if (configured) return "Histgram of " + internalModel.toString(); 
  		return "Path length histogram creating model";
  	}
  
  	/**
  	 * @return the debug
  	 */
  	public boolean isDebug() {
  		return debug;
  	}
  
  	/**
  	 * @param debug the debug to set
  	 */
  	public void setDebug(boolean debug) {
  		this.debug = debug;
  	}
  
  	/**
  	 * @return the internalModel
  	 */
  	public AbsorptionModel getInternalModel() {
  		return internalModel;
  	}
  
  	/**
  	 * @param internalModel the internalModel to set
  	 */
  	public void setInternalModel(AbsorptionModel internalModel) {
  		this.internalModel = internalModel;
  	}
  
  	/**
  	 * @return the histogramMap
  	 */
  	public HashMap<Material, int[]> getHistogramMap() {
  		return histogramMap;
  	}
  
  	/**
  	 * @param histogramMap the histogramMap to set
  	 */
  	public void setHistogramMap(HashMap<Material, int[]> histogramMap) {
  		this.histogramMap = histogramMap;
  	}
  
  	/**
  	 * @return the doesNotFit
  	 */
  	public boolean isDoesNotFit() {
  		return doesNotFit;
  	}
  
  	/**
  	 * @param doesNotFit the doesNotFit to set
  	 */
  	public void setDoesNotFit(boolean doesNotFit) {
  		this.doesNotFit = doesNotFit;
  	}
  
  	/**
  	 * @return the binSize
  	 */
  	public int getBinSize() {
  		return binSize;
  	}
  
  	/**
  	 * @param binSize the binSize to set
  	 */
  	public void setBinSize(int binSize) {
  		this.binSize = binSize;
  	}
  
  	/**
  	 * @return the numberOfBins
  	 */
  	public int getNumberOfBins() {
  		return numberOfBins;
  	}
  
  	/**
  	 * @param numberOfBins the numberOfBins to set
  	 */
  	public void setNumberOfBins(int numberOfBins) {
  		this.numberOfBins = numberOfBins;
  	}
  
  	/**
  	 * @param configured the configured to set
  	 */
  	public void setConfigured(boolean configured) {
  		this.configured = configured;
  	}
  
  }
  