package de.ugoe.cs.quest.eventcore;

import java.io.Serializable;
import java.security.InvalidParameterException;

/**
 * <p>
 * Base class for all events. An event is described by its {@link #type} and its
 * {@link #target}.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 * 
 * @param <T>
 *            Can be used to declare that events belong to a specific platform
 *            without subclassing.
 */
public class Event<T> implements Serializable {

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

	/**
	 * <p>
	 * Global start event that can be used to indicate the start of a sequence.
	 * </p>
	 */
	public static final Event<Object> STARTEVENT = new Event<Object>("START");

	/**
	 * <p>
	 * Global end event that can be used to indicate the end of a sequence.
	 */
	public static final Event<Object> ENDEVENT = new Event<Object>("END");

	/**
	 * <p>
	 * Type of the event.
	 * </p>
	 */
	protected String type;

	/**
	 * </p> Target of the event.
	 */
	protected String target = null;

	/**
	 * <p>
	 * Short description of the event target.
	 * </p>
	 */
	protected String targetShort = null;

	/**
	 * Further information about the event that shall be included in its Id.
	 */
	protected String idInfo = "";

	/**
	 * <p>
	 * Constructor. Creates a new Event with a given type.
	 * </p>
	 * 
	 * @param type
	 *            type of the event
	 */
	public Event(String type) {
		if (type == null) {
			throw new InvalidParameterException("Event type must not be null");
		}
		this.type = type;
	}

	/**
	 * <p>
	 * Two events are equal, if their {@link #type} and {@link #target} are
	 * equal.
	 * </p>
	 * <p>
	 * See {@link Object#equals(Object)} for further information.
	 * </p>
	 * 
	 * @param other
	 *            Event that is compared to this
	 * @return true, if events are equal, false otherwise
	 */
	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (other instanceof Event<?>) {
			Event<?> otherEvent = (Event<?>) other;
			if (otherEvent.canEqual(this)) {
				if (type != null) {
					return targetEquals(otherEvent.target)
							&& type.equals(otherEvent.type);
				} else {
					return targetEquals(otherEvent.target)
							&& otherEvent.type == null;
				}
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public boolean canEqual(Object other) {
		return (other instanceof Event<?>);
	}

	/**
	 * <p>
	 * Returns {@link #getStandardId()} as String representation of the event.
	 * </p>
	 * 
	 * @return String represenation of the event
	 */
	@Override
	public String toString() {
		return getStandardId();
	}

	/**
	 * Informations about the event important for its Id that is neither target
	 * nor type.
	 * 
	 * @return {@link #idInfo} of the event
	 */
	public String getIdInfo() {
		return idInfo;
	}

	/**
	 * <p>
	 * If {@link #targetShort} is set, a shortend version of the Id is returned
	 * of the form {@link #targetShort}.{@link #type}.{@link #idInfo} is
	 * returned. Otherwise the standard Id is returned (see
	 * {@link #getStandardId()}).
	 * </p>
	 * 
	 * @return if available, shortend Id string; {@link #getStandardId()}
	 *         otherwise
	 */
	public String getShortId() {
		String shortId = null;
		if (targetShort != null) {
			shortId = targetShort + "." + getType();
			if (!"".equals(idInfo)) {
				shortId += "." + idInfo;
			}
		} else {
			shortId = getStandardId();
		}
		return shortId;
	}

	/**
	 * <p>
	 * Returns the Id string of the event. It has the form {@link #target}.
	 * {@link #type}.{@link #idInfo};
	 * <p>
	 * 
	 * @return Id string of the event
	 */
	public String getStandardId() {
		String id = "";
		if (target != null) {
			id += target + ".";
		}
		id += getType();
		if (!"".equals(idInfo)) {
			id += "." + idInfo;
		}
		return id;
	}

	/**
	 * <p>
	 * Returns the {@link #target} of the event.
	 * </p>
	 * 
	 * @return {@link #target} of the event
	 */
	public String getTarget() {
		return target;
	}

	/**
	 * <p>
	 * Returns the {@link #targetShort} of the event.
	 * </p>
	 * 
	 * @return {@link #targetShort} of the event
	 */
	protected String getTargetShort() {
		return targetShort;
	}

	/**
	 * <p>
	 * Returns the {@link #type} of the event.
	 * </p>
	 * 
	 * @return {@link #type} of the event
	 */
	public String getType() {
		return type;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		int multiplier = 17;
		int hash = 42;
		if (type != null) {
			hash = multiplier * hash + type.hashCode();
		}
		hash = multiplier * hash + targetHashCode();

		return hash;
	}

	/**
	 * <p>
	 * Sets the {@link #idInfo} of the event. The idInfo is optional and
	 * contains information important for the event's Id that is neither target
	 * nor type.
	 * </p>
	 * 
	 * @param info
	 *            {@link #idInfo} of the event
	 */
	public void setIdInfo(String info) {
		idInfo = info;
	}

	/**
	 * <p>
	 * Sets the target of the event. Once set, the target cannot be changed.
	 * </p>
	 * 
	 * @param target
	 *            target of the event
	 * @return true, if target was changed, false otherwise
	 */
	public boolean setTarget(String target) {
		if (this.target != null) {
			return false;
		}
		this.target = target;
		return true;
	}

	/**
	 * <p>
	 * Sets the short description of the event target. Once set, the target
	 * cannot be changed.
	 * </p>
	 * 
	 * @param targetShort
	 *            short target description
	 * @return true, if target was changed, false otherwise
	 */
	public boolean setTargetShort(String targetShort) {
		if (this.targetShort != null) {
			return false;
		}
		this.targetShort = targetShort;
		return true;
	}

	/**
	 * <p>
	 * This function is used by {@link #equals(Object)} to determine if the
	 * targets of both events are equal. The standard implementation provided by
	 * this class performs a String comparison between the target strings.
	 * </p>
	 * <p>
	 * Subclasses can override this method to implemented more sophisticated
	 * means for the target comparison, e.g., to account for changes in the
	 * title of a widget.
	 * </p>
	 * 
	 * @param otherTarget
	 *            other target string to which the target if this event is
	 *            compared to
	 * @return true if the targets are equals; false otherwise
	 */
	protected boolean targetEquals(String otherTarget) {
		boolean retVal;
		if (target != null) {
			retVal = target.equals(otherTarget);
		} else {
			retVal = (otherTarget == null);
		}
		return retVal;
	}

	/**
	 * <p>
	 * This function is used by {@link #hashCode()} to determine how the hash of
	 * the {@link #target}. It has to be overridden by subclasses that implement
	 * {@link #targetEquals(String)}, to ensure that the equals/hashCode
	 * contract remains valid.
	 * </p>
	 * 
	 * @return hash of the target
	 */
	protected int targetHashCode() {
		if (target != null) {
			return target.hashCode();
		} else {
			return 0;
		}
	}
}
