package de.ugoe.cs.eventbench.data;

import java.security.InvalidParameterException;
import java.util.LinkedList;
import java.util.List;

import de.ugoe.cs.eventbench.IReplayDecorator;

/**
 * <p>
 * Subclass of {@link Event} for events that contain all informations required
 * for replaying them, i.e., generating scripts that can used for automated
 * software execution.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 * 
 * @param <T>
 *            Allows only types that extend {@link IReplayable} and is used to
 *            define a list of replayables that describe the replay of the
 *            event.
 */
public class ReplayableEvent<T extends IReplayable> extends Event<T> {

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

	/**
	 * <p>
	 * List of {@link IReplayable}s of type T that describes the replay of an
	 * event. The {@link IReplayable}s can be interpreted as <it>sub-events</it>
	 * on the platform level that make up the abstract event.
	 * </p>
	 */
	protected List<T> replayEvents = new LinkedList<T>();;

	/**
	 * <p>
	 * Defines whether the replay is valid or invalid. It may be invalid, e.g.,
	 * due to errors during the generation of the event or lack of vital
	 * information.
	 * </p>
	 */
	protected boolean replayValid = true;

	/**
	 * <p>
	 * {@link IReplayDecorator} used when replays of this type are written.
	 * </p>
	 */
	protected IReplayDecorator decorator = null;

	/**
	 * <p>
	 * Constructor. Creates a new event with the given type.
	 * <p>
	 * 
	 * @param type
	 *            type of the event
	 * @see Event#Event(String)
	 */
	public ReplayableEvent(String type) {
		super(type);
	}

	/**
	 * <p>
	 * Adds a new {@link IReplayable} of type T to the replay sequence.
	 * </p>
	 * 
	 * @param replayable
	 *            element that is added to the sequence
	 * @throws InvalidParameterException
	 *             thrown is replayable is null
	 */
	public void addReplayEvent(T replayable) {
		if (replayable == null) {
			throw new InvalidParameterException("replayble must not be null");
		}
		replayEvents.add(replayable);
	}

	/**
	 * <p>
	 * Adds a {@link List}ist of {@link IReplayable} to the replay sequence.
	 * </p>
	 * 
	 * @param generatedReplaySeq
	 *            {@link List} that is added to the sequence
	 * @throws InvalidParameterException
	 *             thrown if generatedReplaySeq is null
	 */
	public void addReplaySequence(List<T> generatedReplaySeq) {
		if (generatedReplaySeq == null) {
			throw new InvalidParameterException(
					"generatedReplaySeq must not be null");
		}
		replayEvents.addAll(generatedReplaySeq);
	}

	/**
	 * <p>
	 * Returns the {@link IReplayDecorator} of the event.
	 * </p>
	 * 
	 * @return {@link IReplayDecorator} of the event; null if no decorator has
	 *         been set
	 */
	public IReplayDecorator getReplayDecorator() {
		return decorator;
	}

	/**
	 * <p>
	 * Returns a the list of replay events.
	 * </p>
	 * <p>
	 * The return value is a copy of the list used internally!
	 * </p>
	 * 
	 * @return list of replay events.
	 */
	public List<T> getReplayMessages() {
		return new LinkedList<T>(replayEvents);
	}

	/**
	 * <p>
	 * Returns whether the replay is valid or not.
	 * </p>
	 * 
	 * @return true, if replay is valid; false otherwise.
	 */
	public boolean hasValidReplay() {
		return replayValid;
	}

	/**
	 * <p>
	 * Marks the replay as invalid. Once marked as invalid, it remains so and
	 * cannot be changed back to valid.
	 * </p>
	 */
	public void invalidateReplay() {
		replayValid = false;
	}

	/**
	 * <p>
	 * Sets the {@link IReplayDecorator} associated with the event.
	 * </p>
	 * 
	 * @param decorator
	 *            decorator associated with the event
	 */
	public void setDecorator(IReplayDecorator decorator) {
		this.decorator = decorator;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.eventbench.data.Event#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (other instanceof ReplayableEvent<?>) {
			ReplayableEvent<?> otherEvent = (ReplayableEvent<?>) other;
			if (replayEvents != null) {
				return super.equals(otherEvent)
						&& replayEvents.equals(otherEvent.replayEvents)
						&& replayValid == otherEvent.replayValid;
			} else {
				return super.equals(otherEvent)
						&& otherEvent.replayEvents == null
						&& replayValid == otherEvent.replayValid;
			}

		} else {
			return false;
		}
	}
	
	@Override
	public boolean canEqual(Object other) {
		return (other instanceof ReplayableEvent<?>);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.eventbench.data.Event#hashCode()
	 */
	@Override
	public int hashCode() {
		int multiplier = 17;
		int hash = super.hashCode();
		if (replayEvents != null) {
			hash = multiplier * hash + replayEvents.hashCode();
		}
		hash = multiplier * hash + (replayValid ? 1 : 0);

		return hash;
	}
}
