/********************************************************************
 * Copyright © 2005 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.service.trigger;

import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.security.SecurityContextHelper;
import gov.va.med.fw.security.UserCredentials;
import gov.va.med.fw.service.ConfigurationConstants;

import java.io.Serializable;
import java.util.Properties;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * Base implementation for representing a triggerable event. 
 * 
 * @author DNS   BOHMEG
 */
public class TriggerEvent extends AbstractEntity {
	private static final String CONSUMER_ROUTING_KEY = ConfigurationConstants.DEFAULT_MESSAGE_TYPE;

	private static final String DESTINATION_TYPE = "Destination_Type"; // can be used for MDB selector

	
	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -591185356879397839L;

	private String name;

	/**
	 * The limited security information for the initiater that is triggering
	 * this event
	 */
	private UserCredentials initiater;

	/**
	 * Event metrics (should not affect equals and hashCode implementations)
	 * TODO: use JMX to track the life of the TriggerEvent
	 */
	private TriggerEventMetrics eventMetrics;

	/**
	 * Potentially complex data type that contains the event information
	 * that is not contextual (ie, for every event of the same type, this will be equal)
	 */
	private TriggerIdentity triggerIdentity;

	/**
	 * Contains the event information that is contextual (ie, does not define
	 * the type of event but rather what object is involved in the event)
	 */
	private Serializable payload;

	/**
	 * @return Returns the initiater.
	 */
	public UserCredentials getInitiater() {
		return initiater;
	}

	/**
	 * @param initiater
	 *            The initiater to set.
	 */
	public void setInitiater(UserCredentials initiater) {
		this.initiater = initiater;
	}

	public TriggerEvent(String name, Serializable payload, TriggerIdentity triggerIdentity) {
		if (name == null)
			throw new IllegalArgumentException(
					"TriggerEvent must have non-null name");

		this.name = name;
		this.payload = payload;
		this.triggerIdentity = triggerIdentity;
		this.eventMetrics = new TriggerEventMetrics();
		this.eventMetrics.createdTimestamp = System.currentTimeMillis();		
	}
	
	public TriggerEvent(String name, Serializable payload) {
		this(name, payload, createTriggerIdentity(TriggerIdentity.DestinationType.DEFAULT));
	}

	public TriggerEvent(String name) {
		this(name, null);
	}

	public TriggerEvent(TriggerIdentity triggerIdentity) {
		this((triggerIdentity != null ? triggerIdentity.getName() : null), null, triggerIdentity);		
	}
	
	public static final TriggerIdentity createTriggerIdentity(TriggerIdentity.DestinationType destType) {
		return new BaseTriggerIdentity(destType);
	}

	/**
	 * @return Returns the name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            The name to set.
	 */
	public void setName(String name) {
		if (name == null)
			throw new IllegalArgumentException(
					"TriggerEvent must have non-null name");

		this.name = name;
	}

	protected void buildToString(ToStringBuilder builder) {
		builder.append("name", name);
		builder.append("payload", payload);
		builder.append("initiater", initiater);
		builder.append("triggerIdentity", triggerIdentity);
		builder.append("eventMetrics", eventMetrics);
	}

	/**
	 * @return Returns the triggerIdentity.
	 */
	public TriggerIdentity getTriggerIdentity() {
		return triggerIdentity;
	}
	
	/* singular method for defining the TriggerEvent context */
	public String getTriggerContext() {
        ToStringBuilder builder = new ToStringBuilder(this,
                ToStringStyle.MULTI_LINE_STYLE);

        this.buildContextString(builder);

        return builder.toString();
 
	}
	
	protected void buildContextString(ToStringBuilder builder) {
		builder.append("name", name);
		builder.append("initiater", SecurityContextHelper.getUserName(initiater));
	}

	/**
	 * @param triggerIdentity
	 *            The triggerIdentity to set.
	 */
	public void setTriggerIdentity(TriggerIdentity triggerIdentity) {
		this.triggerIdentity = triggerIdentity;
	}

	public final void preTriggerInit() {
		if (eventMetrics != null) {
			eventMetrics.routedTimestamp = System.currentTimeMillis();
		}
		preTrigger();
	}
	
	public long getCreationDate() {
		return eventMetrics != null ? eventMetrics.createdTimestamp : 0;
	}

	/** Can be overridden by subclasses */
	protected void preTrigger() {
		// do nothing
	}

	protected void preConsumedInit() {
		if (eventMetrics != null) {
			eventMetrics.consumedTimestamp = System.currentTimeMillis();
		}
	}

	public String getConsumerRoutingKey() {
		// routing is used for communicating to the consumer what service should be invoked
		String routingKey = (getTriggerIdentity() != null && getTriggerIdentity().isUsedForConsumerRouting()) ? getTriggerIdentity()
				.getClass().getName() : getName();
		if (routingKey == null)
			throw new IllegalStateException(
					"Invalid TriggerEvent; getConsumerRoutingKey() can not return null");
		return routingKey;
	}
	
	public Properties getRoutingProperties() {
		Properties props = new Properties();
		props.put(DESTINATION_TYPE, getTriggerIdentity()
				.getDestinationType().getName());
		props.put(CONSUMER_ROUTING_KEY, getConsumerRoutingKey());
		return props;
	}

	/**
	 * @return the payload
	 */
	public Serializable getPayload() {
		return payload;
	}

	/**
	 * @param payload the payload to set
	 */
	public void setPayload(Serializable payload) {
		this.payload = payload;
	}	
}
