#include <glib.h>
#include <string.h>

#include <libical/icalproperty.h>
#include <libical/icaltimezone.h>

#include "e-cal-glue-comp.h"
#include "e-cal-glue-recur.h"

/* 
   Following functions/defines are *SHAMELESSLY* copied from 
   e-d-s/calendar/libecal/e-cal-recur.c 
*/

/* The paramter we use to store the enddate in RRULE and EXRULE properties. */
#define EVOLUTION_END_DATE_PARAMETER	"X-EVOLUTION-ENDDATE"


/* If default_timezone is set, the saved ENDDATE parameter is assumed to be
   in that timezone. This is used when the DTSTART is a DATE or floating
   value, since the RRULE end date will change depending on the timezone that
   it is evaluated in. 
*/
static time_t
e_cal_recur_get_rule_end_date	(icalproperty	*prop,
				 icaltimezone	*default_timezone)
{
	icalparameter *param;
	const char *xname, *xvalue;
	icalvalue *value;
	struct icaltimetype icaltime;
	icaltimezone *zone;

	param = icalproperty_get_first_parameter (prop, ICAL_X_PARAMETER);
	while (param) {
		xname = icalparameter_get_xname (param);
		if (xname && !strcmp (xname, EVOLUTION_END_DATE_PARAMETER)) {
			xvalue = icalparameter_get_x (param);
			value = icalvalue_new_from_string (ICAL_DATETIME_VALUE,
							   xvalue);
			if (value) {
				icaltime = icalvalue_get_datetime (value);
				icalvalue_free (value);

				zone = default_timezone ? default_timezone : 
					icaltimezone_get_utc_timezone ();
				return icaltime_as_timet_with_zone (icaltime,
								    zone);
			}
		}

		param = icalproperty_get_next_parameter (prop,
							 ICAL_X_PARAMETER);
	}

	return -1;
}

static gint
e_cal_recur_ical_weekday_to_weekday     (enum icalrecurrencetype_weekday day)
{
  gint weekday;

  switch (day) {
  case ICAL_NO_WEEKDAY:           /* Monday is the default in RFC2445. */
  case ICAL_MONDAY_WEEKDAY:
    weekday = 0;
    break;
  case ICAL_TUESDAY_WEEKDAY:
    weekday = 1;
    break;
  case ICAL_WEDNESDAY_WEEKDAY:
    weekday = 2;
    break;
  case ICAL_THURSDAY_WEEKDAY:
    weekday = 3;
    break;
  case ICAL_FRIDAY_WEEKDAY:
    weekday = 4;
    break;
  case ICAL_SATURDAY_WEEKDAY:
    weekday = 5;
    break;
  case ICAL_SUNDAY_WEEKDAY:
    weekday = 6;
    break;
  default:
    g_warning ("e_cal_recur_ical_weekday_to_weekday(): Unknown week day %d",
	       day);
    weekday = 0;
  }
  return weekday;
}

/**
 * e_cal_recur_from_icalproperty:
 * @prop: An RRULE or EXRULE #icalproperty.
 * @exception: TRUE if this is an EXRULE rather than an RRULE.
 * @zone: The DTSTART timezone, used for converting the UNTIL property if it
 * is given as a DATE value.
 * @convert_end_date: TRUE if the saved end date needs to be converted to the
 * given @zone timezone. This is needed if the DTSTART is a DATE or floating
 * time.
 * 
 * Converts an #icalproperty to a #ECalRecurrence.  This should be
 * freed using the e_cal_recur_free() function.
 * 
 * Return value: #ECalRecurrence structure.
 **/
ECalGlueRecurrence *
e_cal_recur_from_icalproperty (icalproperty *prop, gboolean exception,
			       icaltimezone *zone, gboolean convert_end_date)
{
	struct icalrecurrencetype ir;
	ECalGlueRecurrence *r;

	g_return_val_if_fail (prop != NULL, NULL);

	r = g_new (ECalGlueRecurrence, 1);

	if (exception)
		ir = icalproperty_get_exrule (prop);
	else
		ir = icalproperty_get_rrule (prop);

        r->handle = prop;
	r->freq = ir.freq;
	r->interval = ir.interval;
	r->count = ir.count;

	if (ir.count != 0) {
		/* If COUNT is set, we use the pre-calculated enddate.
		   Note that this can be 0 if the RULE doesn't actually
		   generate COUNT instances. */
		r->until = e_cal_recur_get_rule_end_date (prop, convert_end_date ? zone : NULL);
	} else {
		if (icaltime_is_null_time (ir.until)) {
			/* If neither COUNT or UNTIL is set, the event
			   recurs forever. */
			r->until = 0;
		} else if (ir.until.is_date) {
			/* If UNTIL is a DATE, we stop at the end of
			   the day, in local time (with the DTSTART timezone).
			   Note that UNTIL is inclusive so we stop before
			   midnight. */
			ir.until.hour = 23;
			ir.until.minute = 59;
			ir.until.second = 59;
			ir.until.is_date = FALSE;

			r->until = icaltime_as_timet_with_zone (ir.until,
								  zone);
#if 0
			g_print ("  until: %li - %s", r->enddate, ctime (&r->enddate));
#endif

		} else {
			/* If UNTIL is a DATE-TIME, it must be in UTC. */
			icaltimezone *utc_zone;
			utc_zone = icaltimezone_get_utc_timezone ();
			r->until = icaltime_as_timet_with_zone (ir.until,
								  utc_zone);
		}
	}

	r->week_start_day = e_cal_recur_ical_weekday_to_weekday (ir.week_start);

	memcpy (r->by_second, ir.by_second, sizeof (ir.by_second));
	memcpy (r->by_minute, ir.by_minute, sizeof (ir.by_minute));
	memcpy (r->by_hour, ir.by_hour, sizeof (ir.by_hour));
	memcpy (r->by_day, ir.by_day, sizeof (ir.by_day));
	memcpy (r->by_month_day, ir.by_month_day, sizeof (ir.by_month_day));
	memcpy (r->by_year_day, ir.by_year_day, sizeof (ir.by_year_day));
	memcpy (r->by_week_no, ir.by_week_no, sizeof (ir.by_week_no));
	memcpy (r->by_month, ir.by_month, sizeof (ir.by_month));
	memcpy (r->by_set_pos, ir.by_set_pos, sizeof (ir.by_set_pos));

#if 0
	r->by_month = array_to_list (ir.by_month,
				    sizeof (ir.by_month) / sizeof (ir.by_month[0]));
	for (elem = r->by_month; elem; elem = elem->next) {
		/* We need to convert from 1-12 to 0-11, i.e. subtract 1. */
		int month = GPOINTER_TO_INT (elem->data) - 1;
		elem->data = GINT_TO_POINTER (month);
	}

	r->by_week_no = array_to_list (ir.by_week_no,
				     sizeof (ir.by_week_no) / sizeof (ir.by_week_no[0]));

	r->by_year_day = array_to_list (ir.by_year_day,
				      sizeof (ir.by_year_day) / sizeof (ir.by_year_day[0]));

	r->by_month_day = array_to_list (ir.by_month_day,
				       sizeof (ir.by_month_day) / sizeof (ir.by_month_day[0]));

	/* FIXME: libical only supports 8 values, out of possible 107 * 7. */
	r->by_day = NULL;
	max_elements = sizeof (ir.by_day) / sizeof (ir.by_day[0]);
	for (i = 0; i < max_elements && ir.by_day[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
		enum icalrecurrencetype_weekday day;
		gint weeknum, weekday;

		day = icalrecurrencetype_day_day_of_week (ir.by_day[i]);
		weeknum = icalrecurrencetype_day_position (ir.by_day[i]);

		weekday = e_cal_recur_ical_weekday_to_weekday (day);

		r->by_day = g_slist_prepend (r->by_day,
					   GINT_TO_POINTER (weeknum));
		r->by_day = g_slist_prepend (r->by_day,
					   GINT_TO_POINTER (weekday));
	}

	r->by_hour = array_to_list (ir.by_hour,
				   sizeof (ir.by_hour) / sizeof (ir.by_hour[0]));

	r->by_minute = array_to_list (ir.by_minute,
				     sizeof (ir.by_minute) / sizeof (ir.by_minute[0]));

	r->by_second = array_to_list (ir.by_second,
				     sizeof (ir.by_second) / sizeof (ir.by_second[0]));

	r->by_set_pos = array_to_list (ir.by_set_pos,
				     sizeof (ir.by_set_pos) / sizeof (ir.by_set_pos[0]));
#endif
	return r;
}


syntax highlighted by Code2HTML, v. 0.9.1