/* -*- mode: c++; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
 * vim: ts=4 sw=4 noet ai cindent syntax=cpp
 *
 * Conky, a system monitor, based on torsmo
 *
 * Please see COPYING for details
 *
 * Copyright (c) 2005-2012 Brenden Matthews, Philip Kovacs, et. al.
 *	(see AUTHORS)
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <libical/ical.h>
#include "conky.h"
#include "logging.h"

struct ical_event {
	icaltimetype start;
	icalcomponent *event;
	ical_event *next, *prev;
};

struct obj_ical {
	struct ical_event *list;
	icalcomponent *comps;
	icalparser *parser;
	unsigned int num;
};

char* read_stream(char *s, size_t size, void *d) {
	return fgets(s, size, (FILE*) d);
}

struct ical_event *add_event(struct ical_event *listend, icalcomponent *new_ev) {
	struct ical_event *ev_new, *ev_cur;
	icaltimetype start;

	start = icalcomponent_get_dtstart(new_ev);
	if(icaltime_compare(start, icaltime_from_timet(time(NULL), 0)) <= 0) {
		icalproperty *rrule = icalcomponent_get_first_property(new_ev, ICAL_RRULE_PROPERTY);
		if(rrule) {
			icalrecur_iterator* ritr = icalrecur_iterator_new(icalproperty_get_rrule(rrule), start);
			icaltimetype nexttime = icalrecur_iterator_next(ritr);
			while (!icaltime_is_null_time(nexttime)) {
				if(icaltime_compare(nexttime, icaltime_from_timet(time(NULL), 0)) > 0) {
					start = nexttime;
					break;
				}
				nexttime = icalrecur_iterator_next(ritr);
			}
			icalrecur_iterator_free(ritr);
		} else return NULL;
	}
	ev_new = (struct ical_event *) malloc(sizeof(struct ical_event));
	memset(ev_new, 0, sizeof(struct ical_event));
	ev_new->event = new_ev;
	ev_new->start = start;
	if(listend) {	//list already contains events
		ev_cur = listend;
		while(icaltime_compare(ev_new->start, ev_cur->start) <= 0) {
			if( ! ev_cur->prev) {	//ev_new starts first
				ev_new->next = ev_cur;
				ev_cur->prev = ev_new;
				return listend;
			}
			ev_cur = ev_cur->prev;
		}
		if(ev_cur == listend) {	//ev_new starts last
			ev_cur->next = ev_new;
			ev_new->prev = ev_cur;
			return ev_new;
		}
		//ev_new somewhere in the middle
		ev_new->prev = ev_cur;
		ev_new->next = ev_cur->next;
		ev_cur->next->prev = ev_new;
		ev_cur->next = ev_new;
		return listend;
	}
	return ev_new;
}

void parse_ical_args(struct text_object *obj, const char* arg, void *free_at_crash, void *free_at_crash2) {
	char *filename = strdup(arg);
	FILE *file;
	icalparser *parser;
	icalcomponent *allc, *curc;
	struct ical_event *ll_start, *ll_end, *ll_new;
	struct obj_ical *opaque;
	unsigned int num;

	if(sscanf(arg , "%d %s", &num, filename) != 2) {
		free(filename);
		free(obj);
		CRIT_ERR(free_at_crash, free_at_crash2, "wrong number of arguments for $ical");
	}
	file = fopen(filename, "r");
	if( ! file) {
		free(obj);
		free(free_at_crash);
		CRIT_ERR(filename, free_at_crash2, "Can't read file %s", filename);
		return;
	}
	free(filename);
	parser = icalparser_new();
	icalparser_set_gen_data(parser, file);
	allc = icalparser_parse(parser, read_stream);
	fclose(file);
	curc = icalcomponent_get_first_component(allc, ICAL_VEVENT_COMPONENT);
	if(!curc) {
		icalparser_free(parser);
		icalcomponent_free(allc);
		NORM_ERR("No ical events available");
		return;
	}
	ll_start = add_event(NULL, curc);
	ll_end = ll_start;
	while(1) {
		curc = icalcomponent_get_next_component(allc, ICAL_VEVENT_COMPONENT);
		if(!curc) break;
		ll_new = add_event(ll_end, curc);
		if( ! ll_start) {	//first component was not added
			ll_start = ll_new;
			ll_end = ll_new;
		}else if( ll_start->prev ) {
			ll_start = ll_start->prev;
		}else if( ll_end->next ) {
			ll_end = ll_end->next;
		}
	}
	opaque = (struct obj_ical *) malloc(sizeof(struct obj_ical));
	opaque->list = ll_start;
	opaque->parser = parser;
	opaque->comps = allc;
	opaque->num = num;
	obj->data.opaque = opaque;
}

void print_ical(struct text_object *obj, char *p, int p_max_size) {
	struct obj_ical *ical_obj = (struct obj_ical *) obj->data.opaque;
	struct ical_event *ll_current;

	if( ! ical_obj) return;
	ll_current = ical_obj->list;
	unsigned int i=1;
	while(1) {
		if( ! ll_current) return;
		if(i > ical_obj->num) return;
		if(i == ical_obj->num) break;
		if(i < ical_obj->num) {
			ll_current = ll_current->next;
			i++;
		}
	}
	snprintf(p, p_max_size, "%s", icalproperty_get_summary(icalcomponent_get_first_property(ll_current->event, ICAL_SUMMARY_PROPERTY)));
}

void free_ical(struct text_object *obj) {
	struct obj_ical *ical_free_me = (struct obj_ical *) obj->data.opaque;

	if( ! ical_free_me) return;
	icalcomponent_free(ical_free_me->comps);
	icalparser_free(ical_free_me->parser);
	while(ical_free_me->list) {
		if(ical_free_me->list->next) {
			ical_free_me->list = ical_free_me->list->next;
			free_and_zero(ical_free_me->list->prev);
		} else free_and_zero(ical_free_me->list);
	}
	free(obj->data.opaque);
}
