/*******************************************************************************
 * 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.waitingroom.ejb.session.beans;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import lu.tudor.santec.gecamed.agenda.ejb.entity.beans.AgendaCalendar;
import lu.tudor.santec.gecamed.agenda.ejb.entity.beans.Appointment;
import lu.tudor.santec.gecamed.agenda.ejb.session.interfaces.AppointmentManager;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.log.LogType;
import lu.tudor.santec.gecamed.core.ejb.session.beans.GECAMedSessionBean;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.LogManager;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface;
import lu.tudor.santec.gecamed.usermanagement.ejb.session.interfaces.LoginInterface;
import lu.tudor.santec.gecamed.waitingroom.ejb.entity.beans.Queue;
import lu.tudor.santec.gecamed.waitingroom.ejb.entity.beans.Reason;
import lu.tudor.santec.gecamed.waitingroom.ejb.entity.beans.Room;
import lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager;
import lu.tudor.santec.gecamed.waitingroom.ejb.session.util.QueueUpdateEvent;
import lu.tudor.santec.gecamed.waitingroom.gui.WaitingroomModule;
import lu.tudor.santec.settings.SettingReader;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.poi.ss.util.DateFormatConverter;

import bizcal.util.DateUtil;

/**
 * @author martin.heinemann@tudor.lu
 * 23.01.2008
 * 16:18:17
 *
 *
 * @version
 * <br>$Log: WaitingroomManagerBean.java,v $
 * <br>Revision 1.26  2013-07-15 06:18:37  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.25  2012-10-03 12:24:34  troth
 * <br>Implement ticket #1000.
 * <br>
 * <br>Revision 1.24  2011-11-29 09:28:21  troth
 * <br>code clearup and add some logger messages
 * <br>
 * <br>Revision 1.23  2011-11-21 15:31:54  troth
 * <br>bug fix: the entry in the waiting now get updated if the patient of a appointment change.
 * <br>
 * <br>Revision 1.22  2011-11-17 16:15:54  troth
 * <br>fix Ticket #908 Falsche Anzeige im Wartesaal nach Termin�nderung in Agenda
 * <br>
 * <br>Revision 1.21  2011-11-16 15:52:32  troth
 * <br>code clearup
 * <br>
 * <br>Revision 1.20  2011-11-09 14:45:02  troth
 * <br>Improved the function which compute the recurrence appointment dates.
 * <br>
 * <br>Revision 1.19  2011-10-28 15:11:02  troth
 * <br>fix Ticket #900: Recurrence Appointments are not show in the Waitingroom.
 * <br>
 * <br>Revision 1.18  2011-02-04 15:46:08  troth
 * <br>Fix Bug - Bad synchronization Agenda/Waitingroom if a physician have more than two waitingrooms.
 * <br>
 * <br>Revision 1.17  2010-12-30 14:32:39  troth
 * <br>fixed problems with synchronization of Waitingroom and Agenda
 * <br>
 * <br>Revision 1.16  2010-07-08 08:25:37  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.15  2010-06-09 06:41:39  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.14  2010-05-12 09:00:15  hermen
 * <br>changed handling and fetching of room-updates.
 * <br>collectAppointments is now called each 5 min by server,
 * <br>rooms are updated by single entry, not by full reload as before.
 * <br>
 * <br>Revision 1.13  2010-04-30 13:08:42  hermen
 * <br>added site to waitingroom
 * <br>
 * <br>Revision 1.12  2010-03-18 17:09:36  hermen
 * <br>fixed updating of waitingroom
 * <br>fixed taking only appointments with attached patient to the waitingeroom
 * <br>
 * <br>Revision 1.11  2009-10-13 09:11:02  hermen
 * <br>Add/Delete/Edit Waitingroom are now in the admin-settings.
 * <br>Description of Waitingroom can be changed by user
 * <br>
 * <br>Revision 1.10  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.9  2008-07-04 13:02:04  heinemann
 * <br>complete - # 159: Add notes to entries of the waitingroom
 * <br>http://santec.tudor.lu/trac/gecamed/ticket/159
 * <br>
 * <br>Revision 1.8  2008-05-08 09:10:02  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.7  2008-05-07 14:39:53  heinemann
 * <br>new waitingroom
 * <br>
 * <br>Revision 1.6  2008-04-29 14:17:38  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.5  2008-02-27 14:23:06  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.4  2008-02-27 09:14:39  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.3  2008-02-27 08:26:49  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.2  2008-02-25 15:49:58  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.1  2008-02-11 16:47:53  heinemann
 * <br>Initial checkin
 * <br>
 *   
 */
@Stateless
public class WaitingroomManagerBean extends GECAMedSessionBean implements WaitingroomManager {
	
	@PersistenceContext(unitName="gecam")
	EntityManager em;
	
	@EJB
	PatientAdminInterface patientManager;
	
	@EJB
	AppointmentManager appointmentManager;
	
	@EJB
	LoginInterface loginManager;
	
	@EJB 
	LogManager logManager;
		
	@Resource(mappedName = TOPIC_NAME)
	private Topic waitingroomUpdateTopic;
	
	@Resource(mappedName = "ConnectionFactory")
	private TopicConnectionFactory factory;
	
	private TopicConnection conn = null;

	private TopicSession session = null;

	private static boolean importRunning;
	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(WaitingroomManagerBean.class.getName());
	
	
	public boolean deleteRoom(Room room) {
		/* ====================================================== */
		if (room == null || !room.isPersistent())
			return false;
		/* ------------------------------------------------------- */
		// find the entity in the database
		/* ------------------------------------------------------- */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Room o WHERE o.id = :id");
		q.setParameter("id", room.getId());
		try {
			/* ------------------------------------------------------- */
			Room r = (Room) q.getSingleResult();
			em.remove(r);
			return true;
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.error("Error deleting Waitingroom: " + room, e);
			return false;
		} finally {
			try {
				sendRoomUpdateMessage();				
			} catch (Exception e2) {
				logger.error("Error sending room update message: ", e2);
			}
		}
		/* ====================================================== */
	}

	@SuppressWarnings("unchecked")
	@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
	public List<Room> getAllRooms() {
		/* ====================================================== */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Room o WHERE o.isWaitingroom = true ORDER BY o.orderId, o.id ASC");
		try {
			/* ------------------------------------------------------- */
			return q.getResultList();
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.error("Error fetching room list!", e);
		}
		return null;
		/* ====================================================== */
	}
	
	@SuppressWarnings("unchecked")
	@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
	public List<Reason> getReasons() {
		/* ====================================================== */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Reason o ORDER BY o.id ASC");
		try {
			/* ------------------------------------------------------- */
			return q.getResultList();
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.error("Error fetching reason list!", e);
		}
		return null;
		/* ====================================================== */
	}
	
	
	/**
	 * @param queue
	 */
	public void removeQueue(Queue queue, String clientID) {
		/* ================================================== */
		if (queue == null)
			return;
		/* ------------------------------------------------------- */
		try {
			/* ------------------------------------------------------- */
			Queue temp = getQueue(queue.getId());
			
			em.remove(temp);
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.warn("Error deleting Queue Entry " + queue, e);
		} finally {
			try {				
				QueueUpdateEvent update = new QueueUpdateEvent(clientID, queue.getRoomId(), QueueUpdateEvent.ENTRY_DELETE, queue);
				sendQueueUpdateMessage(update);
			} catch (Exception e2) {
				logger.warn("Error sending Queue Update Message ", e2);
			}
		}
		/* ================================================== */
	}
	
	public boolean leaveOffice(Queue queue) {
		/* ====================================================== */
		if (queue == null)
			return false;
		/* ------------------------------------------------------- */
		try {
			/* ------------------------------------------------------- */
			Queue temp = getQueue(queue.getId());
			temp.setEndDate(new Date());
			
			em.merge(temp);
			
			return true;
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.error("Error leaving office for queueevent: " + queue, e);
		}
		return false;
		/* ====================================================== */
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#moveToRoom(lu.tudor.santec.gecamed.waitingroom.ejb.entity.beans.Queue, java.lang.Integer)
	 */
	public Queue moveToRoom(Queue queue, Integer roomId) {
		/* ====================================================== */
		if (queue == null || roomId == null)
			return null;
		/* ------------------------------------------------------- */
		Queue q = getQueue(queue.getId());
		if (q == null)
			return null;
		/* ------------------------------------------------------- */
		// close all Queues that are in this room, if it is a consultation room
		/* ------------------------------------------------------- */
		closeAllQueues(roomId);
		
		q.setRoomId(roomId);
		q.setEndDate(null);
		/* ------------------------------------------------------- */
		// get the last order id/value
		/* ------------------------------------------------------- */
		Integer orderId = getLastOrder(roomId);
		if (orderId == null)
			orderId = 0;
		q.setOrderId(orderId + 1);
		/* ------------------------------------------------------- */
		// save to the new room
		/* ------------------------------------------------------- */
		try{
			return em.merge(q);
		} catch (Exception e) {
			log(Level.WARN, "Unable to move Queue object to roomId " + roomId);
			return null;
		}
		/* ====================================================== */
	}
	
	
//	@SuppressWarnings("unchecked")
//	public void moveToRoomAndPosition(List<Queue> queues, Integer roomId, Queue insertPosition) {
//		/* ================================================== */
//		if (queues == null || roomId == null || insertPosition == null)
//			return;
//		/* ------------------------------------------------------- */
//		// get the queues to insert
//		/* ------------------------------------------------------- */
//		ArrayList<Queue> insertQueues = new ArrayList<Queue>();
//		for (Queue q : queues) {
//			/* ------------------------------------------------------- */
//			Queue temp = getQueue(q.getId());
//			if (temp == null)
//				continue;
//			insertQueues.add(temp);
//			// set the roomId
//			temp.setRoomId(roomId);
//			/* ------------------------------------------------------- */
//		}
//		if (insertQueues.size() < 1)
//			return;
//		/* ------------------------------------------------------- */
//		// get all queues and make a new order
//		Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
//									"WHERE o.roomId = :roomId ORDER BY o.orderId ASC");
//		q.setParameter("roomId", roomId);
//		
//		try {
//			/* ------------------------------------------------------- */
//			ArrayList<Queue> result = (ArrayList<Queue>) q.getResultList();
//			/* ------------------------------------------------------- */
//			// get the queue to insert before
//			/* ------------------------------------------------------- */
//			Queue beforeQueue = result.get(result.indexOf(insertPosition));
//			/* ------------------------------------------------------- */
//			// if the first insertQueue is before the insertposition, 
//			// we take the next one to insert after 
//			/* ------------------------------------------------------- */
//			Queue firstInsert = insertQueues.get(0);
//			/* ------------------------------------------------------- */
//			// if insertque is equal to the before queue
//			// do nothing
//			/* ------------------------------------------------------- */
//			if (firstInsert.equals(beforeQueue))
//				return;
//			/* ------------------------------------------------------- */
//			int quPos = result.indexOf(beforeQueue);
//			
//			if (firstInsert.getOrderId() < beforeQueue.getOrderId()) {
//				/* ------------------------------------------------------- */
//				// if the insert location is not the last queue, get the next one
//				if (quPos <= result.size()-2)
//					beforeQueue = result.get(quPos + 1);
//				else {
//					// append at the end
//					beforeQueue = null;
//				}
//				/* ------------------------------------------------------- */
//			}
//			/* ------------------------------------------------------- */
//			// check if a movable Queue is included in the result
//			/* ------------------------------------------------------- */
//			if (result.contains(beforeQueue)) {
//				/* ------------------------------------------------------- */
//				// remove all queues to move from the list
//				/* ------------------------------------------------------- */
//				result.removeAll(insertQueues);
//				/* ------------------------------------------------------- */
//				// insert the queues at insertPosition
//				/* ------------------------------------------------------- */
//				result.addAll(result.indexOf(beforeQueue), insertQueues);
//				/* ------------------------------------------------------- */
//			} else {
//				/* ------------------------------------------------------- */
//				// append at the end
//				result.removeAll(insertQueues);
//				result.addAll(insertQueues);
//				/* ------------------------------------------------------- */
//			}
//				
//			/* ------------------------------------------------------- */
//			// renumber the queue list
//			/* ------------------------------------------------------- */
//			int position = 0;
//			for (Queue qu : result) {
//				qu.setOrderId(position);
//				em.merge(qu);
//				position++;
//			}
//			/* ------------------------------------------------------- */
//		} catch (Exception e) {
//			/* ------------------------------------------------------- */
//			e.printStackTrace();
//			/* ------------------------------------------------------- */
//		}
//	}
	
	
	/**
	 * returns the highest value for Queue objects that are int he same room
	 * 
	 * @param roomId
	 * @return
	 */
	private Integer getLastOrder(Integer roomId) {
		/* ================================================== */
		Query q = em.createQuery("SELECT MAX(o.orderId) FROM Queue o " +
								 	"WHERE o.roomId = :roomId AND o.endDate = null");
		q.setParameter("roomId", roomId);
		try {
			/* ------------------------------------------------------- */
			Integer result =  (Integer) q.getSingleResult();
			if (result == null)
				return 0;
			return result;
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.error("Error getting order for room: " + roomId, e);
			return 0;
		}
		/* ================================================== */
	}
	
	/**
	 * "Closes" all Queues for the room. Puts an EndDate to the queue objects
	 * But only if it is a consultation room
	 * @param roomId
	 */
	@SuppressWarnings("unchecked")
	private void closeAllQueues(Integer roomId) {
		/* ================================================== */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
									"WHERE o.roomId = :roomId and " +
									":roomId IN (SELECT r.id FROM Room r WHERE r.id = :roomId AND r.isWaitingroom = false)");
		q.setParameter("roomId", roomId);
		try {
			List<Queue> result = q.getResultList();
			for (Queue qu : result) {
				qu.setEndDate(new Date());
				em.merge(qu);
			}
		} catch (Exception e) {
			logger.error("Error closing all queues for room: " + roomId, e);
		}
		/* ================================================== */
	}
	
	public Queue saveQueue(Queue queue, String clientId, Integer oldRoom) {
		/* ====================================================== */
		if (queue == null)
			return null;
		/* ------------------------------------------------------- */
		try {
			/* --------------------------------------------- */
			fillPatientNameIfEmpty(queue);
			Queue q =  em.merge(queue);
			em.flush();
			/* ------------------------------------------------------- */
			return q;
			/* --------------------------------------------- */
		} catch (Exception e) {
			/* --------------------------------------------- */
			logger.error("Error saving queue: " + queue, e);
			/* --------------------------------------------- */
		} finally {
			try {				
				QueueUpdateEvent update = new QueueUpdateEvent(clientId, queue.getRoomId(), QueueUpdateEvent.ENTRY_UPDATE, queue);
				sendQueueUpdateMessage(update);
				
				
				if (oldRoom != null && oldRoom != queue.getRoomId()) {
					// remove from old room
					QueueUpdateEvent remove = new QueueUpdateEvent(clientId, oldRoom, QueueUpdateEvent.ENTRY_DELETE, queue);
					sendQueueUpdateMessage(remove);					
				}
			} catch (Exception e2) {
				logger.warn("Error sending Queue Update Message ", e2);
			}
		}
		return null;
		/* ====================================================== */
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#saveRoom(lu.tudor.santec.gecamed.waitingroom.ejb.entity.beans.Room)
	 */
	public Room saveRoom(Room room, String clientId) {
		/* ====================================================== */
		if (room == null)
			return null;
		/* ------------------------------------------------------- */
		if (room.isPersistent()) {
			/* ------------------------------------------------------- */
			Query q = em.createQuery("SELECT OBJECT(o) FROM Room o " +
										"WHERE o.id = :id");
			q.setParameter("id", room.getId());
			try {
				/* ------------------------------------------------------- */
				Room oldRoom = (Room) q.getSingleResult();
				
					oldRoom.setColor(room.getColor());
					oldRoom.setIsWaitingroom(room.getIsWaitingroom());
					oldRoom.setName(room.getName());
					oldRoom.setDescription(room.getDescription());
					oldRoom.setPhysicianId(room.getPhysicianId());
					oldRoom.setCalendarId(room.getCalendarId());
					oldRoom.setOrderId(room.getOrderId());
					oldRoom.setSiteId(room.getSiteId());
					
					Room r =  em.merge(oldRoom);
					
					try {
						sendRoomUpdateMessage();				
					} catch (Exception e2) {
						logger.error("Error sending room update message: ", e2);
					}
					
					return r;
				/* ------------------------------------------------------- */
			} catch (Exception e) {
				logger.error("Error saving room: " + room, e);
				return null;
			}
			/* ------------------------------------------------------- */
		}
		/* ------------------------------------------------------- */
		// just save the new room
		/* ------------------------------------------------------- */
			
		room =  em.merge(room);
		
		try {
			sendRoomUpdateMessage();				
		} catch (Exception e2) {
			logger.error("Error sending room update message: ", e2);
		}
		
		return room;
		/* ====================================================== */
	}

	@SuppressWarnings("unchecked")
	public List<Queue> getAllQueues() {
		/* ====================================================== */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
									"WHERE o.endDate = null");
		try {
			/* ------------------------------------------------------- */
			List<Queue> queues = q.getResultList();
			/* ------------------------------------------------------- */
			// fill the patient name properties of each queue
			/* ------------------------------------------------------- */
			fillPatientNamesIfEmpty(queues);
			return queues;
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			/* ------------------------------------------------------- */
			logger.error("Error getting all Queues", e);
			return null;
			/* ------------------------------------------------------- */
		}
		/* ====================================================== */
	}
	
	
	/**
	 * Fill the queues with additional information
	 * like patient name, ucm code and appointment infos
	 * 
	 * @param queues
	 */
	private void fillPatientNamesIfEmpty(List<Queue> queues) {
		/* ================================================== */
		if (queues != null) {
			/* ------------------------------------------------------- */
			// get names
			/* ------------------------------------------------------- */
			for (Queue qu : queues) {
			    fillPatientNameIfEmpty(qu);
			}
			/* ------------------------------------------------------- */
		}
		/* ================================================== */
	}
	
	
	/**
	 * TODO Will be obsolete once all entries have patient names set (now on creation of entries)
	 * @param qu
	 */
	private Queue fillPatientNameIfEmpty(Queue qu) {
	    
	    try {
	    	// if name is already set - may be removed later...
	    	if (qu.getPatientName() != null && qu.getPatientName().length() > 0) {
	    		return qu;
	    	}
	    	
			Patient p = patientManager.getPatient(qu.getPatientId());
			qu.setPatientName(p.toShortString());
			
			if (qu.getDescription() == null || qu.getDescription().length() == 0) {
				qu.setDescription("MATR:" + p.getSocialSecurityNumber() + " ID:"+p.getId());
			}
			
//			qu.setProperty(Queue.PROPERTY_PATIENTNAME, p.toShortString());
//			qu.setProperty(Queue.PROPERTY_UCM_CODE,    p.getSocialSecurityNumber());
//			setAppointmentInfos(qu);
			
	    } catch (Exception e) {
	    	logger.warn("Error getting patient Information", e);
	    }
		/* ------------------------------------------------------- */
	    return qu;
	}
	
//	private void setAppointmentInfos(Queue queue) {
//		/* ================================================== */
//		// get the appointment object
//		/* ------------------------------------------------------- */
//		Appointment app = appointmentManager.getAppointment(queue.getAppointmentId());
//		
////		em.flush();
//		/* ------------------------------------------------------- */
//		// if null, do nothing
//		/* ------------------------------------------------------- */
//		if (app == null)
//			return;
//		/* ------------------------------------------------------- */
//		// set startdate
//		/* ------------------------------------------------------- */
//		queue.setProperty(Queue.PROPERTY_APPOINTMENT_START, app.getStartDate());
//		/* ------------------------------------------------------- */
//		// set description
//		/* ------------------------------------------------------- */
////		if (app.getDescription() != null) {
////			if (queue.getDescription() != null)
////				queue.setDescription(queue.getDescription()+"\n");
////			queue.setDescription(queue.getDescription()+app.getDescription());
////		}
//		/* ------------------------------------------------------- */
//		// set the color of the calendar
//		/* ------------------------------------------------------- */
//		Integer color = appointmentManager.getCalendarColor(app.getCalendarId());
//		if (color != null) {
//			/* ------------------------------------------------------- */
//			queue.setProperty(Queue.PROPERTY_CALENDAR_COLOR, color);
//			/* ------------------------------------------------------- */
//		}
//		/* ================================================== */
//	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#getQueues(java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	public List<Queue> getQueues(Integer roomId) {
		/* ====================================================== */
		if (roomId == null)
			return null;
		/* ------------------------------------------------------- */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
									"WHERE o.roomId = :id AND o.endDate = null ORDER BY o.orderId ASC");
		q.setParameter("id", roomId);
		try {
			/* ------------------------------------------------------- */
			List<Queue> queues = q.getResultList();
			// fill data
			fillPatientNamesIfEmpty(queues);
			return queues;
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			/* ------------------------------------------------------- */
			log(Level.INFO, "Error during fetching waitingroom queues for room " + roomId, e);
			return null;
			/* ------------------------------------------------------- */
		}
		/* ====================================================== */
	}
	
	/**
	 * Returns the queue for id
	 * 
	 * @param id
	 * @return
	 */
	public Queue getQueue(Integer id) {
		/* ================================================== */
		if (id == null)
			return null;
		/* ------------------------------------------------------- */
		Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
									"WHERE o.id = :id");
		q.setParameter("id", id);
		try {
			/* ------------------------------------------------------- */
			return (Queue) q.getSingleResult();
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			logger.error("Error getting Queue: " + id, e);
			return null;
		}
		/* ================================================== */
	}

//	/**
//	 * 
//	 * 
//	 * 
//	 */
//	public Queue createQueue(Integer patientId, Integer roomId, String comment, String clientID, Date entryDate, Boolean isVisit, Integer kilometers, Integer reason) {
//		/* ====================================================== */
//		// check if the patient is already in a room
//		// is marked by patient id and queue entry without enddate
//		/* ------------------------------------------------------- */
//		Query q = em.createQuery("SELECT COUNT(o.id) FROM Queue o " +
//									"WHERE o.patientId = :patientId  AND o.startDate > :startDate AND o.startDate < :endDate " +
//									"AND o.endDate = null AND o.roomId = :roomId");
//		q.setParameter("patientId", patientId);
//		q.setParameter("roomId", roomId);
//		q.setParameter("startDate", DateUtil.move2Morning(entryDate));
//		q.setParameter("endDate", DateUtil.move2Midnight(entryDate));
//		q.setMaxResults(1);
//		/* ------------------------------------------------------- */
//		try {
//			/* --------------------------------------------- */
//			Long obj = (Long) q.getSingleResult();
//			if (obj == null ||  obj < 1) {
//				/* ------------------------------------------------------- */
//				// we can go ahead
//				/* ------------------------------------------------------- */
//				// create a new Queue
//				/* ------------------------------------------------------- */
//				Queue que = new Queue();
//				/* ------------------------------------------------------- */
//				// get the appointment for that day
//				/* ------------------------------------------------------- */
//				Appointment currApp = appointmentManager.getCurrentAppointment(patientId);
//				if (currApp != null) {
//					que.setAppointmentId(currApp.getId());
//				}
//				/* ------------------------------------------------------- */
//				que.setCreatedBy(loginManager.getCurrentUserID());
//				que.setDescription(comment);
//				que.setPatientId(patientId);
//				que.setIsVisit(isVisit);
//				que.setKilometers(kilometers);
//				que.setReason(reason);
//				
//				fillPatientNameIfEmpty(que);
//				
//				/* ------------------------------------------------------- */
//				que.setRoomId(roomId);
//				que.setStartDate(entryDate);
//				/* ------------------------------------------------------- */
//				que.setOrderId(getLastOrder(roomId)+1);
//				/* ------------------------------------------------------- */
//				// save
//				que = em.merge(que);
//				
//				
//				try {				
//					QueueUpdateEvent update = new QueueUpdateEvent(clientID, roomId, QueueUpdateEvent.ENTRY_ADD, que);
//					sendQueueUpdateMessage(update);
//				} catch (Exception e2) {
//					logger.warn("Error sending Queue Update Message ", e2);
//				}
//				
//				return que;
//				/* ------------------------------------------------------- */
//			}
//		} catch (Exception e) {
//			logger.error("Error creating Queue entry", e);
//		} 
//		return null;
//		
//		/* ====================================================== */
//	}
	
	public Queue createQueue(Queue que, String clientId) {
		/* ====================================================== */
		// check if the patient is already in a room
		// is marked by patient id and queue entry without enddate
		/* ------------------------------------------------------- */
		Query q = em.createQuery("SELECT COUNT(o.id) FROM Queue o " +
									"WHERE o.patientId = :patientId  AND o.startDate > :startDate AND o.startDate < :endDate " +
									"AND o.endDate = null AND o.roomId = :roomId");
		q.setParameter("patientId", que.getPatientId());
		q.setParameter("roomId", que.getRoomId());
		q.setParameter("startDate", DateUtil.move2Morning(que.getStartDate()));
		q.setParameter("endDate", DateUtil.move2Midnight(que.getStartDate()));
		q.setMaxResults(1);
		/* ------------------------------------------------------- */
		try {
			/* --------------------------------------------- */
			Long obj = (Long) q.getSingleResult();
			if (obj == null ||  obj < 1) {
				/* ------------------------------------------------------- */
				// we can go ahead
				/* ------------------------------------------------------- */
				/* ------------------------------------------------------- */
				// get the appointment for that day
				/* ------------------------------------------------------- */
				Appointment currApp = appointmentManager.getCurrentAppointment(que.getPatientId(), que.getStartDate());
				if (currApp != null) {
					que.setAppointmentId(currApp.getId());
				}
				/* ------------------------------------------------------- */
				que.setCreatedBy(loginManager.getCurrentUserID());
				
				fillPatientNameIfEmpty(que);
				/* ------------------------------------------------------- */
				que.setOrderId(getLastOrder(que.getRoomId())+1);
				/* ------------------------------------------------------- */
				// save
				que = em.merge(que);
				
				
				try {				
					QueueUpdateEvent update = new QueueUpdateEvent(clientId, que.getRoomId(), QueueUpdateEvent.ENTRY_ADD, que);
					sendQueueUpdateMessage(update);
				} catch (Exception e2) {
					logger.warn("Error sending Queue Update Message ", e2);
				}
				
				return que;
				/* ------------------------------------------------------- */
			}
		} catch (Exception e) {
			logger.error("Error creating Queue entry", e);
		} 
		return null;
		
		/* ====================================================== */
	}
	
	/**
	 * Sends an update message to all clients to apply the given Update to their Rooms.
	 * If update is null, a full reload will be triggered on the clients! 
	 * 
	 * @param calendarId
	 */
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#sendQueueUpdateMessage(lu.tudor.santec.gecamed.waitingroom.ejb.session.util.QueueUpdateEvent)
	 */
	public void sendQueueUpdateMessage(QueueUpdateEvent update) throws JMSException {
		/* ================================================== */
		conn = factory.createTopicConnection();
		conn.start();
		session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
		/* ------------------------------------------------------- */
		TopicPublisher send = session.createPublisher(waitingroomUpdateTopic);

		// ==============================================================
		// create a base tupel containing the id of the calling client
		// and the roomId
		// ==============================================================
		/* ------------------------------------------------------- */
		ObjectMessage om = session.createObjectMessage(update);
		send.publish(om);
		send.close();
		/* ------------------------------------------------------- */
		session.close();
		conn.close();
		/* ================================================== */
	}

	
	/**
	 * Sends an update message to all clients to update their rooms
	 * 
	 * @throws JMSException
	 */
	public void sendRoomUpdateMessage() throws JMSException {
		/* ================================================== */
	    	conn = factory.createTopicConnection();
		conn.start();
		session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
		/* ------------------------------------------------------- */
		TopicPublisher send = session.createPublisher(waitingroomUpdateTopic);

		// ==============================================================
		// create a base tupel containing the id of the calling client
		// and the roomId
		// ==============================================================
		/* ------------------------------------------------------- */
		logManager.createLogMessage(LogType.SYSTEM, WaitingroomModule.MODULE_NAME,"SENT JMS", TOPIC_CMD_ROOM_UPDATE, (long) 0);
		
		ObjectMessage om = session.createObjectMessage(TOPIC_CMD_ROOM_UPDATE);
		send.publish(om);
		send.close();
		/* ------------------------------------------------------- */
		session.close();
		conn.close();
		/* ================================================== */
	}
	
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#getQueues4Today(java.lang.Integer)
	 */
	@SuppressWarnings("unchecked")
	public List<Queue> getQueues4Day(Integer roomId, Date day) {
		if (roomId == null)
			return null;
		
		long time = System.currentTimeMillis();
		
		GregorianCalendar cal = new GregorianCalendar();
		cal.setTime(day);
		Date startDate = day;
		Date endDate   = day;
		
		// for today we show from 02:00-02:00 of the next day
		if (DateUtil.isSameDay(day, new Date())) {
			// if we are before 02:00 in the night, we behave like the day before (for Maison Medical night work)
			if (cal.get(Calendar.HOUR_OF_DAY) < 2) {
				cal.add(Calendar.DAY_OF_YEAR, -1);
				day = cal.getTime();
			}
			startDate = moveBackToTwo(day);
			endDate   = moveForeToTwo(day);
		
		// for any other day we show 00:00-23:59
		} else {
			startDate = DateUtil.move2Morning(day);
			endDate   = DateUtil.move2Midnight(day);			
		}
		
		try {
			/* ------------------------------------------------------- */
			// get all Queues of today for that doctor
			/* ------------------------------------------------------- */
			Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
					"WHERE o.startDate > :start AND o.startDate < :end AND (o.endDate < :end OR o.endDate = null)" +
					"AND o.roomId = :id ORDER BY o.startDate");
			/* ------------------------------------------------------- */
			
			q.setParameter("start", startDate);
			q.setParameter("end",   endDate);
			q.setParameter("id",   roomId);
			/* --------------------------------------------- */
			List<Queue> result = q.getResultList();
			fillPatientNamesIfEmpty(result);
			return result;
			/* --------------------------------------------- */
		} catch (Exception e) {
			/* --------------------------------------------- */
			logger.error("Error getting Queues Day: " + startDate + " - " + endDate, e);
			/* --------------------------------------------- */
		} finally {
			logger.info("Fetching Queues from "+startDate+" to "+endDate+" for Room ID: " + roomId + " took " + (System.currentTimeMillis()-time));
		}
		return null;
		/* ====================================================== */
	}
	
	
	
	@SuppressWarnings("unchecked")
	public List<Queue> getQueues4Today4Physician(Integer physicianId) {
		long time = System.currentTimeMillis();
		/* ====================================================== */
		try {
			if (physicianId == null)
				return null;
			
	//		// if no room for this physician
	//		List l = em.createQuery("SELECT r.id FROM Room r WHERE r.physicianId = :pId")
	//			.setParameter("pId",   physicianId)
	//			.getResultList();
	//		if (l == null || l.size() == 0)
	//		    return null;
			
			/* ------------------------------------------------------- */
			// get all Queues of today for that doctor
			/* ------------------------------------------------------- */
			Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
					"WHERE o.startDate > :start AND (o.endDate < :end OR o.endDate = null)" +
					"AND o.roomId IN (SELECT r.id FROM Room r WHERE r.physicianId = :pId) ORDER BY o.startDate");
			/* ------------------------------------------------------- */
			
			// collect entries from now-10h until now+10h 
			Calendar c = new GregorianCalendar();
			c.add(Calendar.HOUR_OF_DAY, -10);
			Date startDate = c.getTime();
			c.add(Calendar.HOUR_OF_DAY, +20);
			Date endDate = c.getTime();
			
			q.setParameter("start", startDate);
			q.setParameter("end",   endDate);
			q.setParameter("pId",   physicianId);
			/* --------------------------------------------- */
			List<Queue> result = q.getResultList();
			fillPatientNamesIfEmpty(result);
			return result;
			/* --------------------------------------------- */
		} catch (Exception e) {
			/* --------------------------------------------- */
			logger.error("Error getting Queues for Today for physician: " + physicianId, e);
			/* --------------------------------------------- */
		} finally {
			logger.debug("Fetching Queue with Physician ID: " + physicianId + " took " + (System.currentTimeMillis()-time));
		}
		return null;
		/* ====================================================== */
	}
	
	/**
	 * This method will gather all appointments for the current day from the Agenda 
	 * and create waitingroom entries for each appointment in the corresponding waitingroom.
	 */
	public void collectAppointments4Waitingrooms(Locale locale, boolean sendMessages)
	{
		if (importRunning) {
			logger.info("Agenda import into Waitingwoom allready running - Skipping");
			return;
		}
		
		try {
			SettingReader settingReader = loginManager.getAdminSettingsReader();
			String modules = (String) settingReader.getValue("ModuleManager", "EnabledModules");
			
			if (modules != null && !(modules.contains("AgendaModule") && modules.contains("WaitingroomModule"))) {
				// Either Waitingroom or Calendar not active - NO SYNC -> DONE
				logger.debug("Either Waitingroom or Calendar not active - NO SYNC -> DONE");
				return;		 
			}
		} catch (Exception e) {}
		
		importRunning = true;
		
		long time = System.currentTimeMillis();
		
		try {
	
			/* ------------------------------------------------------- */
			// first, get all rooms
			/* ------------------------------------------------------- */
			List<Room> rooms = getAllRooms();
			if (rooms == null) {
				importRunning = false;
				return;
			}
			/* ------------------------------------------------------- */
			// create the dates
			/* ------------------------------------------------------- */
			Date date = new Date();
			Date startDate = DateUtil.move2Morning( date);
			Date endDate   = DateUtil.move2Midnight(date);
			/* ------------------------------------------------------- */
			// get all queues with appointment
			// each appointment that is not in this list must
			// be imported
			/* ------------------------------------------------------- */
			Query q = em.createQuery("SELECT Object(o) FROM Queue o " +
					"WHERE o.startDate > :start " +
					"AND (o.endDate < :end OR o.endDate = null) " +
					"AND o.appointmentId != null");
			
			q.setParameter("start", startDate);
			q.setParameter("end",   endDate);
			HashMap<String, Queue> waitingroomEntriesWithAppointments = new HashMap<String, Queue>();
			/* ------------------------------------------------------- */
			try {
				/* --------------------------------------------- */
				
				for (Object obj : q.getResultList())
				{
					Queue queue = (Queue) obj; 
					waitingroomEntriesWithAppointments.put(queue.getAppointmentId() + "_" + queue.getRoomId(), queue);
				}
				
				/* --------------------------------------------- */
			} catch (Exception e) {
				/* --------------------------------------------- */
				logger.error("Error collecting Appointments for Waitingrooms", e);
				/* --------------------------------------------- */
			}
			
			/* ------------------------------------------------------- */
			// now the import
			/* ------------------------------------------------------- */
			for (Room room : rooms)
			{	
				/* ------------------------------------------------------- */
				// no physician, no appointemnts
				/* ------------------------------------------------------- */
				if (room.getPhysicianId() == null)
					continue;
				/* ------------------------------------------------------- */
				// we will now gather all appointments that are in the calendars
				// of the physician
				/* ------------------------------------------------------- */
				Collection<AgendaCalendar> docCals = appointmentManager.getCalendarsByPhysician(room.getPhysicianId());
				/* ------------------------------------------------------- */
				if (docCals == null || docCals.size() < 1)
					continue;
				/* ------------------------------------------------------- */
				HashMap<Integer, Appointment> appointmentsFromAgenda = new HashMap<Integer, Appointment>();
				/* ------------------------------------------------------- */
				for (AgendaCalendar ag : docCals) {
					/* ------------------------------------------------------- */
					
					// fetch appointments for today
					List<Appointment> appointments = appointmentManager.getAppointments(ag.getId(), startDate, endDate, "waitingroomupdate");
					
					if (appointments.size() > 0)
					{
					    for (Appointment appointment : appointments)
					    {
					    	if (appointment.getPatientId() == null)
					    		continue;	
						
							Date newDate = null;
							
							Calendar appCal = Calendar.getInstance(locale);
							appCal.setTime(appointment.getStartDate());
							Calendar toCal = Calendar.getInstance(locale);
							toCal.setTime(new Date());
							
							if(
									(appCal.get(Calendar.DAY_OF_MONTH) == toCal.get(Calendar.DAY_OF_MONTH)) &&
									(appCal.get(Calendar.MONTH) == toCal.get(Calendar.MONTH)) &&
									(appCal.get(Calendar.YEAR) == toCal.get(Calendar.YEAR))
							){
								newDate = appointment.getStartDate();
							}else{
								if(isToDayDateADateOfRecurringAppointment(appointment, new Date(), locale))
								{
									Date appointmentDate = new Date(appointment.getStartDate().getTime());
									Calendar appointmentCal = Calendar.getInstance(locale);
									appointmentCal.setTime(appointmentDate);
									toCal.set(Calendar.MILLISECOND,appointmentCal.get(Calendar.MILLISECOND));
									toCal.set(Calendar.SECOND,appointmentCal.get(Calendar.SECOND));
									toCal.set(Calendar.MINUTE,appointmentCal.get(Calendar.MINUTE));
									toCal.set(Calendar.HOUR_OF_DAY,appointmentCal.get(Calendar.HOUR_OF_DAY));
									newDate = toCal.getTime();
								}
							}
												
							if(newDate == null)
								continue;
							
							appointment.setStartDate(newDate);
							appointmentsFromAgenda.put(appointment.getId(), appointment);
					    }
					}	
				}
				/* ------------------------------------------------------- */
				// now we have all appointments for this room, we can start import them
				// if there are already queue objects for an appointment, we will exclude them
				/* ------------------------------------------------------- */
				for (Appointment app : appointmentsFromAgenda.values())
				{
					if (waitingroomEntriesWithAppointments.containsKey(app.getId() + "_" + room.getId()))
						continue;
					/* ------------------------------------------------------- */
				
					Queue que = importAppointment(app, room.getId());
	//				fillPatientNameIfEmpty(que);
					
					if (sendMessages) {
						try {
							QueueUpdateEvent update = new QueueUpdateEvent("server", room.getId(), QueueUpdateEvent.ENTRY_ADD, que);
							sendQueueUpdateMessage(update);
						} catch (JMSException e) {
							logger.warn("Error: Can't send Queue update message ENTRY_ADD !", e);
						}					
					}
				}
				/* ------------------------------------------------------- */
				// check all waitingroom entries if they need to be updated or deleted
				/* ------------------------------------------------------- */
				for (String appointmentId : waitingroomEntriesWithAppointments.keySet())
				{
					Queue que = waitingroomEntriesWithAppointments.get(appointmentId);
					Integer appointmentId_new = que.getAppointmentId();
					
					// only check entries for current room
					if (! room.getId().equals(que.getRoomId()))
						continue;
					
					// delete waitingroom entry if the appointment was not longer in the agenda
					if (! appointmentsFromAgenda.containsKey(appointmentId_new))
					{	
						que = em.find(Queue.class, que.getId());
						if (que != null)
							em.remove(que);
						
						if (sendMessages) {
							try {	
								QueueUpdateEvent update = new QueueUpdateEvent("server", room.getId(), QueueUpdateEvent.ENTRY_DELETE, que);
								sendQueueUpdateMessage(update);
							} catch (JMSException e) {
								logger.warn("Error: Can't send Queue update message ENTRY_DELETE !", e);
							}						
						}
					} else {
						// update the waitingroom entry check for correct date and Time and room and update entry in db
						Date waitingroomDate = que.getStartDate();
						Date agendaDate = appointmentsFromAgenda.get(appointmentId_new).getStartDate();
						
						long waitingroomTime = waitingroomDate.getTime();
						long agendaTime = agendaDate.getTime();
						
						if(waitingroomTime != agendaTime)
						{
							waitingroomDate.setTime(agendaTime);
							que.setStartDate(waitingroomDate);	
						}
						
						int appPatientId = appointmentsFromAgenda.get(appointmentId_new).getPatientId();
						int quePatientId = que.getPatientId();
						
						if(appPatientId != quePatientId)
						{
							que.setPatientId(appPatientId);
						}
						
						if((waitingroomTime != agendaTime) || (appPatientId != quePatientId))
						{
							if (que != null)
								que = em.merge(que);
							
							fillPatientNameIfEmpty(que);
	
							if (sendMessages) {
								try {
									QueueUpdateEvent update = new QueueUpdateEvent("server", room.getId(), QueueUpdateEvent.ENTRY_UPDATE, que);
									sendQueueUpdateMessage(update);
								} catch (JMSException e) {
									logger.warn("Error: Can't send Queue update message ENTRY_UPDATE !", e);
								}							
							}
						}
					}
				}
			}
			
			time = System.currentTimeMillis()-time;
			logger.info("IMPORT from Agenda took: " + time);		
			logManager.createLogMessage(LogType.SYSTEM, WaitingroomModule.MODULE_NAME,"IMPORT from Agenda", "collectAppointments4Waitingrooms", time);			
			/* ================================================== */
			
		} catch (Exception e) {
			logger.error("Error inporting Appointments to Waitingwoom" , e);
		} finally {
			importRunning = false;
		}
		
	}
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#getQueues4Patient(java.lang.Integer)
	 */
	public List<Queue> getQueues4Patient(Integer patientId)
	{
		
		if (patientId == null)
			return null;
		
		// -------------------------------------------------------
		// get all Queues for that patient
		// -------------------------------------------------------
		Query q = em.createQuery("SELECT OBJECT(o) FROM Queue o " +
				"WHERE o.patientId = :patientId ORDER BY o.startDate");
		
		q.setParameter("patientId", patientId);
		
		try {
			@SuppressWarnings("unchecked")
			List<Queue> result = q.getResultList();
			return result;
		} catch (Exception e) {
			logger.error("Error getting queues for Patient: " + patientId, e);
		}
		return null;
	}
	
	
	/**
	 * @param appointment
	 */
	private Queue importAppointment(Appointment appointment, Integer roomId) {
		/* ================================================== */
		// create a new Queue
		/* ------------------------------------------------------- */
		Queue que = new Queue();
		/* ------------------------------------------------------- */
		// get the appointment for that day
		/* ------------------------------------------------------- */
		que.setAppointmentId(appointment.getId());
		/* ------------------------------------------------------- */
		if (appointment.getCreatedBy() != null) {
			que.setCreatedBy(appointment.getCreatedBy());			
		} else {
			que.setCreatedBy(1);
		}
		
		que.setPatientId(appointment.getPatientId());
		
		// set Patient
		que = fillPatientNameIfEmpty(que);
		
		/* ------------------------------------------------------- */
		que.setRoomId(roomId);
		que.setStartDate(appointment.getStartDate());
		/* ------------------------------------------------------- */
		que.setOrderId(getLastOrder(roomId)+1);
		/* ------------------------------------------------------- */
		que.setDescription(appointment.getDescription());
		/* ------------------------------------------------------- */
		// save
		que = em.merge(que);
		return que;
		/* ================================================== */
	}

	@SuppressWarnings("unchecked")
	@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
	public List<Room> getAllRooms(Integer currentSiteId) {
			/* ====================================================== */
			Query q = em.createQuery("SELECT OBJECT(o) FROM Room o WHERE o.isWaitingroom = true " +
				" AND " +
				"( o.siteId IS :currentSiteId" +
				" OR " + 
				"o.siteId IS NULL ) " +
				"ORDER BY o.orderId, o.id ASC").setParameter("currentSiteId", currentSiteId);
			try {
				/* ------------------------------------------------------- */
				return q.getResultList();
				/* ------------------------------------------------------- */
			} catch (Exception e) {
				logger.error("Error getting roomlist!", e);
			}
			return null;
			/* ====================================================== */
	}
	
	/**
	 * Sets the date to 02:00:00 on next day
	 * 
	 * @param date
	 */
	public static Date moveForeToTwo(Date date) {
		/* ================================================== */
		Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 2);
        cal.set(Calendar.MINUTE,      0);
        cal.set(Calendar.SECOND, 	  0);
        cal.set(Calendar.MILLISECOND, 0);
        cal.add(Calendar.DAY_OF_YEAR, 1);
        
        return cal.getTime();
		/* ================================================== */
	}
	
	/**
	 * Sets the date to 02:00:00
	 * 
	 * @param date
	 */
	public static Date moveBackToTwo(Date date) {
		/* ================================================== */
		Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 2);
        cal.set(Calendar.MINUTE,      0);
        cal.set(Calendar.SECOND, 	  0);
        cal.set(Calendar.MILLISECOND, 0);
        
        return cal.getTime();
		/* ================================================== */
	}
		
	/**
	 * Check if the date of today (toDayDate) is a date of the recurring appointment.
	 * 
	 * @param appointment the appointment to check
	 * @param toDayDate the date of today
	 * @param locale
	 * 
	 * @return true if the appointment have a recurrence today, false if not.
	 */
	public static Boolean isToDayDateADateOfRecurringAppointment(Appointment appointment, Date toDayDate, Locale locale)
	{
		Boolean match = false;
		
		// ==========================================================
		// set the untilDate
		Date untilDate = null;
		if (appointment.getUntil() == null)
			untilDate = toDayDate;
		else
			untilDate = appointment.getUntil();
		
		// ==========================================================
		// move the until date to midnight
		untilDate = DateUtil.move2Midnight(untilDate);
		
		// ==========================================================
		// D A I L Y
		// ==========================================================
		if (Appointment.DAILY == appointment.getFrequency())
		{
			// ==========================================================
			// get the recurrence rules of appointment to get the time interval
			HashMap<String, String> elements = new HashMap<String, String>();
			
			String[] tempS = appointment.getRRule().split(";");
			
			for (int i = 0; i < tempS.length; i++) {
				try {
					String[] parts = tempS[i].split("=");
					elements.put(parts[0], parts[1]);
				} catch (Exception e) {
				}
			}
			Integer interval = Integer.parseInt(elements.get(Appointment.INTERVAL));
			
			// ==========================================================
			// get the start date of appointment 
			Date startDate = new Date(appointment.getStartDate().getTime());
			
			Calendar calTemp = Calendar.getInstance(locale);
			calTemp.setTime(untilDate);

			calTemp.set(Calendar.DAY_OF_MONTH, calTemp.get(Calendar.DAY_OF_MONTH) + interval);

			Date newUntilDate = new Date();
			newUntilDate = calTemp.getTime();
			
			while (startDate.before(newUntilDate))
			{
				// ==========================================================
				// compute next date
				Calendar startCal = Calendar.getInstance(locale);
				startCal.setTime(startDate);
				Calendar toCal = Calendar.getInstance(locale);
				toCal.setTime(toDayDate);
				if(startCal.get(Calendar.DAY_OF_MONTH) == toCal.get(Calendar.DAY_OF_MONTH))
					if(startCal.get(Calendar.MONTH) == toCal.get(Calendar.MONTH))
						if(startCal.get(Calendar.YEAR) == toCal.get(Calendar.YEAR))
						{
							match = true;
							break;
						}
				
				Calendar cal = Calendar.getInstance(locale);
				cal.setTime(startDate);
				cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + interval);
				
				startDate = cal.getTime();
				
			}	
		}
		
		// ==========================================================
		// W E E K L Y
		// ==========================================================
		if (Appointment.WEEKLY == appointment.getFrequency())
		{
			// -------------------------------------------------------
			// get start date
			Date startDate = new Date(appointment.getStartDate().getTime());
			
			// -------------------------------------------------------
			// get the recurrence rules of appointment to get the time interval
			HashMap<String, String> elements = new HashMap<String, String>();
			String[] tempS = appointment.getRRule().split(";");
			for (int i = 0; i < tempS.length; i++) {
				try {
					String[] parts = tempS[i].split("=");
					elements.put(parts[0], parts[1]);
				} catch (Exception e) {
				}
			}
			Integer interval = Integer.parseInt(elements.get(Appointment.INTERVAL));
			
			// -------------------------------------------------------
			// get the the set days in the week
			int[] weekDays = null;
			String weekdays = (String)elements.get(Appointment.BYDAY);
			if (weekdays != null) {
				String[] days = weekdays.split(",");
				int[] dayConstants = new int[days.length];
				for (int i = 0; i < dayConstants.length; i++) {
					dayConstants[i] = Integer.parseInt(days[i]);
				}
				weekDays = dayConstants;
			}else weekDays = new int[0];
			
			// -------------------------------------------------------
			// set new until date
			Calendar calTemp = Calendar.getInstance(locale);
			calTemp.setTime(untilDate);

			calTemp.set(Calendar.DAY_OF_MONTH, calTemp.get(Calendar.DAY_OF_MONTH) + 1);

			Date newUntilDate = new Date();
			newUntilDate = untilDate;
			
			if (weekDays.length > 0)
			{
				boolean currentWeekTreated = false;
				while(startDate.before(newUntilDate))
				{
					Calendar cal = null;
					for (int i = 0 ; i < weekDays.length; i++)
					{
						// -------------------------------------------------------
						// compute next date. Specifying the Locale is important
						// because the Locale influences the value for 
						// minimalDaysInFirstWeek property of calendar, which in turn
						// is important for WEEK_OF_YEAR property to be handled properly.
						cal = Calendar.getInstance(locale);
						cal.setFirstDayOfWeek(Calendar.SUNDAY);
						cal.setTime(startDate);
						// -------------------------------------------------------
						// set week
						// we have to keep an eye on the current week if there are days
						// in the recurrence cycle that are between the start date and the 
						// end of the current week. They have be included in the cycle as well.
						
						int startDay = DateUtil.getDayOfWeek(appointment.getStartDate());
						
						if (!currentWeekTreated && startDay < weekDays[i])
							cal.set(Calendar.WEEK_OF_YEAR, cal.get(Calendar.WEEK_OF_YEAR));
						else {
							if(currentWeekTreated){
								cal.set(Calendar.WEEK_OF_YEAR, cal.get(Calendar.WEEK_OF_YEAR) + interval);
							}else continue;
						}
						/* ------------------------------------------------------- */
						cal.set(Calendar.DAY_OF_WEEK, weekDays[i]);
						
						// -------------------------------------------------------
						// check if date is today
						Calendar startCal = Calendar.getInstance(locale);
						startCal.setTime(startDate);
						Calendar toCal = Calendar.getInstance(locale);
						toCal.setTime(toDayDate);
						
						if(cal.get(Calendar.DAY_OF_MONTH) == toCal.get(Calendar.DAY_OF_MONTH))
							if(cal.get(Calendar.MONTH) == toCal.get(Calendar.MONTH))
								if(cal.get(Calendar.YEAR) == toCal.get(Calendar.YEAR))
								{
									match = true;
									break;
								}
						// -------------------------------------------------------	
					}
					
					// -------------------------------------------------------
					// set the start date to next week
					currentWeekTreated = true;
					startDate = cal.getTime();
					if(match) break;
				}
			}
		}
		
		
		// ==========================================================
		// M O N T H L Y
		// ==========================================================
		if (Appointment.MONTHLY == appointment.getFrequency())
		{
			// -------------------------------------------------------
			// get the recurrence rules of appointment to get the time interval
			HashMap<String, String> elements = new HashMap<String, String>();
			
			String[] tempS = appointment.getRRule().split(";");
			
			for (int i = 0; i < tempS.length; i++) {
				try {
					String[] parts = tempS[i].split("=");
					elements.put(parts[0], parts[1]);
				} catch (Exception e) {
				}
			}
			Integer interval = Integer.parseInt(elements.get(Appointment.INTERVAL));
			
			
			Date start = new Date(appointment.getStartDate().getTime());
			// -------------------------------------------------------
			// to compare start an d until date at the same time at midnight
			Date startDate = new Date(appointment.getStartDate().getTime());
			startDate = DateUtil.move2Midnight(startDate);
					
			while (startDate.before(untilDate))
			{
				// -------------------------------------------------------
				// compute next date
				Calendar cal = Calendar.getInstance();
				cal.setTime(start);

				if((cal.get(Calendar.MONTH) + interval) <= 12)
					cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + interval);
				else
				{
					cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 11);
					cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1);
				}
				Calendar startCal = Calendar.getInstance(locale);
				startCal.setTime(startDate);
				Calendar toCal = Calendar.getInstance(locale);
				toCal.setTime(toDayDate);
				if(cal.get(Calendar.DAY_OF_MONTH) == toCal.get(Calendar.DAY_OF_MONTH))
					if(cal.get(Calendar.MONTH) == toCal.get(Calendar.MONTH))
						if(cal.get(Calendar.YEAR) == toCal.get(Calendar.YEAR))
						{
							match = true;
							break;
						}

				start = cal.getTime();
				startDate = cal.getTime();
				startDate = DateUtil.move2Midnight(startDate);
			}
		}
		
		// ==========================================================
		// Y E A R L Y
		// ==========================================================
		if (Appointment.YEARLY == appointment.getFrequency())
		{
			// -------------------------------------------------------
			// get the recurrence rules of appointment to get the time interval
			HashMap<String, String> elements = new HashMap<String, String>();
			
			String[] tempS = appointment.getRRule().split(";");
			
			for (int i = 0; i < tempS.length; i++) {
				try {
					String[] parts = tempS[i].split("=");
					elements.put(parts[0], parts[1]);
				} catch (Exception e) {
				}
			}
			Integer interval = Integer.parseInt(elements.get(Appointment.INTERVAL));
			
			Date startDate = new Date(appointment.getStartDate().getTime());
			// -------------------------------------------------------
			// to compare start an d until date at the same time at midnight
			Date startDateAtMidnight = new Date(appointment.getStartDate().getTime());
			startDateAtMidnight = DateUtil.move2Midnight(startDateAtMidnight);
			
			while (startDateAtMidnight.before(untilDate))
			{
				// -------------------------------------------------------
				// compute next date
				Calendar cal = Calendar.getInstance(locale);
				cal.setTime(startDate);
				cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + interval);
				Calendar startCal = Calendar.getInstance(locale);
				startCal.setTime(startDate);
				Calendar toCal = Calendar.getInstance(locale);
				toCal.setTime(toDayDate);
				
				if(cal.get(Calendar.DAY_OF_MONTH) == toCal.get(Calendar.DAY_OF_MONTH))
					if(cal.get(Calendar.MONTH) == toCal.get(Calendar.MONTH))
						if(cal.get(Calendar.YEAR) == toCal.get(Calendar.YEAR))
						{
							match = true;
							break;
						}
				
				startDate = cal.getTime();
				startDateAtMidnight = cal.getTime();
				startDateAtMidnight = DateUtil.move2Midnight(startDateAtMidnight);
			}
		}
		return match;
	}
//	@PostConstruct
//	public void postConstruct () {
//	    instanceNr++;
//	    logger.info("WaitingroomManagerBean postConstruct instances: " + instanceNr);
//	}
//	
//	
//	@PreDestroy
//	public void preDestroy () {
//	    instanceNr--;
//	    logger.info("WaitingroomManagerBean preDestroy instances: " + instanceNr);
//	}	

	/* (non-Javadoc)
	 * @see lu.tudor.santec.gecamed.waitingroom.ejb.session.interfaces.WaitingroomManager#getRoomById(java.lang.Integer)
	 */
	public Room getRoomById(Integer roomId)
	{
		if (roomId == null)
			return null;
		// ------------------------------------------------------
		// get all Queues for that patient
		// -------------------------------------------------------
		Room result = em.find(Room.class, roomId);
		return result;
	}


}
