package edu.uci.lighthouse.model;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Temporal;

import org.apache.log4j.Logger;
import org.hibernate.annotations.GenerationTime;

/**
 *  Select others "new" events by using the timestamp as a parameter
 * */
@NamedQueries ({
	@NamedQuery(name = "LighthouseEvent.findByTimestamp",
				query = "SELECT event " + 
						"FROM LighthouseEvent event " + 
						"WHERE ( event.timestamp > :timestamp " +
						"OR event.committedTime > :timestamp ) " +
						"AND event.author <> :author")
})

/**
 * Represents a event (ADD, REMOVE, MODIFY) generated by a user.
 */
@Entity
public class LighthouseEvent implements Serializable{
	
	private static final long serialVersionUID = 7247791395122774431L;

	private static Logger logger = Logger.getLogger(LighthouseEvent.class);

	@SequenceGenerator(name="Event_Gen", sequenceName="Event_Gen")
	@Id @GeneratedValue(generator="Event_Gen")
	private Integer id;

	/** User that generates the event. */
	@OneToOne(cascade = CascadeType.ALL)
	private LighthouseAuthor author;

	@Column(name="TIMESTAMP", insertable=false, updatable=false, columnDefinition="timestamp default current_timestamp")
	@org.hibernate.annotations.Generated(value=GenerationTime.INSERT)
	@Temporal(javax.persistence.TemporalType.TIMESTAMP)
	private Date timestamp;

	@OneToOne(cascade = CascadeType.ALL)
	private LighthouseEntity entity = null;

	@OneToOne(cascade = CascadeType.ALL)
	private LighthouseRelationship relationship = null;
	
	private boolean isCommitted = false; 
	
	private Date committedTime = new Date(0);
	
	/** Type of the event. */
	public static enum TYPE {
		ADD, REMOVE, MODIFY, CUSTOM
	}

	private TYPE type;	

	protected LighthouseEvent() {
	}
	
	/**
	 * Create an event
	 * 
	 * @param type
	 * 		Event Type
	 * 
	 * @param author
	 * 		User that generates the Event
	 * 
	 * @param artifact
	 * 		{@link LighthouseEntity}
	 * 		OR
	 * 		{@link LighthouseRelationship}
	 * */
	public LighthouseEvent(TYPE type, LighthouseAuthor author, Object artifact) {
		this.type = type;
		this.author = author;
		this.setArtifact(artifact);
	}
	
	protected void setId(Integer id) {
		this.id = id;
	}

	public Integer getId() {
		return id;
	}
	
	public TYPE getType() {
		return type;
	}
	
	public LighthouseAuthor getAuthor() {
		return author;
	}

	public void setAuthor(LighthouseAuthor author) {
		this.author = author;
	}
	
	public void setTimestamp(Date timestamp) {
		this.timestamp = timestamp;
	}

	/** @return Can return NULL*/
	public Date getTimestamp() {
		return timestamp;
	}
	
	/**
	 * Get the Artifact related with this event
	 * 
	 * @return
	 * 		{@link LighthouseEntity}
	 * 		OR
	 * 		{@link LighthouseRelationship}
	 * */
	public Object getArtifact() {
		if (entity == null && relationship == null) {
			logger.error(this.toString() + " has a NULL artifact");
			return null;
		} else if (entity != null) {
			return entity;
		} else {
			return relationship;
		}
	}

	/**
	 * Set the Artifact related with this event
	 * 
	 * @param obj
	 * 		{@link LighthouseEntity}
	 * 		OR
	 * 		{@link LighthouseRelationship}
	 * */
	protected void setArtifact(Object obj) {
		if (obj instanceof LighthouseEntity) {
			if (relationship == null) {
				this.entity = (LighthouseEntity) obj;
			}
		} else if (obj instanceof LighthouseRelationship) {
			if (entity == null) {
				this.relationship = (LighthouseRelationship) obj;
			}
		}
	}

	public void setCommitted(boolean committed) {
		this.isCommitted = committed;
	}

	public boolean isCommitted() {
		return isCommitted;
	}

	public void setCommittedTime(Date committedTime) {
		this.committedTime = committedTime;
	}

	public Date getCommittedTime() {
		return committedTime;
	}

	@Override
	public String toString() {
		return 
		"LighthouseEvent [" 
		+ "getArtifact()=" + getArtifact()
		+ ", type=" + type
		+ ", timestamp=" + timestamp 
		+ ", committedTime=" + committedTime
		+ ", isCommitted=" + isCommitted
		+ ", author=" + author 
		+ "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((entity == null) ? 0 : entity.hashCode());
		result = prime * result
				+ ((relationship == null) ? 0 : relationship.hashCode());
		result = prime * result + ((type == null) ? 0 : type.hashCode());
		return result;
	}

	/** TODO I removed the "timestamp" from the equals() because I had some
	* problems to compare 2 EQUAL models that was generated in different times
	* The "isCommitted" and "committedTime" is not here because they can be updated,
	* what does not mean that this is a new event
	* */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		LighthouseEvent other = (LighthouseEvent) obj;
		if (author == null) {
			if (other.author != null)
				return false;
		} else if (!author.equals(other.author))
			return false;
		if (entity == null) {
			if (other.entity != null)
				return false;
		} else if (!entity.equals(other.entity))
			return false;
		if (relationship == null) {
			if (other.relationship != null)
				return false;
		} else if (!relationship.equals(other.relationship))
			return false;
		if (type == null) {
			if (other.type != null)
				return false;
		} else if (!type.equals(other.type))
			return false;
		return true;
	}

}

@@ -1722,16 +1722,30 @@
 imestamp
+ = new Date(0)
 ;%0D%0A%0D%0A%09@O

event can not be null