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

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

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.gui.AgendaModule;
import lu.tudor.santec.gecamed.agenda.ejb.entity.beans.AppointmentType;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Categories;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.Method;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Version;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import bizcal.common.Event;

/**
 * Exports Event and Appointment objects into ical format.
 * Uses iCal4j lib.
 * 
 * 
 * @author johannes.hermen@tudor.lu
 * 18.01.2008
 * 08:10:04
 *
 *
 * @version
 * <br>$Log: AppointmentICALExporter.java,v $
 * <br>Revision 1.8  2013-12-27 18:09:26  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.7  2013-07-15 06:18:37  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.6  2012-06-28 15:32:31  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.5  2012-04-24 14:02:13  troth
 * <br>Remove same warnings.
 * <br>
 * <br>Revision 1.4  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.3  2008-01-18 16:09:05  heinemann
 * <br>code cleanup and java doc
 * <br>
 *   
 */

public class IcalImportExporter {

	private static final String CALENDAR 	= "CALENDAR";
	private static final String APPOINTMENT = "APPOINTMENT";
	private static final String BUYSTIME 	= "BUSYTIME";
	private boolean showCalendarName = true;
	private boolean showAppType = true;
	private HashMap<Integer, AgendaCalendar> calendarHash = new HashMap<Integer, AgendaCalendar>();
	private HashMap<Integer, AppointmentType> appointmentTypeHash = new HashMap<Integer, AppointmentType>();
	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(IcalImportExporter.class
			.getName());

	/**
	 * 
	 */
	public IcalImportExporter(Collection<AgendaCalendar> cals, Collection<AppointmentType> types) {
		if (cals != null) {
			calendarHash.clear();
			for (AgendaCalendar calendar : cals) {
				calendarHash.put(calendar.getId(), calendar);
			}
		}
		
		if (types != null) {
			appointmentTypeHash.clear();
			for (AppointmentType appointmentType : types) {
				appointmentTypeHash.put(appointmentType.getId(), appointmentType);
			}
		}
	}
		
	/**
	 * @param apps
	 * @return 
	 */
	public Calendar exportEvents(Collection<Event> events) {
		
		Calendar calendar = new net.fortuna.ical4j.model.Calendar();
		calendar.getProperties().add(new ProdId("GECAMed//Ben Fortuna//iCal4j 1.0//EN"));
		calendar.getProperties().add(Version.VERSION_2_0);
		calendar.getProperties().add(CalScale.GREGORIAN);
		calendar.getProperties().add(Method.PUBLISH);
		
		for (Iterator<Event> iter = events.iterator(); iter.hasNext();) {
			Event ev = (Event) iter.next();
			Appointment app = (Appointment) ev.getId();
			
			String summary = "";
			
			if (showCalendarName && calendarHash.containsKey(app.getCalendarId())) {
				summary += "Cal: " + calendarHash.get(app.getCalendarId()).getTitle() + "\n";
			}
			if (showAppType && appointmentTypeHash.containsKey(app.getTypeId())) {
				summary += "Typ: " + appointmentTypeHash.get(app.getTypeId()).getName() + "\n";
			}
			summary += app.getSummary();
			
			VEvent vev = new VEvent(
					new DateTime(ev.getStart()),
					new DateTime(ev.getEnd()),
					summary
				);
			
			Description d = new Description(app.getDescription());
			vev.getProperties().add(d);
			Location l = new Location(calendarHash.get(app.getCalendarId()).getTitle());
			vev.getProperties().add(l);

			vev.getProperties().add(new Uid(CALENDAR + "_" + app.getCalendarId()+"@"+ APPOINTMENT + "-" + ev.hashCode()));
			calendar.getComponents().add(vev);
		}
		return calendar;
	}
	
	/**
	 * @param apps
	 * @return 
	 */
	public Calendar exportAppointments(Collection<Appointment> apps) {
		
		Calendar calendar = new net.fortuna.ical4j.model.Calendar();
		calendar.getProperties().add(new ProdId("GECAMed//Ben Fortuna//iCal4j 1.0//EN"));
		calendar.getProperties().add(Version.VERSION_2_0);
		calendar.getProperties().add(CalScale.GREGORIAN);
		calendar.getProperties().add(Method.PUBLISH);
		
		for (Iterator<Appointment> iter = apps.iterator(); iter.hasNext();) {
			Appointment app = (Appointment) iter.next();
			
			
			String summary = "";
			
			if (showCalendarName && calendarHash.containsKey(app.getCalendarId())) {
				summary += "Cal: " + calendarHash.get(app.getCalendarId()).getTitle() + "\n";
			}
			if (showAppType && appointmentTypeHash.containsKey(app.getTypeId())) {
				summary += "Typ: " + appointmentTypeHash.get(app.getTypeId()).getName() + "\n";
			}
			summary += app.getSummary();
			
			VEvent vev = new VEvent(
					new DateTime(app.getStartDate()),
					new DateTime(app.getEndDate()),
					summary
				);
			
			Description d = new Description(app.getDescription());
			vev.getProperties().add(d);
			Location l = new Location(calendarHash.get(app.getCalendarId()).getTitle());
			vev.getProperties().add(l);

			vev.getProperties().add(new Uid(CALENDAR + "_" + app.getCalendarId()+"@"+ APPOINTMENT + "-" + app.getAppointmentUID()));
			calendar.getComponents().add(vev);
		}
		return calendar;
	}
	
	/**
	 * @param apps
	 */
	public Calendar exportBuystimes(Collection<Appointment> apps) {
		
		Calendar calendar = new net.fortuna.ical4j.model.Calendar();
		calendar.getProperties().add(new ProdId("GECAMed//Ben Fortuna//iCal4j 1.0//EN"));
		calendar.getProperties().add(Version.VERSION_2_0);
		calendar.getProperties().add(CalScale.GREGORIAN);
		calendar.getProperties().add(Method.PUBLISH);
		
		for (Iterator<Appointment> iter = apps.iterator(); iter.hasNext();) {
			Appointment app = (Appointment) iter.next();
			
			// SUMMARY
			String summary = "GECAMed Appointment";
			VEvent vev = new VEvent(
					new DateTime(app.getStartDate()),
					new DateTime(app.getEndDate()),
					summary
				);

			// CATEGORIES
			vev.getProperties().add(new Categories(BUYSTIME));
			
			// DESCRIPTION
			Description d = new Description("Unavailable Timeslot");
			vev.getProperties().add(d);
			
			// LOCATION
			Location l = new Location(calendarHash.get(app.getCalendarId()).getTitle()+"@GECAMed");
			vev.getProperties().add(l);

			// UID
			vev.getProperties().add(new Uid(APPOINTMENT + "_" + app.getAppointmentUID() +"@"+ CALENDAR + "_" + app.getCalendarId()));

			calendar.getComponents().add(vev);
		}
		return calendar;
	}
	
	/**
	 * @param f
	 */
	public void writeICALFile(Calendar calendar, File f) {
		FileOutputStream fout;
		try {
			fout = new FileOutputStream(f);
			CalendarOutputter outputter = new CalendarOutputter();
			outputter.output(calendar, fout);
			fout.close();
		} catch (Exception e) {
			logger.log(Level.WARN, "writing ICS file failed", e);
		}
	}

	/**
	 * 
	 */
	public byte[] createICAL(Calendar calendar) {
		try {
			ByteArrayOutputStream fout = new ByteArrayOutputStream();
			CalendarOutputter outputter = new CalendarOutputter();
			outputter.output(calendar, fout);
			fout.close();
			return fout.toByteArray();
		} catch (Exception e) {
			logger.log(Level.WARN, "writing ICS file failed", e);
		}
		return null;
	}

	/**
	 * @return the showCalendarName
	 */
	public boolean isShowCalendarName() {
		return showCalendarName;
	}

//	/**
//	 * @param calWeekdays
//	 * @return
//	 */
//	private String getICALWeekDays(int[] calWeekdays) {
//		StringBuffer sb = new StringBuffer();
//		for (int i = 0; i < calWeekdays.length; i++) {
//			switch (calWeekdays[i]) {
//			case Calendar.MONDAY:
//				sb.append(WeekDay.MO + ",");
//				break;
//			case Calendar.TUESDAY:
//				sb.append(WeekDay.TU + ",");
//				break;
//			case Calendar.WEDNESDAY:
//				sb.append(WeekDay.WE + ",");
//				break;
//			case Calendar.THURSDAY:
//				sb.append(WeekDay.TH + ",");
//				break;
//			case Calendar.FRIDAY:
//				sb.append(WeekDay.FR + ",");
//				break;
//			case Calendar.SATURDAY:
//				sb.append(WeekDay.SA + ",");
//				break;
//			case Calendar.SUNDAY:
//				sb.append(WeekDay.SU + ",");
//				break;
//			default:
//				break;
//			}
//		}
//		return sb.toString();
//	}
	
	/**
	 * @param showCalendarName the showCalendarName to set
	 */
	public void setShowCalendarName(boolean showCalendarName) {
		this.showCalendarName = showCalendarName;
	}
	
	/**
	 * @param f
	 * @return 
	 */
	public static Calendar readICSFile(File f) throws Exception{
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(f);
			CalendarBuilder builder = new CalendarBuilder();
			return builder.build(fin);
		} catch (Exception e) {
			logger.log(Level.WARN, "reading ICS file failed", e);
			throw e;
		} finally {
			if (fin != null)
				fin.close();
		}
	}
	
	/**
	 * @param f
	 * @return 
	 */
	public Calendar readICS(byte[] data) throws Exception{
		ByteArrayInputStream fin = null;
		try {
			fin = new ByteArrayInputStream(data);
			CalendarBuilder builder = new CalendarBuilder();
			return builder.build(fin);
		} catch (Exception e) {
			logger.log(Level.WARN, "reading ICS data failed", e);
			throw e;
		} finally {
			if (fin != null)
				fin.close();
		}
	}
	
	public Collection<Appointment> importAppointments(Calendar calendar, Integer calendarID, Integer appointmentTypeID) throws ParseException {
		Collection<Appointment> apps = new ArrayList<Appointment>();
		
		if (calendar != null) {
            
            //Iterating over a Calendar
            for (Iterator<?> i = calendar.getComponents().iterator(); i.hasNext();) {
                Component component = (Component) i.next();
                if (Component.VEVENT.equals(component.getName())) {
                	DateTime start = new DateTime(component.getProperty(Property.DTSTART).getValue());
                	DateTime end = new DateTime(component.getProperty(Property.DTEND).getValue());
//                	Property category = component.getProperty(Property.CATEGORIES);
                	Property summary = component.getProperty(Property.SUMMARY);
                	Property description = component.getProperty(Property.DESCRIPTION);
//                	Property location = component.getProperty(Property.LOCATION);
                	Property uid = component.getProperty(Property.UID);
                	
                	// create a new Appointment
                	Appointment ap = new Appointment();
                	ap.setStartDate(start);
                	ap.setEndDate(end);
                	ap.setTypeId(appointmentTypeID);
                	ap.setSummary(summary.getValue());
                	ap.setDescription(description.getValue() + "\n"+Appointment.DESC_UID+"=" + uid.getValue());
                	ap.setCalendarId(calendarID);
                	ap.setCreated(new Date());
                	ap.setCreatedBy(null);

                	apps.add(ap);
                }
            }
        }
		
		return apps;
	}
	
	public static void main(String args[]) {
		try {
			Calendar cal = readICSFile(new File("/home/hermenj/gecamed.ics"));
			
			Collection<Appointment> apps = new IcalImportExporter(null, null).importAppointments(cal, 1 , 1);
			
			for (Appointment appointment : apps) {
				System.out.println("------------------");
				System.out.println(appointment);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
}
