package de.ugoe.cs.quest.plugin.jfc.eventcore;

import java.util.HashMap;
import java.util.Map;

import de.ugoe.cs.quest.eventcore.IReplayable;
import de.ugoe.cs.quest.eventcore.ReplayableEvent;
import de.ugoe.cs.quest.plugin.jfc.JFCLogParser;

/**
 * <p>
 * This class defines JFC events.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class JFCEvent extends ReplayableEvent<IReplayable> {

	/**
	 * <p>
	 * Id for object serialization.
	 * </p>
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * <p>
	 * Internal map of parameters associated with the event.
	 * </p>
	 */
	private Map<String, String> parameters;

	/**
	 * <p>
	 * Information about the event source.
	 * </p>
	 */
	private Map<String, String> sourceParameters;

	/**
	 * <p>
	 * Information about the parent of the event source.
	 * </p>
	 */
	private Map<String, String> parentParameters;
	
	private boolean targetChanged = false;
	
	private int targetHash = -1;

	/**
	 * <p>
	 * Constructor. Creates a new JFCEvent.
	 * </p>
	 * 
	 * @param type
	 *            type of the event
	 */
	public JFCEvent(String type) {
		super(type);
		parameters = new HashMap<String, String>();
		sourceParameters = new HashMap<String, String>();
		parentParameters = new HashMap<String, String>();
	}

	/**
	 * <p>
	 * Adds a new parameter to the event.
	 * </p>
	 * 
	 * @param name
	 *            name of the parameter
	 * @param value
	 *            value of the parameter
	 */
	public void addParameter(String name, String value) {
		parameters.put(name, value);
	}

	/**
	 * <p>
	 * Retrieves the value of a parameter.
	 * </p>
	 * 
	 * @param name
	 *            name of the parameter
	 * @return value of the parameter
	 */
	public String getParameter(String name) {
		return parameters.get(name);
	}

	/**
	 * <p>
	 * Adds new information about the source of the event.
	 * </p>
	 * 
	 * @param name
	 *            name of the information
	 * @param value
	 *            value of the information
	 */
	public void addSourceInformation(String name, String value) {
		sourceParameters.put(name, value);
	}

	/**
	 * <p>
	 * Retrieves information about the source of the event.
	 * </p>
	 * 
	 * @param name
	 *            name of the information
	 * @return value of the information
	 */
	public String getSourceInformation(String name) {
		return sourceParameters.get(name);
	}

	/**
	 * <p>
	 * Adds new information about the parent of the source of the event.
	 * </p>
	 * 
	 * @param name
	 *            name of the information
	 * @param value
	 *            value of the information
	 */
	public void addParentInformation(String name, String value) {
		parentParameters.put(name, value);
	}

	/**
	 * <p>
	 * Used by the {@link JFCLogParser} to extend the target string of the
	 * current event with a further ancestor. The resulting target string will
	 * have the structure {@code etc.grandparent.parent.eventtarget}.
	 * </p>
	 * 
	 * @param extension
	 *            extension for the target.
	 */
	public void extendTarget(String extension) {
		if (target == null || "".equals(target)) {
			target = extension;
		} else {
			target += "." + extension;
		}
		targetChanged = true;
	}

	/**
	 * <p>
	 * Retrieves information about the parent of the source of the event.
	 * </p>
	 * 
	 * @param name
	 *            name of the information
	 * @return value of the information
	 */
	public String getParentInformation(String name) {
		return parentParameters.get(name);
	}

	/**
	 * <p>
	 * This method implements the comparison between two targets of JFCEvents.
	 * The targets are equal, if they have the same placement in the widget
	 * hierarchy, i.e., the target strings describe the same widgets, according
	 * to the implementation of widget equality provided by
	 * {@link #compareWidgets(String, String)}.
	 * </p>
	 * 
	 * @see de.ugoe.cs.quest.eventcore.Event#targetEquals(java.lang.String)
	 */
	@Override
	protected boolean targetEquals(String otherTarget) {
		return JFCTargetComparator.compare(target, otherTarget);
	}

	/**
	 * <p>
	 * The targetHashCode ignores the parts of the target that describe the
	 * title and hash of a widget, to ensure that the equals/hashCode contract
	 * is fulfilled.
	 * </p>
	 * 
	 * @see de.ugoe.cs.quest.eventcore.Event#targetHashCode()
	 */
	@Override
	protected int targetHashCode() {
		if( targetChanged || targetHash==-1 ) {
			targetHash = 0;
			int multiplier = 29;
			if (target != null) {
				String[] targetParts = target.split("\\]\\.\\[");
				if (targetParts.length == 0) {
					targetHash = widgetHashCode(target);
				} else {
					for (String widgetString : targetParts) {
						targetHash = targetHash * multiplier
								+ widgetHashCode(widgetString);
					}
				}
			}
			targetChanged = false;
		}
		return targetHash;
	}

	/**
	 * <p>
	 * This method calculates the hashCode for a a widget. If is used by
	 * {@link #targetHashCode()} to build the complete hashCode.
	 * </p>
	 * 
	 * @param widget
	 *            string describing the widget
	 * @return hashCode of the widget
	 */
	private int widgetHashCode(String widget) {
		int hashCode = 0;
		int multiplier = 37;
		String[] widgetInfo = widget.split("','");
		if (widgetInfo.length == 5) {
			hashCode = hashCode * multiplier + widgetInfo[1].hashCode();
			hashCode = hashCode * multiplier + widgetInfo[2].hashCode();
			//hashCode = hashCode * multiplier + widgetInfo[3].hashCode();
		}
		return hashCode;
	}

}
