#include "calendar.h"

int  get_datetype(int day){return datetype[day][0];}

/*------------------------------------------------------
 *   checkicalversion
 -----------------------------------------------------*/
void checkicalversion()
{
    GtkWidget* dialog;
    GtkWidget* label;
    char* msg = "\n\nWARNING:\nIt is highly recommended to upgrade to libical 0.24!\nOtherwise wmCalendar will not work stable!\n";
    if(!strcmp(ICAL_VERSION, "0.23")){
	printf(msg);
     	dialog = gtk_dialog_new_with_buttons ("Warning",
					      NULL,
					      GTK_DIALOG_DESTROY_WITH_PARENT,
					      GTK_STOCK_OK,
					      GTK_RESPONSE_NONE,
					      NULL);
	label = gtk_label_new (msg);
	g_signal_connect_swapped (GTK_OBJECT (dialog),
				  "response",
				  G_CALLBACK (gtk_widget_destroy),
				  GTK_OBJECT (dialog));
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
	gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
                        GTK_SIGNAL_FUNC(gtk_main_quit ),
                        NULL);
 	gtk_widget_show_all (dialog);
	gtk_main();
    }
}


void move(GtkWidget *widget, GdkEventButton *event)
{
    xr = event->x;
    yr = event->y;
}



void move2(GtkWidget *widget, GdkEventMotion *event)
{
    gtk_widget_set_uposition((GtkWidget*)gtk_widget_get_toplevel(widget),event->x_root - xr , event->y_root - yr);
}



void kill (GtkWidget * widget)
{
    gtk_widget_destroy ((GtkWidget*)gtk_widget_get_toplevel(widget));
    gtk_main_quit ();
}



/*------------------------------------------------------
 *   calendar
 -----------------------------------------------------*/
void calendar(){
    int value;
    char* line;
    const char* text;
    const char* transp="";
    struct stat filestat;
    icalproperty *prop, *reocc;
    icalparser *parser;
    icalcomponent *c, *d;
    FILE *stream;
    struct icaltimetype t1, t2;
    if(get_debug())printf("check for new calendar data\n");
    stream = fopen((const char*)get_icsfile(),"r");
    if(stream == 0)
	return;
    fstat(fileno(stream), &filestat);
    if(filestat.st_mtime == modtime){
	fclose(stream);
	return;
    }
    if(get_debug())printf("read calendar data\n");
    deleteCalObjs();
    modtime = filestat.st_mtime;
    parser = icalparser_new();
    /* Tell the parser what input routie it should use. */
    icalparser_set_gen_data(parser, stream);
    do{
	line = icalparser_get_line(parser, read_stream);
	c = icalparser_add_line(parser, line);
	free(line);
	if(c != 0){
	    for(d = icalcomponent_get_first_component(c, ICAL_ANY_COMPONENT);d != 0;
		d = icalcomponent_get_next_component(c, ICAL_ANY_COMPONENT)){

		/* get date */
		t1 = icalcomponent_get_dtstart(d);
		t2 = icalcomponent_get_dtend(d);
		if(icaltime_is_null_time(t2)){
		    t2 = t1;
		    icaltime_adjust(&t2, 1,0,0,0);
		}

		/* get transparency */
		prop = icalcomponent_get_first_property(d, ICAL_TRANSP_PROPERTY );
		reocc = icalcomponent_get_first_property(d, ICAL_RRULE_PROPERTY);
		if(prop)
		    transp = icalproperty_get_value_as_string(prop);
		if(!strcmp(transp, "OPAQUE"))
		    value = 1;
		else if(!strcmp(transp, "TRANSPARENT"))
		    value = 2;
		else
		    value = 1 ;


		/* get desciption */
		prop = icalcomponent_get_first_property(d,     ICAL_SUMMARY_PROPERTY);
		if(prop) {
		    text = icalproperty_get_value_as_string(prop);
		    addCalObj(t1, t2, value, text, d);
		    if(get_debug())printf("read: %d.%d.%d - %d.%d.%d %s\n", t1.day, t1.month,
				    t1.year, t2.day, t2.month, t2.year, text);
	       }
		icalcomponent_free(d);
	    }
	    icalcomponent_free(c);
	}
    } while(line != 0);
    icalparser_free(parser);
    fclose(stream);
}



/*------------------------------------------------------
 *   showDay
 -----------------------------------------------------*/
void showDay(struct icaltimetype dt){
    static GtkWidget *dayView;
    static GtkWidget *table;
    static GtkWidget *label1;
    static GtkWidget *event_box;
    static GtkWidget *event_box2;
    char buf[28];
    char buf2[28];
    struct tm *timptr = NULL;
    time_t tt = icaltime_as_timet(dt);
    timptr = gmtime(&tt);
    event_box = gtk_event_box_new ();
    gtk_widget_show (event_box);
    event_box2 = gtk_event_box_new ();
    gtk_widget_show (event_box2);

    /* create titlebartext */
    strftime(buf, 26, "%A, %x", timptr);
    sprintf(buf2, "<b>%s</b>",buf);
    /* Create a new window with no boarder and day as titlebar*/
    dayView = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    // xr =yr =0;
    gtk_window_set_position(GTK_WINDOW (dayView),GTK_WIN_POS_MOUSE);
    gtk_window_set_policy(GTK_WINDOW (dayView), FALSE, FALSE, FALSE);
    gtk_window_set_decorated (GTK_WINDOW (dayView), FALSE);
    gtk_window_set_title(GTK_WINDOW (dayView), buf);
    /* create a table */
    table = gtk_table_new(1, 4, FALSE);
    gtk_table_set_row_spacings ((GtkTable*)table, 4);
    gtk_table_set_col_spacings ((GtkTable*)table, 15);
    gtk_table_set_col_spacing ((GtkTable*)table,2, 0);

    gtk_table_attach_defaults (GTK_TABLE(table), event_box, 0, 3, 1, 2);
    gtk_table_attach_defaults (GTK_TABLE(table), event_box2, 3, 4, 1, 2);

    label1 = gtk_label_new (NULL);
    gtk_label_set_markup ((GtkLabel*)label1, buf2);
    gtk_container_add (GTK_CONTAINER (event_box), label1);

    gtk_table_set_row_spacing ((GtkTable*)table,0, 1);
    gtk_table_set_row_spacing ((GtkTable*)table,1, 1);
    gtk_widget_show (label1);
    label1 = gtk_label_new (" X ");
    gtk_container_add (GTK_CONTAINER (event_box2), label1);
    gtk_widget_show (label1);

    /* fill table with events and draw window if there are any events */
    if(dayevents(dt, table)){
	gtk_widget_show (dayView);

	gtk_widget_show (table);
	gtk_container_add (GTK_CONTAINER (dayView), table);
	gtk_signal_connect(GTK_OBJECT (event_box), "motion_notify_event",
			   GTK_SIGNAL_FUNC (move2), NULL);
	gtk_signal_connect(GTK_OBJECT (event_box), "button_press_event",
			   GTK_SIGNAL_FUNC (move), NULL);
	gtk_signal_connect(GTK_OBJECT (event_box2), "button_press_event",
			   (GtkSignalFunc) kill, GTK_OBJECT(dayView));

	gtk_widget_realize(dayView);
	//	gtk_window_set_position(GTK_WINDOW (dayView),GTK_WIN_POS_CENTER_ON_PARENT);
	gtk_main ();
    }
}



/*------------------------------------------------------
 *   dayevents
 -----------------------------------------------------*/
int dayevents(struct icaltimetype dt, GtkWidget *table){
    static GtkWidget   *label1;
    struct calobj* it;
    struct icaltimetype t1, t2;
    icalproperty *prop;
    int j; /* tablerow */
    char buftime1[30];
    char buftime2[30];
    char buf[1024];
    struct tm *timptr = NULL;
    time_t tt;

    GtkWidget *separator;
    j = 2;
    it = calRoot;
    while(it){
	if(eventOnDay(dt, it)){
	    t1 = it->start;
	    t2 = it->end;
	    separator = gtk_hseparator_new ();
	    gtk_widget_show (separator);
	    gtk_table_attach_defaults (GTK_TABLE(table),separator,  0, 4, j, j+1);
	    j++;
	    if(daysEqual(t1, t2)){ /* single event */
		/* create time entry */
		tt = icaltime_as_timet(t1);
		timptr = gmtime(&tt);
		strftime(buftime1, 26, "%X", timptr);
		tt = icaltime_as_timet(t2);
		timptr = gmtime(&tt);
		strftime(buftime2, 26, "%X", timptr);
		sprintf(buf,"%s - %s",buftime1, buftime2);
		label1 = gtk_label_new (NULL);
		gtk_label_set_markup ((GtkLabel*)label1, buf);
		gtk_table_attach_defaults (GTK_TABLE(table), label1, 1, 2, j, j+1);
		gtk_widget_show (label1);


		/* create description entry */
		label1 = gtk_label_new (it->text);
		gtk_table_attach_defaults (GTK_TABLE(table), label1, 2, 3, j, j+1);
		gtk_widget_show (label1);
		j++;
	    }
	    else {
		/* multiday event or allday event*/
		t2.day--; /* endtime always on the first day after the event ... */
		if(t2.day == 0){
		    t2.month--;
		    if(t2.month == 0){
			t2.month = 12;
			t2.year--;
		    }
		    t2.day = icaltime_days_in_month(t2.month, t2.year);
		}
		if(t2.day < dt.day && t2.month == dt.month && t2.year == dt.year)
		    continue; /* event ended the day before ... */
		if(daysEqual(t1,t2)){
		    /* allday event */
		    /* create description entry */
		    sprintf(buf, "<i>all day event</i>");
		}
		else{ /* multiday event */
		    /* create description with start and enddate */
		    tt = icaltime_as_timet(t1);
		    timptr = gmtime(&tt);
		    strftime(buftime1, 26, "%a, %x", timptr);
		    tt = icaltime_as_timet(t2);
		    timptr = gmtime(&tt);
		    strftime(buftime2, 26, "%a, %x", timptr);
		    sprintf(buf, "%s - %s", buftime1, buftime2);
		}
		label1 = gtk_label_new (NULL);
		gtk_label_set_markup ((GtkLabel*)label1, buf);

		gtk_table_attach_defaults (GTK_TABLE(table), label1, 1, 2, j, j+1);
		gtk_widget_show (label1);

		prop = icalcomponent_get_first_property(it->comp, ICAL_LOCATION_PROPERTY);
		if(prop)
		    sprintf(buf, "%s\n%s", it->text, icalproperty_get_location(prop));
		else
		    sprintf(buf, "%s", it->text);
		label1 = gtk_label_new (buf);
		gtk_label_set_justify(GTK_LABEL(label1),GTK_JUSTIFY_CENTER );
		gtk_table_attach_defaults (GTK_TABLE(table), label1, 2, 3, j, j+1);
		gtk_widget_show (label1);
		j++;
	    }
	}
	it = it->next;
    }
    label1 = gtk_label_new (NULL);
    gtk_table_attach_defaults (GTK_TABLE(table), label1, 2, 3, j+1, j+2);
    gtk_table_set_row_spacing (GTK_TABLE(table),j, 0);
    if(j==2) /* no entries for this day */
	return FALSE;
    return TRUE;
}



/*------------------------------------------------------
 *   deleteCalObjs
 -----------------------------------------------------*/
void deleteCalObjs(){
    int i;
    struct calObj* help;
    while(calRoot){
	help = (struct calObj*) calRoot->next;
	free(calRoot->text);
	free(calRoot->comp);
	free(calRoot);
	calRoot = help;
    }
    for(i = 0; i < 32; i++)
	datetype[i][1] = 0;
}



/*------------------------------------------------------
 *  addCalObj
 -----------------------------------------------------*/
void addCalObj(struct icaltimetype start, struct icaltimetype end,
	       int type, const char *text, icalcomponent * d){
    struct calobj *newobj;
    icalcomponent *newcomp = malloc(sizeof(struct calobj));
    char* buf = malloc (strlen(text) + 1);
    newcomp =  icalcomponent_new_clone(d);
    strcpy(buf,text);
    newobj = malloc(sizeof(struct calobj));
    newobj->comp = newcomp;
    newobj->start = start;
    newobj->end = end;
    newobj->type = type;
    newobj->text = buf;
    newobj->next = calRoot;
    calRoot = newobj;
}



/*------------------------------------------------------
 *   getDayType
 -----------------------------------------------------*/
int  getDayType(struct icaltimetype dt){
    int jd;
    jd = civil_jdn(dt);
    if(datetype[jd % 31][1] == jd)
	return datetype[jd % 31][0];

    return calcDayType(dt);
}



/*------------------------------------------------------
 *   isExluded
 -----------------------------------------------------*/
int isExluded(icalcomponent *comp, struct icaltimetype dt){
    icalproperty *prop;
    prop = icalcomponent_get_first_property(comp, ICAL_EXDATE_PROPERTY);
    while(prop){
	if(daysEqual(icalproperty_get_exdate(prop), dt))
	    return 1;
	prop = icalcomponent_get_next_property(comp, ICAL_EXDATE_PROPERTY);
    }
    return 0;
}



/*------------------------------------------------------
 *   eventOnDay
 -----------------------------------------------------*/
int eventOnDay(struct icaltimetype dt, struct calobj* it)
{
    icalrecur_iterator* ritr;
    icalproperty *rrule;
    struct icaltimetype next;
    if((daysEarlierEqual(dt, it->start)	&& daysLater(dt, it->end))
       || (daysEqual(dt, it->start) && daysEqual(dt, it->end)))
	return 1;
    rrule = icalcomponent_get_first_property((icalcomponent*)it->comp, ICAL_RRULE_PROPERTY);

    if(rrule){
	if(daysEarlierEqual(dt, it->start)){
	    ritr = icalrecur_iterator_new( icalproperty_get_rrule(rrule), it->start);
	    if(ritr)
		next = icalrecur_iterator_next(ritr);
	    while(daysEarlierEqual(dt, next) && !icaltime_is_null_time(next)){
		if(daysEqual(dt, next) && !isExluded((icalcomponent*)it->comp, dt))
		    {
			free(ritr);
			return 1;
		    }
		next = icalrecur_iterator_next(ritr);
	    }
	    free(ritr);
	}
    }
    return 0;
}



/*------------------------------------------------------
 *   calcDayType
 -----------------------------------------------------*/
int calcDayType(struct icaltimetype dt){
    int jd;
    struct calobj* it;
    jd = civil_jdn(dt);
    datetype[jd % 31][1] = jd;
    datetype[jd % 31][0] = 0;
    it = calRoot;
    while(it){
	if(eventOnDay(dt, it))
	    if(datetype[jd % 31][0] != 1)
		datetype[jd % 31][0] = it->type;
	it = it->next;
    }
    return datetype[jd % 31][0];
}