/*******************************************************************************
 * 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
 *******************************************************************************/
/**
 * @author Martin Heinemann martin.heinemann@tudor.lu
 *
 *
 *
 * @version
 * <br>$Log: DefaultNamedCalendar.java,v $
 * <br>Revision 1.44  2013-12-27 18:09:25  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.43  2012-11-05 14:36:46  troth
 * <br>Fix Ticket #1089 GECAMed hangs when function "Next Period" in Agenda is pressed several times.
 * <br>
 * <br>Revision 1.42  2012-06-28 15:34:07  troth
 * <br>Move the java class RRule from the package 'lu.tudor.santec.gecamed.agenda.gui.widgets.recurrence' to 'lu.tudor.santec.gecamed.agenda.utils' because the class is need in the agenda beans.
 * <br>
 * <br>Revision 1.41  2011-11-16 15:52:32  troth
 * <br>code clearup
 * <br>
 * <br>Revision 1.40  2011-11-09 14:45:02  troth
 * <br>Improved the function which compute the recurrence appointment dates.
 * <br>
 * <br>Revision 1.39  2011-10-20 15:04:28  troth
 * <br>1. add new functions of ticket #879
 * <br>2. fix ticket #904
 * <br>3. new bizcal lib
 * <br>
 * <br>Revision 1.38  2011-09-28 11:28:18  troth
 * <br>Second Version of function 'logging Agenda modifications in the admin log' Ticket #894 Logging for Agenda modifications
 * <br>
 * <br>Revision 1.37  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.36  2011-09-15 15:44:33  troth
 * <br>remove some system.out.printlin
 * <br>
 * <br>Revision 1.35  2011-09-14 12:20:34  troth
 * <br>Add the new function for printing a appointment overview for the physician.
 * <br>
 * <br>Revision 1.34  2011-08-05 15:06:07  troth
 * <br>fix ticket #891
 * <br>
 * <br>Revision 1.33  2011-07-14 12:40:47  troth
 * <br>Clean up code.
 * <br>
 * <br>Revision 1.32  2011-06-28 13:48:27  troth
 * <br>testing agenda
 * <br>
 * <br>Revision 1.30  2011-05-10 16:50:52  troth
 * <br>code clean up
 * <br>
 * <br>Revision 1.29  2011-05-09 14:30:20  troth
 * <br>Fix Ticket 839: "End after [X] occurrences" not taken into account
 * <br>http://santec.tudor.lu/trac/gecamed/ticket/839
 * <br>
 * <br>Revision 1.28  2011-04-04 11:38:31  troth
 * <br>Redesign of the appointment create dialog and the right navi bar.
 * <br>
 * <br>Revision 1.27  2011-02-16 10:47:41  troth
 * <br>check in new bizcal jar and add the new bizcal view layout to the agenda
 * <br>
 * <br>Revision 1.26  2009-12-01 16:20:07  mack
 * <br>Fixed a bug causing recurring events to lockup application. Using WEEK_OF_YEAR property of GregorianCalendar requires setMinimumDaysForWeek to be set property to work properly. Specifying a locale at calendar creation time takes care of this
 * <br>
 * <br>Revision 1.25  2009-04-03 13:22:05  heinemann
 * <br>fix for: Ticket #266 (new enhancement)
 * <br>
 * <br>If Color of a physician is changed, it will not change the color of his Calendar
 * <br>
 * <br>Revision 1.24  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.23  2008-06-24 13:18:48  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.22  2008-05-29 12:43:39  heinemann
 * <br>fixed recurrence bug for appointments that recurr per week.
 * <br>The days in the current week are now also treated.
 * <br>
 * <br>Revision 1.21  2008-04-14 13:11:36  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.20  2008-03-27 14:06:16  heinemann
 * <br>nullpointer fixed, when color of a calendar is null
 * <br>
 * <br>Revision 1.19  2008-01-21 09:27:04  heinemann
 * <br>code cleanup and java doc
 * <br>
 * <br>Revision 1.18  2007-11-20 08:58:53  hermen
 * <br>moved Managerfactory to core.utils and refactured code to use ManagerFactory instead of context.lookup
 * <br>
 * <br>Revision 1.17  2007-09-26 13:00:14  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.16  2007/08/28 11:34:36  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.15  2007/08/22 11:59:53  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.14  2007/08/09 14:10:34  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.13  2007/07/03 07:35:52  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.12  2007/07/03 06:41:54  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.11  2007/07/02 13:28:16  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.10  2007/06/26 08:56:03  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.9  2007/06/22 13:15:11  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.8  2007/06/14 13:32:22  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.7  2007/06/11 11:35:21  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.6  2007/06/08 12:20:55  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.5  2007/06/06 11:23:20  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.4  2007/06/04 07:23:03  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.3  2007/06/01 08:45:37  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.2  2007/05/31 13:00:17  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.1  2007/05/25 13:50:25  heinemann
 * <br>pres-weekend checkin
 * <br>
 *
 */
package lu.tudor.santec.gecamed.agenda.gui;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.jms.JMSException;
import javax.swing.ImageIcon;

import lu.tudor.santec.bizcal.NamedCalendar;
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.beans.AppointmentManagerBean;
import lu.tudor.santec.gecamed.agenda.ejb.session.interfaces.AppointmentManager;
import lu.tudor.santec.gecamed.agenda.gui.widgets.AppointmentCreateDialog;
import lu.tudor.santec.gecamed.agenda.utils.RRule;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;

import org.apache.log4j.Logger;

import bizcal.common.Event;
import bizcal.util.DateUtil;

/**
 * Implementation of the NamedCalendar interface of the tudor bizcal calendar.
 * It is a kind of model for a calendar.
 * 
 * @author martin.heinemann@tudor.lu
 * 22.05.2007
 * 16:18:09
 *
 *
 * @version
 * <br>$Log: DefaultNamedCalendar.java,v $
 * <br>Revision 1.44  2013-12-27 18:09:25  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.43  2012-11-05 14:36:46  troth
 * <br>Fix Ticket #1089 GECAMed hangs when function "Next Period" in Agenda is pressed several times.
 * <br>
 * <br>Revision 1.42  2012-06-28 15:34:07  troth
 * <br>Move the java class RRule from the package 'lu.tudor.santec.gecamed.agenda.gui.widgets.recurrence' to 'lu.tudor.santec.gecamed.agenda.utils' because the class is need in the agenda beans.
 * <br>
 * <br>Revision 1.41  2011-11-16 15:52:32  troth
 * <br>code clearup
 * <br>
 * <br>Revision 1.40  2011-11-09 14:45:02  troth
 * <br>Improved the function which compute the recurrence appointment dates.
 * <br>
 * <br>Revision 1.39  2011-10-20 15:04:28  troth
 * <br>1. add new functions of ticket #879
 * <br>2. fix ticket #904
 * <br>3. new bizcal lib
 * <br>
 * <br>Revision 1.38  2011-09-28 11:28:18  troth
 * <br>Second Version of function 'logging Agenda modifications in the admin log' Ticket #894 Logging for Agenda modifications
 * <br>
 * <br>Revision 1.37  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.36  2011-09-15 15:44:33  troth
 * <br>remove some system.out.printlin
 * <br>
 * <br>Revision 1.35  2011-09-14 12:20:34  troth
 * <br>Add the new function for printing a appointment overview for the physician.
 * <br>
 * <br>Revision 1.34  2011-08-05 15:06:07  troth
 * <br>fix ticket #891
 * <br>
 * <br>Revision 1.33  2011-07-14 12:40:47  troth
 * <br>Clean up code.
 * <br>
 * <br>Revision 1.32  2011-06-28 13:48:27  troth
 * <br>testing agenda
 * <br>
 * <br>Revision 1.30  2011-05-10 16:50:52  troth
 * <br>code clean up
 * <br>
 * <br>Revision 1.29  2011-05-09 14:30:20  troth
 * <br>Fix Ticket 839: "End after [X] occurrences" not taken into account
 * <br>http://santec.tudor.lu/trac/gecamed/ticket/839
 * <br>
 * <br>Revision 1.28  2011-04-04 11:38:31  troth
 * <br>Redesign of the appointment create dialog and the right navi bar.
 * <br>
 * <br>Revision 1.27  2011-02-16 10:47:41  troth
 * <br>check in new bizcal jar and add the new bizcal view layout to the agenda
 * <br>
 * <br>Revision 1.26  2009-12-01 16:20:07  mack
 * <br>Fixed a bug causing recurring events to lockup application. Using WEEK_OF_YEAR property of GregorianCalendar requires setMinimumDaysForWeek to be set property to work properly. Specifying a locale at calendar creation time takes care of this
 * <br>
 * <br>Revision 1.25  2009-04-03 13:22:05  heinemann
 * <br>fix for: Ticket #266 (new enhancement)
 * <br>
 * <br>If Color of a physician is changed, it will not change the color of his Calendar
 * <br>
 * <br>Revision 1.24  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.23  2008-06-24 13:18:48  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.22  2008-05-29 12:43:39  heinemann
 * <br>fixed recurrence bug for appointments that recurr per week.
 * <br>The days in the current week are now also treated.
 * <br>
 * <br>Revision 1.21  2008-04-14 13:11:36  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.20  2008-03-27 14:06:16  heinemann
 * <br>nullpointer fixed, when color of a calendar is null
 * <br>
 * <br>Revision 1.19  2008-01-21 09:27:04  heinemann
 * <br>code cleanup and java doc
 * <br>
 * <br>Revision 1.18  2007-11-20 08:58:53  hermen
 * <br>moved Managerfactory to core.utils and refactured code to use ManagerFactory instead of context.lookup
 * <br>
 * <br>Revision 1.1  2007/05/25 13:50:25  heinemann
 * <br>pres-weekend checkin
 * <br>
 *
 */
public class DefaultNamedCalendar extends NamedCalendar {

	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(DefaultNamedCalendar.class.getName());

	private AgendaCalendar agenda;
	private Locale locale = Locale.getDefault();
	private Date lastToDate 	= new Date();
	private Date lastFromDate 	= new Date();

	public DefaultNamedCalendar(String name, AgendaCalendar agenda, Locale locale) {
		/* ================================================== */
		this(name,locale,agenda.isBackground());
		this.agenda = agenda;
		this.setId(agenda.getId());

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


	/**
	 * @param name
	 */
	public DefaultNamedCalendar(String name, Locale locale) {
		super(name);
		this.setLocale(locale);
	}

	/**
	 * @param name
	 * @param description
	 */
	public DefaultNamedCalendar(String name, String description, Locale locale) {
		super(name, description);
		this.setLocale(locale);
	}

	/**
	 * @param name
	 * @param description
	 * @param color
	 */
	public DefaultNamedCalendar(String name, String description, Color color, Locale locale) {
		super(name, description, color);
		this.setLocale(locale);
	}

	/**
	 * @param name
	 * @param locale
	 * @param isBackground
	 */
	public DefaultNamedCalendar(String name, Locale locale, Boolean isBackground) {
		super(name, isBackground);
		this.setLocale(locale);
	}
	
	/**
	 * @param name
	 * @param description
	 * @param color
	 */
	public void setLocale (Locale locale) {
		this.locale = (locale != null) ? locale : Locale.getDefault();
	}
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.bizcal.NamedCalendar#addEvent(bizcal.common.Event)
	 */
	@Override
	public List<Event> addEvent(String clientId, Event event) {
		/* ====================================================== */
		return saveEvent(clientId, event, true);
		/* ====================================================== */
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.bizcal.NamedCalendar#deleteEvent(bizcal.common.Event)
	 */
	@Override
	public void deleteEvent(String clientId, Event event) {
		logger.info("Deleting Appointment: " + event);
		/* ====================================================== */
		if (event == null)
			return;
		/* ------------------------------------------------------- */
		if (event.getId() == null || !(event.getId() instanceof Appointment))
			return;
		/* ------------------------------------------------------- */
		Appointment ap = (Appointment) event.getId();
		if (ap.getId() == null)
			return;
		/* ------------------------------------------------------- */
		AppointmentManager aManager = (AppointmentManager) ManagerFactory.getRemote(AppointmentManagerBean.class);
		try {
			/* ------------------------------------------------------- */
			aManager.deleteAppointment(MainFrame.getClientId(),ap);

			try {
				aManager.sendCalendarUpdateMessage(clientId,
												   AppointmentManager.MSG_UPDATE_CALENDAR_EVENTS,
												   ap.getCalendarId(),
												   event.getStart());
			} catch (JMSException e) {
				logger.warn("Error sending Update Message", e);
			}
			/* ------------------------------------------------------- */
		} catch (Exception e) {
		}
		/* ====================================================== */
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.bizcal.NamedCalendar#getEvents(java.util.Date, java.util.Date)
	 */
	@Override
	public List<Event> getEvents(Date from, Date to) {
		/* ====================================================== */
		AppointmentManager aManager = (AppointmentManager)
			ManagerFactory.getRemote(AppointmentManagerBean.class);
		// save the to date
		this.lastToDate = to;
		this.lastFromDate = from;
		
		List<Appointment> apps = aManager.getAppointments(agenda.getId(), from, to, MainFrame.getClientId());
		/* ------------------------------------------------------- */
		if (apps == null)
			return new ArrayList<Event>(0);
		/* ------------------------------------------------------- */
		List<Event> returnList = new ArrayList<Event>();
		for (Appointment a : apps) {
			/* ------------------------------------------------------- */
			if (Appointment.NO_RECUR == a.getFrequency()) {
				/* ------------------------------------------------------- */
				Event e = AgendaModule.appointment2Event(a);
				e.set(NamedCalendar.CALENDAR_NAME, this.getName());
				// set the calendar id of the event
				e.set(Event.CALENDAR_ID, this.getId());
				// set Calendar isBackground to Event
				e.set(Event.CALENDAR_IS_BACKGROUND, this.isBackground());
				if (agenda.getColor() != null)
					e.setColor(new Color(agenda.getColor()));
				/* ------------------------------------------------------- */
				e.setId(a);
				returnList.add(e);
				/* ------------------------------------------------------- */
			} else {
				/* ------------------------------------------------------- */
				returnList.addAll(getRecurrEvents(a, from, to));
				/* ------------------------------------------------------- */
			}
			
			/* ------------------------------------------------------- */
		}

		return returnList;
		/* ====================================================== */
	}

	
	/**
	 * @param app
	 * @param toDate
	 * @return
	 */
	public List<Event> getRecurrEvents(Appointment app, Date fromDate, Date toDate) {
		/* ================================================== */
		// static call of the convert method
		List<Event> recList = createRecurrEvents(app, fromDate, toDate, this.locale);
		/* ------------------------------------------------------- */
		// add additional infos to the events
		if (recList != null)
			for (Event e : recList) {
				/* ------------------------------------------------------- */
				e.set(NamedCalendar.CALENDAR_NAME, this.getName());
				// set the calender id of the event
				e.set(Event.CALENDAR_ID, this.getId());
				// set Calendar isBackground to Event
				e.set(Event.CALENDAR_IS_BACKGROUND, AgendaModule.getCalendarForId(app.getCalendarId()).isBackground());
				e.setColor(new Color(AgendaModule.getCalendarForId(app.getCalendarId()).getColor()));
				/* ------------------------------------------------------- */
			}
		return recList;
		/* ================================================== */
	}

	
	/**
	 * Creates a list of events for each recurring event of the appointment to special date (toDate).
	 * All recurring event bevor and at the special date (toDate) are collected in the list.
	 * @param app the appointment
	 * @param toDate
	 * @param locale
	 * @return the list of events
	 */
	public static List<Event> createRecurrEvents(Appointment app, Date fromDate, Date toDate, Locale locale)
	{
		// ==========================================================
		// create a event list and add the appointment as a event 
		List<Event> recurList = new ArrayList<Event>();
		Event e = AgendaModule.appointment2Event(app);
		e.setId(app);
		recurList.add(e);
		
		/* ------------------------------------------------------- */	
		Date until = toDate;
		if (app.getUntil() != null && app.getUntil().before(toDate)){
			until = app.getUntil();
		}
		
		/* ------------------------------------------------------- */
		// move the until date to midnight, because we want to create
		// recurring appointments on the last day
		/* ------------------------------------------------------- */
		until = DateUtil.move2Midnight(until);
		
		// ==========================================================
		// if no recurrence is set for the appointment return the
		// event list
		if (Appointment.NO_RECUR == app.getFrequency())
			return recurList;
		
		/* ------------------------------------------------------- */
		// recur icon
		ImageIcon recIcon = AgendaModule.getMiniIcon(AgendaModule.RECURRUNCE);
		if (Appointment.NO_RECUR < app.getFrequency())
			e.setUpperRightIcon(recIcon);
		/* ------------------------------------------------------- */
		// D A I L Y
		// ==========================================================
		if (Appointment.DAILY == app.getFrequency())
		{
			RRule rule = new RRule(app.getRRule());
			Integer interval = rule.getInterval();
			
			Date start = new Date(e.getStart().getTime());
			
			Calendar calTemp = Calendar.getInstance(locale);
			calTemp.setTime(until);

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

			Date newUntil = new Date();
			newUntil = calTemp.getTime();
			while (start.before(newUntil))
			{
				/* ------------------------------------------------------- */
				// compute next date
				Calendar cal = Calendar.getInstance(locale);
				cal.setTime(start);

				cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + interval);
				
				start = cal.getTime();

				/* ------------------------------------------------------- */
				if (start.after(fromDate)) {
					Event er = AgendaModule.appointment2Event(app);
					er.move(cal.getTime());
					er.setUpperRightIcon(recIcon);
					er.setId(app);
					recurList.add(er);
				}
				/* ------------------------------------------------------- */

			}
		}
		
		// ==========================================================
		// W E E K L Y
		// ==========================================================
		if (Appointment.WEEKLY == app.getFrequency())
		{
			RRule rule = new RRule(app.getRRule());
			Integer interval = rule.getInterval();
			
			Date start = new Date(e.getStart().getTime());
			int[] weekDays = rule.getWeekDays();
			
			if (weekDays.length > 0) {
				/* ------------------------------------------------------- */
				boolean currentWeekTreated = false;
				while(start.before(until)) {
//					System.out.println("++ start date: " + start);
//					System.out.println("++ until date: " + until);
					/* ------------------------------------------------------- */
					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(cal.getFirstDayOfWeek());
						cal.setTime(start);
						/* ------------------------------------------------------- */
						// 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(e.getStart());
						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);
								cal.add(Calendar.WEEK_OF_YEAR, interval);
							} else {
								continue;								
							}
						}

						/* ------------------------------------------------------- */
						cal.set(Calendar.DAY_OF_WEEK, weekDays[i]);

						/* ------------------------------------------------------- */
						if (cal.getTime().after(fromDate)) {
							Event er = AgendaModule.appointment2Event(app);
							er.setUpperRightIcon(recIcon);
							er.setId(app);
							er.move(cal.getTime());
							recurList.add(er);
						}
						/* ------------------------------------------------------- */
						
					}
					currentWeekTreated = true;
					if (cal == null)
						break;
					start = cal.getTime();
				}
			}
		}
		
		// ==========================================================
		// M O N T H L Y
		// ==========================================================
		if (Appointment.MONTHLY == app.getFrequency())
		{
			RRule rule = new RRule(app.getRRule());
			Integer interval = rule.getInterval();
			
			Date start = new Date(e.getStart().getTime());
			// to compare start an d until date at the same time at midnight
			Date startDateAtMidnight = new Date(e.getStart().getTime());
			startDateAtMidnight = DateUtil.move2Midnight(startDateAtMidnight);
			
			while (startDateAtMidnight.before(until))
			{		
				/* ------------------------------------------------------- */
				// 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);
				}
				
				/* ------------------------------------------------------- */
				if (cal.getTime().after(fromDate)) {
					Event er = AgendaModule.appointment2Event(app);
					er.move(cal.getTime());
					er.setUpperRightIcon(recIcon);
					er.setId(app);
					recurList.add(er);
				}
				/* ------------------------------------------------------- */
				
				start = cal.getTime();
				startDateAtMidnight = cal.getTime();
				startDateAtMidnight = DateUtil.move2Midnight(startDateAtMidnight);
			}
		}
		
		// ==========================================================
		// Y E A R L Y
		// ==========================================================
		if (Appointment.YEARLY == app.getFrequency())
		{
			RRule rule = new RRule(app.getRRule());
			Integer interval = rule.getInterval();
			
			Date start = new Date(e.getStart().getTime());
			
			// to compare start an d until date at the same time at midnight
			Date startDateAtMidnight = new Date(e.getStart().getTime());
			startDateAtMidnight = DateUtil.move2Midnight(startDateAtMidnight);
			
			while (startDateAtMidnight.before(until))
			{
				/* ------------------------------------------------------- */
				// compute next date
				Calendar cal = Calendar.getInstance(locale);
				cal.setTime(start);
				cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + interval);
				
				/* ------------------------------------------------------- */
				if (cal.getTime().after(fromDate)) {
					Event er = AgendaModule.appointment2Event(app);
					er.move(cal.getTime());
					er.setUpperRightIcon(recIcon);
					er.setId(app);
					recurList.add(er);
				}
				/* ------------------------------------------------------- */
				
				start = cal.getTime();
				startDateAtMidnight = cal.getTime();
				startDateAtMidnight = DateUtil.move2Midnight(startDateAtMidnight);
			}
		}
		// list of events
		return recurList;
	}
	
	
	/* (non-Javadoc)
	 * @see lu.tudor.santec.bizcal.NamedCalendar#saveEvent(bizcal.common.Event)
	 */
	@Override
	public List<Event> saveEvent(final String clientId, Event event, boolean userInteraction) {
		String msg = "";
		/* ====================================================== */
		if (event == null)
			return null;
		/* ------------------------------------------------------- */
		if (userInteraction) {
			
			/* ------------------------------------------------------- */
			// show dialog
			AppointmentCreateDialog nd = AppointmentCreateDialog.getInstance();
			Appointment ap = null;

			if (event.getId() == null) {
				/* ------------------------------------------------------- */
				nd.showDialog(this.agenda,
						event.getStart(), event.getEnd());
				ap = nd.getAppointment();
				/* ------------------------------------------------------- */
			} else {
				/* ------------------------------------------------------- */
				// all appointment type open in the same dialog
				nd.showDialog((Appointment) event.getId());
				ap = nd.getAppointment();
				/* ------------------------------------------------------- */
			}
			long start = System.currentTimeMillis();
			/* ------------------------------------------------------- */
			if (ap == null)
				return null;
			/* ------------------------------------------------------- */

			final AppointmentManager aManager = (AppointmentManager)
			ManagerFactory.getRemote(AppointmentManagerBean.class);
			/* ------------------------------------------------------- */
			if (event.getId() != null)
				ap.setId(((Appointment)event.getId()).getId());
			/* ------------------------------------------------------- */
			
			final Appointment newApp = aManager.saveAppointment(ap);
			
			Event newEvent = AgendaModule.appointment2Event(newApp);
			newEvent.setId(newApp);
			newEvent.setColor(new Color(AgendaModule.getCalendarForId(newApp.getCalendarId()).getColor()));
			// set the calender id of the event
			newEvent.set(Event.CALENDAR_ID, newApp.getCalendarId());
			// set Calendar isBackground to Event
			newEvent.set(Event.CALENDAR_IS_BACKGROUND, AgendaModule.getCalendarForId(newApp.getCalendarId()).isBackground());
			/* ------------------------------------------------------- */
			// inform other clients - asynchrounous
			try {
				new Thread() {
					public void run() {
						try {
							aManager.sendCalendarUpdateMessage(clientId, AppointmentManager.MSG_UPDATE_CALENDAR_EVENTS, newApp.getCalendarId(), newApp.getStartDate());						
						} catch (Exception e) {
							logger.error("Error sending JMS: ", e);
						}
					}
				}.start();				
			} catch (Throwable e) {
				logger.info("Error sending JMS: ", e);
			}
			List<Event> list = getRecurrEvents(newApp, this.lastFromDate, this.lastToDate);
			
			// Log the user actions in agenda
			AgendaModule.logUserActionAgenda(ap, AgendaModule.CHANGE_APPOINTMENT_BY_DIALOG, System.currentTimeMillis()- start);
			
			/* ------------------------------------------------------- */
			return list;
			
		} else {
			// just save
			/* ------------------------------------------------------- */
			// if there is an Appointment object attached, we must
			// copy the data of the event to the appointment
			// otherwise we will create a new appointment with the
			// data of the Event
			/* ------------------------------------------------------- */
			long start = System.currentTimeMillis();
			Appointment ap = null;
			if (event.getId() instanceof Appointment) {
				/* ------------------------------------------------------- */
				ap = (Appointment) event.getId();
				msg = AgendaModule.CHANGE_APPOINTMENT;
				/* ------------------------------------------------------- */
				
			} else {
				/* ------------------------------------------------------- */
				ap = new Appointment();
				msg = AgendaModule.CREATE_APPOINTMENT;
				// no type
				/* ------------------------------------------------------- */
			}
			ap.setStartDate(event.getStart());
			ap.setEndDate(event.getEnd());
			ap.setDescription(event.getDescription());
			ap.setSummary(event.getSummary());
			/* ------------------------------------------------------- */
			// set the calendar id, but only if the appointment does not have one
			if (ap.getCalendarId() == null)
				ap.setCalendarId(this.agenda.getId());
			/* ------------------------------------------------------- */
			AppointmentManager aManager = (AppointmentManager) ManagerFactory.getRemote(AppointmentManagerBean.class);
			/* ------------------------------------------------------- */
			// save
			Appointment newApp = aManager.saveAppointment(ap);
			
			Event newEvent = AgendaModule.appointment2Event(newApp);
			newEvent.setId(newApp);
			newEvent.setColor(new Color(agenda.getColor()));

			// inform other calendars
			try {
				aManager.sendCalendarUpdateMessage(clientId, AppointmentManager.MSG_UPDATE_CALENDAR_EVENTS, newApp.getCalendarId(), newApp.getStartDate());
			} catch (JMSException e) {
				e.printStackTrace();
			}

			List<Event> list = getRecurrEvents(newApp, this.lastFromDate, this.lastToDate);
			
			// Log the user actions in agenda
			AgendaModule.logUserActionAgenda(ap, msg, System.currentTimeMillis()- start);
			
			return list;
			/* ------------------------------------------------------- */
		}
		/* ====================================================== */
	}
	
	public Boolean isBackground()
	{
		return this.agenda.isBackground();
	}

}