package templates;

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.concurrent.ConcurrentMap;

import jebl.evolution.graphs.Node;
import jebl.evolution.trees.RootedTree;
import structure.Coordinates;
import utils.ThreadLocalSpreadDate;
import utils.Utils;

public class AnalyzeTree implements Runnable {

	private static final int DaysInYear = 365;

	private RootedTree currentTree;
	private String precisionString;
	private String coordinatesName;
	private String rateString;
	private double timescaler;
	private double[] sliceHeights;
	private ThreadLocalSpreadDate mrsd;
	private ConcurrentMap<Double, List<Coordinates>> slicesMap;
	private boolean useTrueNoise;

	public AnalyzeTree(RootedTree currentTree, String precisionString,
			String coordinatesName, String rateString, double[] sliceHeights,
			double timescaler, ThreadLocalSpreadDate mrsd,
			ConcurrentMap<Double, List<Coordinates>> slicesMap,
			boolean useTrueNoise) {

		this.currentTree = currentTree;
		this.precisionString = precisionString;
		this.coordinatesName = coordinatesName;
		this.rateString = rateString;
		this.sliceHeights = sliceHeights;
		this.timescaler = timescaler;
		this.mrsd = mrsd;
		this.slicesMap = slicesMap;
		this.useTrueNoise = useTrueNoise;

	}// END: Constructor

	public void run() throws ConcurrentModificationException {

		try {

			// attributes parsed once per tree
			double currentTreeNormalization = Utils.getTreeLength(currentTree,
					currentTree.getRootNode());
			double[] precisionArray = Utils.getTreeDoubleArrayAttribute(
					currentTree, precisionString);

			for (Node node : currentTree.getNodes()) {
				if (!currentTree.isRoot(node)) {

					// attributes parsed once per node
					Node parentNode = currentTree.getParent(node);

					double nodeHeight = Utils.getNodeHeight(currentTree, node);
					double parentHeight = Utils.getNodeHeight(currentTree,
							parentNode);

					double[] location = Utils.getDoubleArrayNodeAttribute(node,
							coordinatesName);

					double[] parentLocation = Utils
							.getDoubleArrayNodeAttribute(parentNode,
									coordinatesName);

					double rate = Utils
							.getDoubleNodeAttribute(node, rateString);

					for (int i = 0; i < sliceHeights.length; i++) {

						double sliceHeight = sliceHeights[i];
						
						if (nodeHeight < sliceHeight && sliceHeight <= parentHeight) {

							double[] imputedLocation = Utils.imputeValue(location,
									parentLocation, sliceHeight, nodeHeight,
									parentHeight, rate, useTrueNoise,
									currentTreeNormalization, precisionArray);

							// calculate key
							int days = (int) (sliceHeight * DaysInYear * timescaler);
							double sliceTime = mrsd.minus(days);
							
							// grow map entry if key exists
							if (slicesMap.containsKey(sliceTime)) {

								slicesMap.get(sliceTime).add(
										new Coordinates(imputedLocation[1], // longitude
												imputedLocation[0], // latitude
												0.0 // altitude
										));

								// start new entry if no such key in the map
							} else {

								List<Coordinates> coords = new ArrayList<Coordinates>();
								coords.add(new Coordinates(imputedLocation[1], // longitude
										imputedLocation[0], // latitude
										0.0 // altitude
								));

								slicesMap.put(sliceTime, coords);

							}// END: key check
							
						}// END: sliceTime check

					}// END: numberOfIntervals loop
				}// END: root node check
			}// END: node loop

		} catch (Exception e) {
			e.printStackTrace();
		}// END: try-caych block

	}// END: run

}// END: AnalyzeTree
