/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.agenda.ejb.entity.beans;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import bizcal.util.DateUtil;


/**
 * An Appointment is a single event in a calendar.
 * See properties for details.
 * 
 * An appointment can be recurring. For that there are two methods to use.
 * setFrequency sets the number of times it should recur
 * setRRule sets the way how it should recur, e.G. daily,weekly, etc. It is a string containing
 * parameters like
 * <ul>
 * <li>	public static final String INTERVAL 	= "INTERVAL";</li>
 * <li>public static final String BYDAY 		= "BYDAY";</li>
 * <li>public static final String BY_MONTH_DAY  = "BYMONTHDAY";</li>
 * <li>public static final String BY_MONTH 	    = "BYMONTH";</li>
 * </ul>
 * 
 * See lu.tudor.santec.gecamed.agenda.gui.widgets.recurrence.RRule.
 * Additionally you can set a date until the appointment should recur. (untilDate).
 * 
 * @author martin.heinemann@tudor.lu
 * 17.01.2008
 * 14:21:04
 *
 *
 * @version
 * <br>$Log: Appointment.java,v $
 * <br>Revision 1.26  2011-10-05 10:57:43  troth
 * <br>Ticket #894
 * <br>add: Log message string formatting
 * <br>
 * <br>Revision 1.25  2011-10-05 09:17:03  troth
 * <br>clearing code
 * <br>
 * <br>Revision 1.24  2011-09-22 15:28:52  troth
 * <br>Fist Version of function 'logging Agenda modifications in the admin log' Ticket #894 Logging for Agenda modifications
 * <br>
 * <br>Revision 1.23  2010-03-19 16:33:59  hermen
 * <br>sort searched appointments by date
 * <br>
 * <br>Revision 1.22  2008-10-21 15:42:56  heinemann
 * <br>own clone method. because the clone of GECAMedEntityBean does not care about all properties.
 * <br>
 * <br>Revision 1.21  2008-10-07 09:33:30  heinemann
 * <br>GECAMedEntityBean now implements Comparable
 * <br>
 * <br>Revision 1.20  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.19  2008-01-18 16:09:05  heinemann
 * <br>code cleanup and java doc
 * <br>
 *   
 */
@javax.persistence.NamedQueries
({
	@NamedQuery(name=Appointment.FIND_APPOINTMENTS,
			query="SELECT Object(o) FROM Appointment o "
				+ "WHERE o.calendarId in (:calendarIds) "
				+ "AND ( UPPER(o.summary) LIKE UPPER(:searchString) "
					+ "OR UPPER(o.description) LIKE UPPER(:searchString) ) "
				+ "ORDER BY o.startDate DESC"),
	@NamedQuery(name=Appointment.NO_OF_NATIONAL_HOLIDAYS_FOR_DAY,
			query="SELECT COUNT(o.id) FROM NationalHoliday o WHERE o.date = :date")
})

@Entity
@Table(name = "appointment", schema = "agenda")
public class Appointment extends GECAMedEntityBean implements Serializable {
	
	public static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	public static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyyMMddHHmmss");
	
	public static final String FIND_APPOINTMENTS = "FIND_APPOINTMENTS";
	public static final String NO_OF_NATIONAL_HOLIDAYS_FOR_DAY = "noOfNationalHolidaysForDay";

	// Fields
	public static final int 	NO_RECUR 	= 0;
	public static final int 	DAILY 	 	= 1;
	public static final int 	WEEKLY 	 	= 2;
	public static final int 	MONTHLY  	= 3;
	public static final int 	YEARLY 	 	= 4;
	
	public static final String 	STR_DAILY 	= "DAILY"; 
	public static final String 	STR_WEEKLY 	= "WEEKLY";
	public static final String 	STR_MONTHLY = "MONTHLY";
	public static final String 	STR_YEARLY 	= "YEARLY";

	
	// =======================================
	// Rule Engine strings
	public static final String INTERVAL 	= "INTERVAL";
	public static final String BYDAY 		= "BYDAY";
	public static final String BY_MONTH_DAY = "BYMONTHDAY";
	public static final String BY_MONTH 	= "BYMONTH";
	
	private static final long serialVersionUID = 1L;
	
	public static final String DESC_UID 	= "UID";
	public static final String DESC_CANCEL 	= "CANCEL";

	private Integer typeId;

	private Integer calendarId;

	private Date startDate;

	private Date endDate;

	private Integer patientId;

	private String summary;

	private String description;

	private Integer frequency;

	private String rRule;

	private Date until;

	private Integer status;

	private Boolean isPrivate;

	private Date created;

	private Integer createdBy;

	private Boolean isBackground;
	
	private Date modified;
	
	private Integer modifiedBy;

	private Appointment origAppointment;

//	private Date notifyDate;
	
	// Constructors

	/** default constructor */
	public Appointment() {
	}

	// Property accessors
	@Column(name = "type_id", unique = false, nullable = false, insertable = true, updatable = true)
	public Integer getTypeId() {
		return this.typeId;
	}

	public void setTypeId(Integer typeId) {
		this.typeId = typeId;
	}

	@Column(name = "calendar_id", unique = false, nullable = false, insertable = true, updatable = true)
	public Integer getCalendarId() {
		return this.calendarId;
	}

	public void setCalendarId(Integer calendarId) {
		this.calendarId = calendarId;
	}
	
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "start_date", unique = false, nullable = false, insertable = true, updatable = true, length = 8)
	public Date getStartDate() {
		return this.startDate;
	}

	public void setStartDate(Date startDate) {
		try {
			this.startDate = new Date(startDate.getTime());
		} catch (Exception e) {
		}
	}

	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "end_date", unique = false, nullable = false, insertable = true, updatable = true, length = 8)
	public Date getEndDate() {
		return this.endDate;
	}

	public void setEndDate(Date endDate) {
		try {
			this.endDate = new Date(endDate.getTime());
		} catch (Exception e) {
		}
	}

	@Column(name = "patient_id", unique = false, nullable = true, insertable = true, updatable = true)
	public Integer getPatientId() {
		return this.patientId;
	}

	public void setPatientId(Integer patientId) {
		this.patientId = patientId;
	}

	@Column(name = "summary", unique = false, nullable = true, insertable = true, updatable = true, length = 100)
	public String getSummary() {
		return this.summary;
	}

	public void setSummary(String summary) {
		this.summary = summary;
	}

	@Column(name = "description", unique = false, nullable = true, insertable = true, updatable = true)
	public String getDescription() {
		return this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Column(name = "frequency", unique = false, nullable = true, insertable = true, updatable = true)
	public Integer getFrequency() {
		if (frequency == null)
			return NO_RECUR;
		return this.frequency;
	}

	public void setFrequency(Integer frequency) {
		this.frequency = frequency;
	}

	@Column(name = "recuring_rule", unique = false, nullable = true, insertable = true, updatable = true)
	public String getRRule() {
		return this.rRule;
	}

	public void setRRule(String rRule) {
		this.rRule = rRule;
	}

	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "until", unique = false, nullable = true, insertable = true, updatable = true, length = 4)
	public Date getUntil() {
		return this.until;
	}

	public void setUntil(Date until) {
		this.until = until;
	}

	@Column(name = "status", unique = false, nullable = true, insertable = true, updatable = true)
	public Integer getStatus() {
		return this.status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}

	@Column(name = "created", unique = false, nullable = true, insertable = true, updatable = true, length = 8)
	public Date getCreated() {
		return this.created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	@Column(name = "created_by", unique = false, nullable = true, insertable = true, updatable = true)
	public Integer getCreatedBy() {
		return this.createdBy;
	}

	public void setCreatedBy(Integer createdBy) {
		this.createdBy = createdBy;
	}

	@Column(name="private")
	public Boolean isPrivate() {
		/* ================================================== */
		if (this.isPrivate == null)
			return false;
		return this.isPrivate;
		/* ================================================== */
	}

	public void setPrivate(Boolean isPrivate) {
		/* ================================================== */
		this.isPrivate = isPrivate;
		/* ================================================== */
	}
	
	@Column(name="modified")
	public Date getModified() {
		return modified;
	}

	public void setModified(Date modified) {
		this.modified = modified;
	}

	@Column(name="modified_by")
	public Integer getModifiedBy() {
		return modifiedBy;
	}

	public void setModifiedBy(Integer modifiedBy) {
		this.modifiedBy = modifiedBy;
	}

//	@Column(name = "notifyDate")
//	public Date getNotifyDate() {
//		return this.notifyDate;
//	}
//
//	public void setNotifyDate(Date notifydate) {
//		try {
//			this.notifyDate = new Date(notifydate.getTime());
//		} catch (Exception e) {
//		}
//	}

	/**
	 * Equals clone
	 *
	 * @return
	 */
	public Appointment copy() {
		/* ================================================== */
		Appointment a = new Appointment();

		a.setId(this.getId());
		a.setCalendarId(getCalendarId());
		a.setCreated(getCreated());
		a.setCreatedBy(getCreatedBy());
		a.setModified(getModified());
		a.setModifiedBy(getModifiedBy());

		a.setDescription(getDescription());
		a.setEndDate(getEndDate());
		a.setFrequency(getFrequency());
		a.setRRule(getRRule());

		a.setPatientId(getPatientId());
		a.setPrivate(isPrivate());
		a.setStartDate(getStartDate());

		a.setStatus(getStatus());
		a.setSummary(getSummary());
		a.setTypeId(getTypeId());

		a.setUntil(getUntil());

		a.setIsBackground(getIsBackground());

		return a;



		/* ================================================== */
	}

	/**
	 * @return the isBackground
	 */
	@Column(name="is_background")
	public Boolean getIsBackground() {
		if (isBackground == null)
			isBackground = false;
		return isBackground;
	}

	/**
	 * @param isBackground the isBackground to set
	 */
	public void setIsBackground(Boolean isBackground) {
		this.isBackground = isBackground;
	}

	
	@Override
	public int compareTo (GECAMedEntityBean o)
	{
		if (o instanceof Appointment)
		{
			Appointment a = (Appointment) o;
			if (a.getStartDate().before(startDate))
				return 1;
			else if (a.getStartDate().after(startDate))
				return -1;
			else
				return 0;
		}
		else
			return super.compareTo(o);
	}

	
	public String toString() {
		/* ================================================== */
		return "App for Cal: " + getCalendarId() + " ["+df.format(getStartDate())+" - " + df.format(getEndDate())+" ] " + getSummary();
		/* ================================================== */
	}
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean#clone()
	 */
	@Override
	public Appointment clone() {
		/* ================================================== */
		Appointment app = new Appointment();
		
		app.setTypeId(			getTypeId());
		app.setCalendarId(		getCalendarId());
		app.setStartDate(		getStartDate());
		app.setEndDate(			getEndDate());
		app.setPatientId(		getPatientId());
		app.setSummary(			getSummary());
		app.setDescription(		getDescription());
		app.setFrequency(		getFrequency());
		app.setRRule(			getRRule());
		app.setUntil(			getUntil());
		app.setStatus(			getStatus());
		app.setPrivate(			isPrivate());
		app.setCreated(			getCreated());
		app.setCreatedBy(		getCreatedBy());
		app.setModified(		getModified());
		app.setModifiedBy(		getModifiedBy());
		app.setIsBackground(	getIsBackground());
		
		return app;
		/* ================================================== */
	}
	public boolean isEqual(Appointment app)
	{
		if(this.typeId != null)
		{
			if(!this.typeId.equals(app.typeId))
			{
				return false;
			}
		}
		
		if(this.calendarId != null)
		{
			if(!this.calendarId.equals(app.calendarId))
			{
				return false;
			}
		}
		
		if(this.startDate != null)
		{
			if(!this.startDate.equals(app.startDate))
			{
				return false;
			}
		}
		
		if(this.endDate != null)
		{
			if(!this.endDate.equals(app.endDate))
			{
				return false;
			}
		}
		
		if(this.patientId != null)
		{
			if(!this.patientId.equals(app.patientId))
			{
				return false;
			}
		}
		
		if(this.summary != null)
		{
			if(!this.summary.equals(app.summary))
			{
				return false;
			}
		}
		
		if(this.description != null)
		{
			if(!this.description.equals(app.description))
			{
				return false;
			}
		}
		
		if(this.frequency != null)
		{
			if(!this.frequency.equals(app.frequency))
			{
				return false;
			}
		}
		
		if(this.rRule != null)
		{
			if(!this.rRule.equals(app.rRule))
			{
				return false;
			}
		}
		
		if(this.until != null)
		{
			if(!this.until.equals(app.until))
			{
				return false;
			}
		}
		
		if(this.status != null)
		{
			if(!this.status.equals(app.status))
			{
				return false;
			}
		}
		
		if(this.isPrivate != null)
		{
			if(!this.isPrivate.equals(app.isPrivate))
			{
				return false;
			}
		}
		
		if(this.created != null)
		{
			if(!this.created.equals(app.created))
			{
				return false;
			}
		}
		
		if(this.createdBy != null)
		{
			if(!this.createdBy.equals(app.createdBy))
			{
				return false;
			}
		}
		
		if(this.isBackground != null)
		{
			if(!this.isBackground.equals(app.isBackground))
			{
				return false;
			}
		}
		return true; 
	}
	
	/**
	 * Generates a real instance of an recuring Appointment
	 * for one of the recuring dates.
	 * 
	 * @return
	 */
	public Appointment cloneAsRecuringInstance(Date realDate) {
		Appointment appInstance = this.clone();
		appInstance.move(realDate);
		appInstance.setId(-1);
		appInstance.setOrigAppointment(this);
		return appInstance;
	}
	
	/**
	 * generates a uniq UID for this Appointment 
	 * (or this instance on reccuring Appointments)
	 * @return
	 */
	@Transient
	public String getAppointmentUID() {
		if (getOrigAppointment() != null) {
			return getOrigAppointment().getId() + "_" + TIMESTAMP.format(getStartDate());
		} else {
			return ""+getId();
		}
	}
	
	/**
	 * 
	 * @param appointment The original Appointment this recuring instance was created from
	 */
	@Transient
	public void setOrigAppointment(Appointment appointment) {
		this.origAppointment = appointment;
	}

	/**
	 * @return The original Appointment this recuring instance was created from
	 */
	@Transient
	public Appointment getOrigAppointment() {
		return this.origAppointment;
	}
	
	/**
	 * Moves the Appointment to a new startdate.
	 *
	 * @param newStartDate
	 */
	public void move(Date newStartDate) {
		/* ================================================== */
		if (newStartDate != null) {
			setEndDate(new Date(getEndDate().getTime() + DateUtil.getDiffDay(getStartDate(), newStartDate)));
			setStartDate(newStartDate);
		}
		/* ================================================== */
	}

	/**
	 * parses the Appointments Description and delivers the lines that have a key=value structure as an hashmap. 
	 * @return
	 */
	@Transient
	public HashMap<String, String> getDescriptionValues() {
		HashMap<String, String> descValues = new HashMap<String, String>();
		try {
			String desc = getDescription();
			String[] lines = desc.split("[\\n\\r]");
			for (String line : lines) {
				String[] keyVal = line.split("=");
				if (keyVal.length == 2) {
					descValues.put(keyVal[0], keyVal[1]);							
				}
			}
		} catch (Exception e2) {
		}
		return descValues;
	}
	
//	public static void main (String[] args)
//	{
//		Appointment singleApp = null;
//		Appointment app = null;
//		
//		
//		singleApp.setCreated(app.getCreated());
//		singleApp.setCreatedBy(app.getCreatedBy());
//		singleApp.setFrequency(0);
//		singleApp.setIsBackground(app.getIsBackground());
//		singleApp.setPatientId(app.getPatientId());
//		singleApp.setPrivate(app.isPrivate());
//		singleApp.setRRule(app.getRRule());
//		singleApp.setStatus(app.getStatus());
//		singleApp.setTypeId(app.getTypeId());
//		singleApp.setUntil(app.getUntil());
//	}
}