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

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javafx.application.Platform;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.ht.data.RealTimeDataManager;
import com.ht.svg.AbstractSVGHandler;
import com.ht.ui.GlobalSettings;
import com.ht.ui.svg.SVGViewer;

/**
 * the class of the animations handler that will animate nodes by means of the
 * values it receives from the rtdp, the functions handler, the blinking
 * handler, ...
 * 
 * @author ITRIS, Jordi SUC
 */
public class AnimatHandler extends AbstractSVGHandler {

	private static final Logger log = Logger.getLogger(AnimatHandler.class
			.getName());

	// private final Set<String> blinkingAnimatIdSet = Collections
	// .synchronizedSet(new HashSet<String>());

	private BlinkingHandler blinkingHandler = new BlinkingHandler();
	private Thread blinkingThread;

	private List<AbstractAnimationDataListener> dataChangedListeners = new ArrayList<AbstractAnimationDataListener>();
	private SVGViewer svgViewer;

	/**
	 * the invalidity notifier
	 */
	// private InvalidityNotifier invalidityNotifier;
	/**
	 * the constructor of the class
	 * 
	 * @param canvas
	 */
	public AnimatHandler(SVGViewer viewer, Document document) {
		log.config("解析SVG动画.");

		this.svgViewer = viewer;

		NodeList elems = document.getElementsByTagName("SimpleText");
		for (int i = 0; i < elems.getLength(); i++) {
			dataChangedListeners.add(new SimpleText(this, (Element) elems
					.item(i)));
		}

		elems = document.getElementsByTagName("TextOnState");
		for (int i = 0; i < elems.getLength(); i++) {
			dataChangedListeners.add(new TextOnState(this, (Element) elems
					.item(i)));
		}

		elems = document.getElementsByTagName("TextOnDateTime");
		for (int i = 0; i < elems.getLength(); i++) {
			dataChangedListeners.add(new TextOnDateTime(this, (Element) elems
					.item(i)));
		}

		elems = document.getElementsByTagName("TextOnMeasure");
		for (int i = 0; i < elems.getLength(); i++) {
			dataChangedListeners.add(new TextOnMeasure(this, (Element) elems
					.item(i)));
		}

		elems = document.getElementsByTagName("SymbolOnState");
		for (int i = 0; i < elems.getLength(); i++) {
			dataChangedListeners.add(new SymbolOnState(this, (Element) elems
					.item(i)));
		}

		elems = document.getElementsByTagName("SymbolOnMeasure");
		for (int i = 0; i < elems.getLength(); i++) {
			dataChangedListeners.add(new SymbolOnMeasure(this, (Element) elems
					.item(i)));
		}

		elems = document.getElementsByTagName("set");
		for (int i = 0; i < elems.getLength(); i++) {
			String animType = ((Element) elems.item(i)).getAttribute("name");
			if (animType
					.equals(AnimationConstants.SVG_ANIMATION_FILL_ON_EXPRESSION)) {
				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_STROKE_ON_EXPRESSION)) {
				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_VISIBILITY_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_FILL_BLINKING_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), true));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_STROKE_BLINKING_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), true));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_NORMAL_BLINKING_ON_EXPRESSION)) {

				// dataChangedListener = new
				// NormalBlinkingOnExpression(animationElement);
				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), true));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_ROTATE_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_SCALE_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_TRANSLATE_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			}
		}

		elems = document.getElementsByTagName("animate");
		for (int i = 0; i < elems.getLength(); i++) {
			String animType = ((Element) elems.item(i)).getAttribute("name");
			if (animType
					.equals(AnimationConstants.SVG_ANIMATION_FILL_ON_EXPRESSION)) {
				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_STROKE_ON_EXPRESSION)) {
				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_VISIBILITY_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_FILL_BLINKING_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), true));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_STROKE_BLINKING_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), true));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_NORMAL_BLINKING_ON_EXPRESSION)) {

				// dataChangedListener = new
				// NormalBlinkingOnExpression(animationElement);
				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), true));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_ROTATE_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_SCALE_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			} else if (animType
					.equals(AnimationConstants.SVG_ANIMATION_TRANSLATE_ON_EXPRESSION)) {

				dataChangedListeners.add(new SVGAnimationOnExpression(this,
						(Element) elems.item(i), false));
			}
		}

	}

	@Override
	public void init() {
		log.config("初始化SVG动画.");
		this.regist();
		blinkingThread = new Thread(this.blinkingHandler);
		blinkingThread.start();
	}

	@Override
	protected void regist() {

		// adding the data changed listeners
		for (AbstractAnimationDataListener listener : dataChangedListeners) {

			registerListenerOrAction(listener);
		}
	}

	@Override
	public void unregist() {
		// removing the data changed listeners
		for (AbstractAnimationDataListener listener : dataChangedListeners) {
			unregistersListenerOrAction(listener);
		}
	}

	/**
	 * disposes the animations handler
	 */
	@Override
	public void dispose() {
		log.config("注销SVG动画.");
		blinkingHandler.stop();

		// unregist();
		for (AbstractAnimationDataListener listener : dataChangedListeners) {

			listener.dispose();
		}
		dataChangedListeners.clear();
	}

	/**
	 * registers a listener to a data modification or and action
	 * 
	 * @param listenerOrAction
	 *            a listener or an action
	 */
	private void registerListenerOrAction(
			final AbstractAnimationDataListener listenerOrAction) {

		if (listenerOrAction != null) {

			RealTimeDataManager.INSTANCE
					.registDataChangeListener(listenerOrAction);

			// AbstractAnimationDataListener listener =
			// (AbstractAnimationDataListener) listenerOrAction;

			// adding the listener to the transform handler
			// if (listener.isTransformAnimation()) {
			//
			// transformHandler.addDataChangedListener(listener);
			// }

			// adding the blinking value modifiers
			// Collection<BlinkingValueModifier> blinkingValueModifiers =
			// listener.getBlinkingValueModifiers();
			//
			// if (blinkingValueModifiers != null &&
			// blinkingValueModifiers.size() > 0) {
			//
			// blinkingsHandler.addBlinkingValueModifiers(blinkingValueModifiers);
			// }

		}
	}

	/**
	 * unregisters a listener to a data modification or and action
	 * 
	 * @param listenerOrAction
	 *            a listener or an action
	 */
	private void unregistersListenerOrAction(
			final AbstractAnimationDataListener listenerOrAction) {

		if (listenerOrAction != null) {

			RealTimeDataManager.INSTANCE
					.unregistDataChangeListener(listenerOrAction);
			listenerOrAction.dispose();

			// AbstractAnimationDataListener listener =
			// (AbstractAnimationDataListener) listenerOrAction;

			// removing the blinking value modifiers
			// blinkingsHandler.removeBlinkingValueModifiers(listener.getBlinkingValueModifiers());

		}
	}

	public void blinkingReset() {
		blinkingHandler.reset();
	}

	public SVGViewer getSvgViewer() {
		return svgViewer;
	}

	void addBlinking(BlinkingModel model) {
		blinkingHandler.addNewBlinkingModel(model);
	}

	void removeBlinking(BlinkingModel model) {
		blinkingHandler.removeBlinkingModel(model);
	}

	static class BlinkingHandler implements Runnable {
		private Set<BlinkingModel> blinkingModelSet = Collections
				.synchronizedSet(new HashSet<BlinkingModel>());
		private boolean state = false;
		private boolean running = true;
		private int interval = 500;

		private final Object monitor = new Object();
		private final Object lock = new Object();

		@Override
		public void run() {

			while (running) {
				handleBlinking();
			}
		}

		private void handleBlinking() {
			if (!blinkingModelSet.isEmpty()) {

				if (GlobalSettings.INSTANCE.showBlinkingProperty().getValue()) {

					final boolean state = this.state;
					final Set<BlinkingModel> set = new HashSet<BlinkingModel>(
							blinkingModelSet);

					Platform.runLater(new Runnable() {
						public void run() {

							synchronized (lock) {

								if (state) {
									for (BlinkingModel model : set) {
										if (!running) {
											break;
										}
										model.getElement()
												.getStyle()
												.setProperty(
														model.getAttributeName(),
														model.getValue1(), "");
									}
								} else {
									for (BlinkingModel model : set) {
										if (!running) {
											break;
										}
										model.getElement()
												.getStyle()
												.setProperty(
														model.getAttributeName(),
														model.getValue2(), "");
									}
								}
							}
						}
					});

					synchronized (lock) {
						this.state = !state;
					}
				} else if (!state) {
					Platform.runLater(new Runnable() {
						public void run() {

							synchronized (lock) {

								Set<BlinkingModel> set = new HashSet<BlinkingModel>(
										blinkingModelSet);
								for (BlinkingModel model : set) {
									model.getElement()
											.getStyle()
											.setProperty(
													model.getAttributeName(),
													model.getValue2(), "");
								}
							}
						}
					});
					synchronized (lock) {
						state = true;
					}
				}

				try {
					Thread.sleep(interval);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				synchronized (monitor) {

					try {
						monitor.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}

		void addNewBlinkingModel(BlinkingModel model) {
			if (GlobalSettings.INSTANCE.showNormalBlinkingProperty().getValue()
					&& model.equals(AnimationConstants.SVG_ANIMATION_NORMAL_BLINKING_ON_EXPRESSION)) {
				return;
			}
			
			if (GlobalSettings.INSTANCE.showFillBlinkingProperty().getValue()
					&& model.equals(AnimationConstants.SVG_ANIMATION_FILL_BLINKING_ON_EXPRESSION)) {
				return;
			}
			
			if (GlobalSettings.INSTANCE.showStrokeBlinkingProperty().getValue()
					&& model.equals(AnimationConstants.SVG_ANIMATION_STROKE_BLINKING_ON_EXPRESSION)) {
				return;
			}

			blinkingModelSet.add(model);
			synchronized (monitor) {
				try {
					monitor.notifyAll();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		void removeBlinkingModel(BlinkingModel model) {
			blinkingModelSet.remove(model);
		}

		void reset() {
			Platform.runLater(new Runnable() {
				public void run() {

					Set<BlinkingModel> set = new HashSet<BlinkingModel>(
							blinkingModelSet);
					blinkingModelSet.clear();
					for (BlinkingModel model : set) {
						model.getElement()
								.getStyle()
								.setProperty(model.getAttributeName(),
										model.getValue2(), "");
					}
				}
			});
		}

		void stop() {
			running = false;
			synchronized (monitor) {
				try {
					monitor.notifyAll();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}
