/*
 * Created on 27 janv. 2005
 */
package com.ht.svg.animat;

import java.util.LinkedList;
import java.util.List;

import javafx.application.Platform;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.ht.data.RealTimeDataManager;
import com.ht.data.util.Toolkit;
import com.ht.svg.DataLimit;
import com.ht.svg.util.DomUtil;

/**
 * the listener to the changes of data for the "symbol on measure" animation
 * node
 * 
 * @author ITRIS, Jordi SUC
 */
public class SymbolOnMeasure extends AbstractAnimationDataListener {

	private static final Logger log = LoggerFactory
			.getLogger(SymbolOnMeasure.class);
	/**
	 * the tag min and tag max
	 */
	private DataLimit tagMin, tagMax;
	/**
	 * the computed value of the tag, tagMin and tagMax attributes
	 */
	private double tagValue = Double.NaN, tagMinValue = Double.MIN_VALUE,
			tagMaxValue = Double.MAX_VALUE;
	/**
	 * the symbols for the invalid an out of range symbols
	 */
	private String defaultSymbol = "", outOfRangeSymbol = "";

	/**
	 * the last text value
	 */
	private String lastSymbol = null;
	/**
	 * the list of the objects containing all information about the child of an
	 * animation node
	 */
	private List<AnimationChildValues> childrenAttributeValues = new LinkedList<AnimationChildValues>();
	// private String id = null;
	private String tagVarName = "";

	/**
	 * the constructor of the class
	 * 
	 * @param animationHandler
	 * @param animationElement
	 *            an animation node
	 */
	public SymbolOnMeasure(AnimatHandler animationHandler,
			Element animationElement) {

		super(animationHandler, animationElement);
		// id = ID_PREFIX + DomUtil.generateID();
		// parentElement.setAttribute("id", id);

		if (dataNames.isEmpty()) {
			log.error("未设置变量!!!!!!!");
			return;
		}
		tagVarName = dataNames.iterator().next();

		// getting the tagMin and tagMax
		tagMin = new DataLimit(animationElement.getAttribute("tagMin"), true);
		tagMax = new DataLimit(animationElement.getAttribute("tagMax"), false);

		// getting the tag min and tag max values
		if (tagMin.isAbsoluteValue()) {

			tagMinValue = tagMin.getAbsoluteValue();
		}

		if (tagMax.isAbsoluteValue()) {

			tagMaxValue = tagMax.getAbsoluteValue();
		}

		// storing the values of the attributes of the animation node
		defaultSymbol = animationElement.getAttribute("defaultValueAttr");
		outOfRangeSymbol = animationElement.getAttribute("outOfRangeSymbol");

		// getting children information
		int i = 0;
		AnimationChildValues animationChildValues = null;

		for (Node cur = animationElement.getFirstChild(); cur != null; cur = cur
				.getNextSibling()) {

			if (cur instanceof Element) {

				// creating the animations child values
				animationChildValues = new AnimationChildValues(
						cur.getNodeName() + i, (Element) cur);

				if (animationChildValues.isUsed()) {

					childrenAttributeValues.add(animationChildValues);
				}

				i++;
			}
		}

	}

	/**
	 * the method called when the data to which the listener is registered, is
	 * modified
	 */
	@Override
	public void dataChanged(String ...varNames) {

		if (tagVarName == null) {
			log.warn("未设置动画变量");
			return;
		}

		// whether the current state of the listener is invalid or not
		boolean isInvalidState = false;

		// getting the value of the tag//
		tagValue = Toolkit.getNumber(RealTimeDataManager.INSTANCE
				.getVarValue(tagVarName));

		// refreshing the min and max values//
		if (tagMin.isTag()) {
			tagMinValue = Toolkit.getNumber(RealTimeDataManager.INSTANCE
					.getVarValue(tagMin.getTag()));
		}

		if (tagMax.isTag()) {
			tagMaxValue = Toolkit.getNumber(RealTimeDataManager.INSTANCE
					.getVarValue(tagMax.getTag()));
		}

		// the new symbol
		String newSymbol = "";

		// whether the tag value is in one of the intervals or not
		boolean isInInterval = false;

		if (!Double.isNaN(tagValue) && !Double.isNaN(tagMinValue)
				&& !Double.isNaN(tagMaxValue) && tagMaxValue >= tagMinValue) {

			// refreshing the min and max value of each interval
			for (AnimationChildValues child : childrenAttributeValues) {

				child.refreshValues();
			}

			// for each child of the animation node, checks if the current value
			// is inside a child's interval//
			for (AnimationChildValues child : childrenAttributeValues) {

				if (child != null) {

					isInvalidState = isInvalidState || child.isChildInvalid();
				}

				if (child != null && child.isInInterval(tagValue)) {

					newSymbol = child.getSymbol();
					isInInterval = true;
					break;
				}
			}

			if (!isInInterval) {

				// the tag value is out of range
				newSymbol = outOfRangeSymbol;
			}

		} else {

			// the state is invalid//
			newSymbol = defaultSymbol;
			isInvalidState = true;
		}

		// displaying the child corresponding to the new symbol
		if (newSymbol != null && !newSymbol.isEmpty()
				&& !newSymbol.equals(lastSymbol)) {

			try {
				DomUtil.activeSymbolAnimat(parentElement, newSymbol);
				lastSymbol = newSymbol;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * the class storing information on a the child of an animation node that
	 * describes an interval
	 * 
	 * @author ITRIS, Jordi SUC
	 */
	protected class AnimationChildValues {

		/**
		 * whether this child should be considered or not
		 */
		private boolean isUsed = false;
		/**
		 * whether the value of the tag of the animation node could be equal to
		 * the min
		 */
		private boolean equal1 = false;
		/**
		 * whether the value of the tag of the animation node could be equal to
		 * the max
		 */
		private boolean equal2 = false;
		/**
		 * the min and max values
		 */
		private DataLimit min = null, max = null;
		/**
		 * the value of the min and the max
		 */
		private double minValue = Double.NaN, maxValue = Double.NaN;
		/**
		 * the symbol linked with this child
		 */
		private String symbol = "";
		/**
		 * the id of the child
		 */
		private String childId = "";

		/**
		 * the constructor of the class
		 * 
		 * @param childId
		 *            the id of the child
		 * @param animationChildElement
		 *            a child of the animation node
		 */
		protected AnimationChildValues(String childId,
				Element animationChildElement) {

			this.childId = childId;

			if (animationChildElement != null) {

				// whether this child should be used
				String used = animationChildElement.getAttribute("used");

				if (used != null && used.equals("true")) {

					isUsed = true;
				}

				// getting the equal1 and equal2 attributes
				String eq1 = animationChildElement.getAttribute("equal1"), eq2 = animationChildElement
						.getAttribute("equal2");

				if (eq1 != null && !eq1.equals("false")) {

					equal1 = true;
				}

				if (eq2 != null && !eq2.equals("false")) {

					equal2 = true;
				}

				// the symbol attribute
				symbol = animationChildElement.getAttribute("symbol");

				// getting the min and max
				String minStr = animationChildElement.getAttribute("min"), maxStr = animationChildElement
						.getAttribute("max");

				min = new DataLimit(minStr, true);
				max = new DataLimit(maxStr, false);

				// computing the values of the min and max
				if (min.isAbsoluteValue()) {

					setMinValue(min.getAbsoluteValue());
				}

				if (max.isAbsoluteValue()) {

					setMaxValue(max.getAbsoluteValue());
				}

				if (min.isInfinite()) {

					setMinValue(Double.NEGATIVE_INFINITY);
				}

				if (max.isInfinite()) {

					setMaxValue(Double.POSITIVE_INFINITY);
				}

				refreshPercentValues();

			}
		}

		/**
		 * @return the id of this child object
		 */
		public String getChildId() {
			return childId;
		}

		/**
		 * @return whether the child tags are invalid
		 */
		public boolean isChildInvalid() {

			return (Double.isNaN(minValue) || Double.isNaN(maxValue));
		}

		/**
		 * @param maxValue
		 *            The maxValue to set.
		 */
		private void setMaxValue(double maxValue) {
			this.maxValue = maxValue;
		}

		/**
		 * @param minValue
		 *            The minValue to set.
		 */
		private void setMinValue(double minValue) {
			this.minValue = minValue;
		}

		/**
		 * refreshes the values
		 */
		public void refreshValues() {

			// re-computing the values of the min and max//
			if (!Double.isNaN(tagMinValue) && !Double.isNaN(tagMaxValue)) {

				refreshPercentValues();

				// computing the tag value
				if (min.isTag()) {

					setMinValue(Toolkit.getNumber(RealTimeDataManager.INSTANCE
							.getVarValue(min.getTag())));
				}

				if (max.isTag()) {

					setMaxValue(Toolkit.getNumber(RealTimeDataManager.INSTANCE
							.getVarValue(max.getTag())));
				}
			}
		}

		/**
		 * refreshes the percent values
		 */
		private void refreshPercentValues() {

			// computing the percentage values
			double range = tagMaxValue - tagMinValue;

			if (min.isPercentValue()) {

				setMinValue(tagMinValue + range * min.getPercentValue() / 100);
			}

			if (max.isPercentValue()) {

				setMaxValue(tagMinValue + range * max.getPercentValue() / 100);
			}
		}

		/**
		 * whether the given value is in this interval
		 * 
		 * @param theTagValue
		 *            the value of the main tag of the animation node
		 * @return whether the given value is in this interval
		 */
		public boolean isInInterval(double theTagValue) {

			// tests if the given value is in the interval
			boolean isInInterval = true;

			if (!Double.isNaN(tagMinValue) && !Double.isNaN(tagMaxValue)
					&& !Double.isNaN(minValue) && !Double.isNaN(maxValue)) {

				if (equal1) {

					isInInterval = isInInterval && (minValue <= theTagValue);

				} else {

					isInInterval = isInInterval && (minValue < theTagValue);
				}

				if (equal2) {

					isInInterval = isInInterval && (maxValue >= theTagValue);

				} else {

					isInInterval = isInInterval && (maxValue > theTagValue);
				}

			} else {

				isInInterval = false;
			}

			return isInInterval;
		}

		/**
		 * @return Returns the equal1.
		 */
		public boolean isEqual1() {
			return equal1;
		}

		/**
		 * @return Returns the equal2.
		 */
		public boolean isEqual2() {
			return equal2;
		}

		/**
		 * @return Returns the isUsed.
		 */
		public boolean isUsed() {
			return isUsed;
		}

		/**
		 * @return Returns the max.
		 */
		public DataLimit getMax() {
			return max;
		}

		/**
		 * @return Returns the min.
		 */
		public DataLimit getMin() {
			return min;
		}

		/**
		 * @return Returns the symbol
		 */
		public String getSymbol() {
			return symbol;
		}
	}
}