/*
 * Osmo - a handy personal organizer
 *
 * Copyright (C) 2007 Tomasz Maka <pasp@users.sourceforge.net>
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "check_events.h"
#include "i18n.h"
#include "calendar.h"
#include "gui.h"
#include "utils.h"
#include "utils_gui.h"
#include "calendar_notes.h"
#include "options_prefs.h"
#include "calendar_utils.h"
#include "stock_icons.h"
#include "tasks_items.h"
#include "tasks_utils.h"

/*------------------------------------------------------------------------------*/

#ifdef TASKS_ENABLED

/*------------------------------------------------------------------------------*/

static void
tsk_status_icon_set_normal(GUI *appGUI) {
    gtk_status_icon_set_from_icon_name(appGUI->osmo_trayicon, OSMO_STOCK_SYSTRAY_NORMAL);
}

static void
tsk_status_icon_set_task(GUI *appGUI) {
    if (gtk_status_icon_get_visible(appGUI->osmo_trayicon)) {
#ifdef HAVE_LIBNOTIFY
#ifndef HAVE_LIBNOTIFY7
        notify_notification_attach_to_status_icon(a->notify, appGUI->osmo_trayicon);
#endif /* HAVE_LIBNOTIFY7 */
#endif /* HAVE_LIBNOTIFY */
        gtk_status_icon_set_from_icon_name(appGUI->osmo_trayicon, OSMO_STOCK_SYSTRAY_TASK);
    }
}
/*------------------------------------------------------------------------------*/
#ifdef HAVE_LIBNOTIFY
static TASK_NTF *
get_task_notification(guint id, GUI *appGUI) {
    GSList *node;
    for (node = appGUI->tsk->notifications; node != NULL; node = node->next) {
        TASK_NTF *notification = (TASK_NTF *) node->data;
        if (notification->id == id) {
            return notification;
        }
    }
    return NULL;
}
#endif /* HAVE_LIBNOTIFY */
/*------------------------------------------------------------------------------*/

static gboolean
tsk_check_notification_id(guint id, gint type, GUI *appGUI) {
#ifdef HAVE_LIBNOTIFY
    TASK_NTF *a = get_task_notification(id, appGUI);
    /* Don't show warning notification when alarm notification is visible */
    if (a != NULL && (a->type == NOTIFY_ALARM || a->type == type)) {
        return TRUE;
    }
#endif /* HAVE_LIBNOTIFY */
    return FALSE;
}

/*------------------------------------------------------------------------------*/

#ifdef HAVE_LIBNOTIFY

void
free_notifications_list (GUI *appGUI)
{
	GSList *node;
	TASK_NTF *a;

	for (node = appGUI->tsk->notifications; node != NULL; node = node->next) {
		a = (TASK_NTF *) node->data;
		notify_notification_close (a->notify, NULL);
		g_free (a);
	}

	if (appGUI->tsk->notifications != NULL) {
		g_slist_free (appGUI->tsk->notifications);
		appGUI->tsk->notifications = NULL;
	}
}

/*------------------------------------------------------------------------------*/

static void
tsk_show_info_dialog (GUI *appGUI)
{
gchar tmpbuf[BUFFER_SIZE];

	g_snprintf (tmpbuf, BUFFER_SIZE, "%s\n%s", _("Cannot perform selected operation."),
	            _("Task has been modified or removed."));
	utl_gui_create_dialog (GTK_MESSAGE_INFO, tmpbuf, GTK_WINDOW (appGUI->main_window));
}
/*------------------------------------------------------------------------------*/
static TASK_NTF *
get_task_notification_by_notification(NotifyNotification *n, GUI *appGUI) {
#ifdef HAVE_LIBNOTIFY
    GSList *node;
    for (node = appGUI->tsk->notifications; node != NULL; node = node->next) {
        TASK_NTF *notification = (TASK_NTF *) node->data;
        if (notification->notify == n) {
            return notification;
        }
    }
#endif /* HAVE_LIBNOTIFY */
    return NULL;
}

/*------------------------------------------------------------------------------*/

static void
tsk_done_cb(NotifyNotification *n, const char *action, GUI *appGUI) {
    TASK_NTF *a = get_task_notification_by_notification(n, appGUI);

    if (a != NULL) {
        GtkTreeIter *iter = tsk_get_iter(a->id, appGUI);

        if (iter != NULL) {
            TASK_ITEM *t = tsk_get_item(iter, appGUI);

            if (t->repeat == TRUE) {
                // delete the notification so it can be recreated by the next due date
                delete_task_notification(a->id, appGUI);
                tasks_repeat_done(iter, t, appGUI);
            } else {
                // notification will be deleted via a callback
                tasks_done(iter, t, appGUI);
            }

            tsk_item_free(t);
        } else {
            tsk_show_info_dialog(appGUI);
        }

        tsk_status_icon_set_normal(appGUI);
    }
}

/*------------------------------------------------------------------------------*/

static void
tsk_postpone_notify_cb(NotifyNotification *n, const char *action, GUI *appGUI) {
    TASK_NTF *a = get_task_notification_by_notification(n, appGUI);

    if (a != NULL) {
        TASK_ITEM *t = tsk_get_item_id(a->id, appGUI);
        g_return_if_fail(t != NULL);

        a->time = utl_time_get_current_seconds() + t->postpone_time * 60;
        a->date = utl_date_get_current_julian();

        if (a->time >= 24 * 3600) {
            a->time -= 24 * 3600;
            a->date++;
        }
        a->notify = NULL;

        tsk_item_free(t);
        tsk_status_icon_set_normal(appGUI);
    }
}

/*------------------------------------------------------------------------------*/

static void
tsk_show_task_cb (NotifyNotification *n, const char *action, GUI *appGUI)
{
	GtkTreeIter iter;
	GtkTreePath *sort_path, *filter_path, *path;
	GtkTreeModel *model;
	TASK_NTF *a;
	TASK_ITEM *t;
	guint id;

	model = GTK_TREE_MODEL (appGUI->tsk->tasks_list_store);

    if (config.enable_systray == TRUE && appGUI->window_visible == FALSE) {
		gtk_widget_show (appGUI->main_window);
		appGUI->window_visible = TRUE;
	} else {
		gtk_window_deiconify (GTK_WINDOW (appGUI->main_window));
	}

	/* select task tab */
	if (config.hide_tasks == FALSE) {
		gtk_notebook_set_current_page (GTK_NOTEBOOK (appGUI->notebook), PAGE_TASKS);
	}

	gtk_combo_box_set_active (GTK_COMBO_BOX (appGUI->tsk->cf_combobox), 0);

        a = get_task_notification_by_notification(n, appGUI);
		if (a != NULL) {
	/* select task on list */
			tasks_selection_activate (FALSE, appGUI);

			if (gtk_tree_model_get_iter_first (model, &iter) == TRUE) {
				sort_path = gtk_tree_model_get_path (model, &iter);

				while (sort_path != NULL) {
					gtk_tree_view_set_cursor (GTK_TREE_VIEW (appGUI->tsk->tasks_list), sort_path, NULL, FALSE);
					filter_path = gtk_tree_model_sort_convert_path_to_child_path (GTK_TREE_MODEL_SORT (appGUI->tsk->tasks_sort), sort_path);

					if (filter_path != NULL) {
						path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (appGUI->tsk->tasks_filter), filter_path);

						if (path != NULL) {
							gtk_tree_model_get_iter (model, &iter, path);
							gtk_tree_model_get (model, &iter, TA_COLUMN_ID, &id, -1);

							if (a->id == id) {
								tasks_selection_activate (TRUE, appGUI);
								gtk_tree_view_set_cursor (GTK_TREE_VIEW (appGUI->tsk->tasks_list), sort_path, NULL, FALSE);
								gtk_tree_path_free (path);
								gtk_tree_path_free (filter_path);
								g_signal_emit_by_name (G_OBJECT (appGUI->tsk->tasks_list_selection), "changed");
								break;
							}

							gtk_tree_path_free (path);
						}

						gtk_tree_path_free (filter_path);
					}

					gtk_tree_path_next (sort_path);
					if (gtk_tree_model_get_iter (model, &iter, sort_path) == FALSE) break;
				}

				gtk_tree_path_free (sort_path);
			}

			tsk_status_icon_set_normal (appGUI);

			if (a->type == NOTIFY_WARNING)
				return;

			notify_notification_clear_actions (a->notify);

			t = tsk_get_item_id (a->id, appGUI);
			g_return_if_fail (t != NULL);

			if (t->postpone_time > 0) {
				notify_notification_add_action (a->notify, "postpone", _("Remind me later"),
												(NotifyActionCallback) tsk_postpone_notify_cb, appGUI, NULL);
			}
			notify_notification_add_action (a->notify, "done", _("Done"), (NotifyActionCallback) tsk_done_cb, appGUI, NULL);
			notify_notification_show (a->notify, NULL);

			tsk_item_free (t);
		}
}
#endif /* HAVE_LIBNOTIFY */

/*------------------------------------------------------------------------------*/
static gboolean
is_past_task_warning_time(TASK_ITEM *item, GUI *appGUI) {
    if (item->warning_days > 0 || item->warning_time > 0) {
        guint32 current_date, warning_date;
        gint current_time, warning_time;
        current_date = utl_date_get_current_julian();
        current_time = utl_time_get_current_seconds();
        utl_subtract_from_date(item->due_date_julian, item->due_time,
                item->warning_days, item->warning_time * 60, &warning_date, &warning_time);

        if (warning_date < current_date + (warning_time <= current_time) ? 1 : 0) {
            if (item->repeat == TRUE && item->offline_ignore == TRUE) {
                if (warning_date < appGUI->run_date + (warning_time < appGUI->run_time) ? 1 : 0) {
                    return FALSE;
                }
            }

            return TRUE;
        }
    }
    return FALSE;
}

static gboolean
is_past_task_due_time(TASK_ITEM *item) {
    return utl_date_time_in_the_past_js (item->due_date_julian, item->due_time);
}
/*------------------------------------------------------------------------------*/

static gboolean
is_show_warning_notification(TASK_ITEM *item, GUI *appGUI) {
    if (tsk_check_notification_id(item->id, NOTIFY_WARNING, appGUI)) {
        return FALSE;
    } else if (item->done) {
        return FALSE;
    } else {
        return is_past_task_warning_time(item, appGUI);
    }
}
/*------------------------------------------------------------------------------*/

static gboolean
is_show_task_notification(TASK_ITEM *item, GUI *appGUI) {
    if (!item->active) {
        return FALSE;
    } else if (item->done) {
        return FALSE;
    } else if (item->due_date_julian == 0 || tsk_check_notification_id(item->id, NOTIFY_ALARM, appGUI)) {
        return FALSE;
    }
    return is_past_task_due_time(item);
}

/*------------------------------------------------------------------------------*/

static gboolean
is_delete_task_notification(TASK_ITEM *item, GUI *appGUI) {
    if(item->done) {
        return TRUE;
    } else if(!is_past_task_due_time(item) && !is_past_task_warning_time(item, appGUI)) {
        return TRUE;
    }
    return FALSE;
}

/*------------------------------------------------------------------------------*/
#ifdef HAVE_LIBNOTIFY

static gboolean
is_show_postponed_notification(TASK_NTF *notification) {
    guint32 current_date;
    gint current_time;

    if (notification->type == NOTIFY_WARNING) {
        return FALSE;
    }
    current_date = utl_date_get_current_julian();
    current_time = utl_time_get_current_seconds();

    if ((g_date_valid_julian(notification->date) && utl_time_valid_seconds(notification->time)) == FALSE) {
        return FALSE;
    }
    if (utl_date_time_compare_js(notification->date, notification->time, current_date, current_time) <= 0) {
        return TRUE;
    }
    return FALSE;
}

static NotifyNotification *
create_notify_notification(TASK_ITEM *item, const char *summary, const char *body, GUI *appGUI) {
    NotifyNotification *notification;
#ifdef HAVE_LIBNOTIFY7
    notification = notify_notification_new(summary, body, "dialog-warning");
#else
    notification = notify_notification_new(summary, body, "dialog-warning", NULL);
#endif /* HAVE_LIBNOTIFY7 */
    notify_notification_set_timeout(notification, NOTIFY_EXPIRES_NEVER);
    switch (tsk_get_priority_index(item->priority)) {
        case LOW_PRIORITY: notify_notification_set_urgency(notification, NOTIFY_URGENCY_LOW);
            break;
        case MEDIUM_PRIORITY: notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL);
            break;
        case HIGH_PRIORITY: notify_notification_set_urgency(notification, NOTIFY_URGENCY_CRITICAL);
            break;
    }

    if (item->postpone_time > 0) {
        notify_notification_add_action(notification, "postpone", _("Remind me later"),
                (NotifyActionCallback) tsk_postpone_notify_cb, appGUI, NULL);
    }

    if (tsk_get_category_state(item->category, STATE_TASKS, appGUI) == TRUE) {
        notify_notification_add_action(notification, "show_task", _("Show task"),
                (NotifyActionCallback) tsk_show_task_cb, appGUI, NULL);
    }

    notify_notification_add_action(notification, "done", _("Done"),
            (NotifyActionCallback) tsk_done_cb, appGUI, NULL);

    return notification;
}

static TASK_NTF *
create_task_notification(TASK_ITEM *item, GUI *appGUI) {
    TASK_NTF * notification = g_malloc(sizeof (TASK_NTF));
    gchar *date, *date_formatted;

    notification->id = item->id;
    notification->type = NOTIFY_ALARM;
    notification->time = -1;
    notification->date = 0;

    date = utl_date_time_print_default(item->due_date_julian, item->due_time, FALSE);
    date_formatted = g_strdup_printf("<i>%s</i>", date);

    if (item->desc != NULL && strlen(item->desc)) {
        gchar *text = g_strdup_printf("%s\n%.100s", date_formatted, item->desc);
        notification->notify = create_notify_notification(item, item->summary, text, appGUI);
        g_free(text);
    } else {
        notification->notify = create_notify_notification(item, item->summary, date_formatted, appGUI);
    }

    g_free(date);
    g_free(date_formatted);
    return notification;
}

static TASK_NTF *
create_task_warning(TASK_ITEM *item, GUI *appGUI) {
    TASK_NTF * notification = g_malloc(sizeof (TASK_NTF));
    gchar *date, *summary;

    notification->id = item->id;
    notification->type = NOTIFY_WARNING;
    notification->time = -1;
    notification->date = 0;

    date = utl_date_time_print_default(item->due_date_julian, item->due_time, FALSE);
    summary = g_strdup_printf("<b>%s</b>\n<i>%s</i>", item->summary, date);

    if (item->desc != NULL && strlen(item->desc)) {
        gchar *text = g_strdup_printf("%s\n%.100s", summary, item->desc);
#ifdef HAVE_LIBNOTIFY7
        notification->notify = notify_notification_new(_("Alarm warning!"), text, "dialog-information");
#else
        notification->notify = notify_notification_new(_("Alarm warning!"), textdesc, "dialog-information", NULL);
#endif /* HAVE_LIBNOTIFY7 */
        g_free(text);
    } else {
#ifdef HAVE_LIBNOTIFY7
        notification->notify = notify_notification_new(_("Alarm warning!"), summary, "dialog-information");
#else
        notification->notify = notify_notification_new(_("Alarm warning!"), text, "dialog-information", NULL);
#endif /* HAVE_LIBNOTIFY7 */
    }

    notify_notification_set_timeout(notification->notify, NOTIFY_EXPIRES_NEVER);
    notify_notification_set_urgency(notification->notify, NOTIFY_URGENCY_NORMAL);
    if (tsk_get_category_state(item->category, STATE_TASKS, appGUI) == TRUE)
        notify_notification_add_action(notification->notify, "show_task", _("Show task"),
            (NotifyActionCallback) tsk_show_task_cb, appGUI, NULL);

    g_free(date);
    g_free(summary);
    return notification;
}

static void
update_postponed_notification(TASK_NTF *a, TASK_ITEM *item, GUI *appGUI) {
    gchar *date, *date_formatted, *title;

    a->date = 0;
    a->time = -1;

    date = utl_date_time_print_default(item->due_date_julian, item->due_time, FALSE);
    date_formatted = g_strdup_printf("<i>%s</i>", date);
    title = g_strdup_printf("%s (%s)", item->summary, _("postponed"));

    if (item->desc != NULL && strlen(item->desc)) {
        gchar *text = g_strdup_printf("%s\n%.100s", date_formatted, item->desc);
        a->notify = create_notify_notification(item, title, text, appGUI);
        g_free(text);
    } else {
        a->notify = create_notify_notification(item, title, date_formatted, appGUI);
    }

    g_free(title);
    g_free(date);
    g_free(date_formatted);
}
#endif /* HAVE_LIBNOTIFY */

/*------------------------------------------------------------------------------*/
static void
warn_task(TASK_ITEM *item, gboolean play_sound, GUI *appGUI) {
    tsk_status_icon_set_task(appGUI);

    if (item->sound_enable && play_sound) {
        utl_play_alarm_sound(config.sound_alarm_repeat);
    }
}

static void
notify_task(TASK_ITEM *item, gboolean play_sound, GUI *appGUI) {
    if (item->alarm_command != NULL) {
        if (strlen(item->alarm_command)) {
            gui_save_data_and_run_command(item->alarm_command, appGUI);
        }
    }
    if (strlen(config.global_notification_command)) {
        gui_save_data_and_run_command(config.global_notification_command, appGUI);
    }
    warn_task(item, play_sound, appGUI);
}

#ifdef HAVE_LIBNOTIFY
static void
show_task_notification(TASK_NTF *notification, gboolean notification_enabled, gboolean new_notification, GUI *appGUI) {
    if (new_notification) {
        /* Delete alarm warning */
        delete_task_notification(notification->id, appGUI);
    }
    if (notification_enabled) {
        GError *err = NULL;
        if (!notify_notification_show(notification->notify, &err)) {
            g_warning("Failed to send notification: %s", err->message);
            g_error_free (err);
            return;
        }
    }
    if (new_notification) {
        appGUI->tsk->notifications = g_slist_prepend(appGUI->tsk->notifications, notification);
    }
}
#endif /* HAVE_LIBNOTIFY */

/*------------------------------------------------------------------------------*/

static gboolean
check_task_notification(TASK_ITEM *item, gboolean play_sound, GUI *appGUI) {
#ifdef HAVE_LIBNOTIFY
    TASK_NTF *notification;
#endif /* HAVE_LIBNOTIFY */
    if (!appGUI->tsk->notifications_enable) {
        return FALSE;
    }

    if (item == NULL) {
        return FALSE;
    }

#ifdef HAVE_LIBNOTIFY
    if (get_task_notification(item->id, appGUI) == NULL) {
#endif /* HAVE_LIBNOTIFY */
        if (is_show_task_notification(item, appGUI)) {
            notify_task(item, play_sound, appGUI);
#ifdef HAVE_LIBNOTIFY
            notification = create_task_notification(item, appGUI);
            show_task_notification(notification, item->ndialog_enable, TRUE, appGUI);
#endif /* HAVE_LIBNOTIFY */
            return TRUE;
        } else if (is_show_warning_notification(item, appGUI)) {
            warn_task(item, play_sound, appGUI);
#ifdef HAVE_LIBNOTIFY
            notification = create_task_warning(item, appGUI);
            show_task_notification(notification, item->ndialog_enable, TRUE, appGUI);
#endif /* HAVE_LIBNOTIFY */
            return TRUE;
        }
#ifdef HAVE_LIBNOTIFY
    } else {
        if (is_delete_task_notification(item, appGUI)) {
            delete_task_notification(item->id, appGUI);
        } else {
            notification = get_task_notification(item->id, appGUI);
            if (is_show_postponed_notification(notification)) {
                notify_task(item, play_sound, appGUI);
                update_postponed_notification(notification, item, appGUI);
                show_task_notification(notification, item->ndialog_enable, FALSE, appGUI);
                return TRUE;
            }
        }
    }
#endif /* HAVE_LIBNOTIFY */
    return FALSE;
}

/*------------------------------------------------------------------------------*/
void
change_task_notification(TASK_ITEM *item, GUI *appGUI) {
    check_task_notification(item, TRUE, appGUI);
}

/*------------------------------------------------------------------------------*/

static void
notify_tasks(GUI *appGUI) {
    GtkTreeIter iter;
    TASK_ITEM *item;
    gboolean play_sound = TRUE, has_next;

    if (appGUI->tsk->notifications_enable == FALSE) {
        return;
    }

    has_next = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter);
    while (has_next) {
        gboolean task_notified;
        item = tsk_get_item(&iter, appGUI);
        task_notified = check_task_notification(item, play_sound, appGUI);
        play_sound = play_sound && !task_notified;
        tsk_item_free(item);
        has_next=gtk_tree_model_iter_next(GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter);
    }
}

/*------------------------------------------------------------------------------*/

static void
ignore_offline_tasks(GUI *appGUI) {
    GtkTreeIter iter;
    TASK_ITEM *item;
    guint32 now_date = utl_date_get_current_julian ();
    gint now_time = utl_time_get_current_seconds ();
    gboolean valid;

    valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter);
    while (valid) {
        item = tsk_get_item(&iter, appGUI);
        if (item->repeat == TRUE && item->offline_ignore == TRUE) {
            if (utl_date_time_compare_js(item->due_date_julian, item->due_time, now_date, now_time) < 0) {
                tasks_repeat_done(&iter, item, appGUI);
            }
        }
        tsk_item_free(item);
        valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter);
    }
}

/*------------------------------------------------------------------------------*/

void
delete_task_notification(guint id, GUI *appGUI) {
#ifdef HAVE_LIBNOTIFY
    TASK_NTF *a = get_task_notification(id, appGUI);
    if (a != NULL) {
        if (a->notify) {
            GError *err = NULL;
            if (!notify_notification_close(a->notify, &err)) {
                g_warning("Failed to close notification: %s", err->message);
                g_error_free (err);
            }
        }
        appGUI->tsk->notifications = g_slist_remove(appGUI->tsk->notifications, a);
        g_free(a);
    }
#endif /* HAVE_LIBNOTIFY */
}

#endif  /* TASKS_ENABLED */

/*------------------------------------------------------------------------------*/
static void
refresh_calendar(GUI *appGUI) {
    if (appGUI->current_tab == PAGE_CALENDAR) {
        /* redraw calendar to move the Today marker */
        gtk_widget_queue_draw(appGUI->cal->calendar);
    }
}
/*------------------------------------------------------------------------------*/

gboolean
time_handler (gpointer data)
{
static gint32 minute = -1;
static guint32 last_refresh_date = 0;
GUI *appGUI = data;
gint32 current_minute;
guint32 current_date;

	if (config.di_show_current_time_seconds == TRUE) {
		update_clock (appGUI);
	}

    current_minute = get_absolute_minute ();
    current_date = utl_date_get_current_julian ();

	if (minute != current_minute) {

#ifdef TASKS_ENABLED
        if (current_minute - minute > 1) {
            // the program just started or resumed from suspend
            ignore_offline_tasks(appGUI);
        }
        notify_tasks(appGUI);
#endif  /* TASKS_ENABLED */

		minute = current_minute;

		if (config.di_show_current_time_seconds == FALSE) {
			update_clock (appGUI);
		}
		gui_systray_tooltip_update (appGUI);
	}

	if (current_date != last_refresh_date) {
#ifdef TASKS_ENABLED
		refresh_tasks (appGUI);
#endif  /* TASKS_ENABLED */
                refresh_calendar(appGUI);

		/* update systray status */
		gui_systray_update_icon (appGUI);

		last_refresh_date = current_date;
	}

	return TRUE;
}

/*------------------------------------------------------------------------------*/

gboolean
check_tasks_contacts (guint32 julian_day, GUI *appGUI) {

    if (appGUI->calendar_only == TRUE || appGUI->all_pages_added == FALSE) {
        return FALSE;
    }

#ifdef CONTACTS_ENABLED
    /* check contacts */
    if (check_contacts (julian_day, appGUI) == TRUE) {
        return TRUE;
    }
#endif  /* CONTACTS_ENABLED */

#ifdef TASKS_ENABLED
    /* check tasks */
    return tsk_check_tasks (julian_day, julian_day, STATE_CALENDAR, appGUI);
#else
    return FALSE;
#endif  /* TASKS_ENABLED */

}

/*------------------------------------------------------------------------------*/

#ifdef CONTACTS_ENABLED

gboolean
check_contacts (guint32 julian_day, GUI *appGUI)
{
	GtkTreeModel *model;
	GtkTreePath *path;
	GtkTreeIter iter;
	GDate *date = NULL;
	guint32 julian;
	gint age, year, flag;

	model = GTK_TREE_MODEL (appGUI->cnt->contacts_list_store);
	g_return_val_if_fail (model != NULL, FALSE);

	date = g_date_new ();
	g_return_val_if_fail (date != NULL, FALSE);

	year = julian_to_year (julian_day);
	flag = FALSE;
	path = gtk_tree_path_new_first ();

	while (gtk_tree_model_get_iter (model, &iter, path) == TRUE) {
		gtk_tree_model_get (model, &iter, COLUMN_BIRTH_DAY_DATE, &julian, -1);

		if (g_date_valid_julian (julian) == TRUE) {

			g_date_set_julian (date, julian);
			age = year - g_date_get_year (date);

			if (age >= 0) {
				if (g_date_valid_dmy (g_date_get_day (date), g_date_get_month (date), year) == FALSE) {
					g_date_subtract_days (date, 1);
				}

				g_date_set_year (date, year);
				julian = g_date_get_julian (date);

				if (julian_day == julian) {
					flag = TRUE;
					break;
				}
			}

		}

		gtk_tree_path_next (path);
	}

	gtk_tree_path_free (path);
	g_date_free (date);

	return flag;
}

#endif  /* CONTACTS_ENABLED */

/*------------------------------------------------------------------------------*/

void
button_event_checker_window_delete_cb (GtkButton *button, gpointer user_data) {}

void
button_event_checker_window_close_cb (GtkButton *button, gpointer user_data) {

    GUI *appGUI = (GUI *)user_data;

    gtk_widget_destroy (appGUI->event_checker_window);
    gui_quit_osmo (appGUI);
}

/*------------------------------------------------------------------------------*/

void
button_event_checker_show_osmo_window_cb (GtkButton *button, gpointer user_data) {

    GUI *appGUI = (GUI *)user_data;

    appGUI->check_events = FALSE;
    gtk_widget_destroy (appGUI->event_checker_window);

    gui_systray_initialize (appGUI);
    gtk_widget_show (appGUI->main_window);
    gtk_window_move (GTK_WINDOW (appGUI->main_window), config.window_x, config.window_y);
}

/*------------------------------------------------------------------------------*/

void
event_checker_select_item (GUI *appGUI)
{
GtkTreeIter iter;
GtkTreeModel *model;
guint32 julian_day;
GDate *date;

	date = g_date_new ();
	g_return_if_fail (date != NULL);

	if (gtk_tree_selection_get_selected (appGUI->event_checker_list_selection, &model, &iter)) {
		gtk_tree_model_get (model, &iter, CE_COLUMN_DATE_JULIAN, &julian_day, -1);

		if (g_date_valid_julian (julian_day) == TRUE) {
			g_date_set_julian (date, julian_day);
			cal_jump_to_date (date, appGUI);
			gtk_notebook_set_current_page (GTK_NOTEBOOK (appGUI->notebook), PAGE_CALENDAR);
			button_event_checker_show_osmo_window_cb (NULL, appGUI);
		}
	}

	g_date_free (date);
}

/*------------------------------------------------------------------------------*/

gint
event_checker_list_dbclick_cb(GtkWidget * widget, GdkEventButton * event, gpointer func_data) {

    GUI *appGUI = (GUI *)func_data;

    if ((event->type==GDK_2BUTTON_PRESS) && (event->button == 1)) {
        event_checker_select_item (appGUI);
        return TRUE;
    }

    return FALSE;
}

/*------------------------------------------------------------------------------*/

gint 
event_checker_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data) {

    GUI *appGUI = (GUI *)data;

    switch(event->keyval) {

        case GDK_KEY_Return:
            event_checker_select_item (appGUI);
            return TRUE;
        case GDK_KEY_Escape:
            button_event_checker_window_close_cb (NULL, appGUI);
            return TRUE;
    }
    return FALSE;
}

/*------------------------------------------------------------------------------*/

static gboolean
add_note_to_event_window (struct note *n, GUI *appGUI)
{
	GtkTreeIter iter;
	gchar *note_str, *date_str, *stripped;

	note_str = cal_note_remove_empty_lines (n->note);
	stripped = utl_gui_text_strip_tags (note_str);
	g_free (note_str);
	
	date_str = g_strdup_printf ("%s\n(%s)",
	                            julian_to_str (n->date, config.date_format, config.override_locale_settings),
	                            utl_get_julian_day_name (n->date));

	gtk_list_store_append (appGUI->event_checker_list_store, &iter);
	gtk_list_store_set (appGUI->event_checker_list_store, &iter,
	                    CE_COLUMN_DATE, date_str,
	                    CE_COLUMN_DATE_JULIAN, n->date,
	                    CE_COLUMN_EVENT_TYPE, _("Day note"),
	                    CE_COLUMN_EVENT_LINE, stripped, -1);

	g_free (stripped);
	g_free (date_str);

	return TRUE;
}

/*------------------------------------------------------------------------------*/

gboolean
create_event_checker_window (GUI *appGUI) {

GtkWidget           *scrolledwindow;
GtkWidget           *vbox1;
GtkWidget           *hseparator;
GtkWidget           *hbuttonbox;
GtkWidget           *close_button;
GtkWidget           *show_osmo_button;
GtkTreeViewColumn   *column, *julian_day_column;
GtkCellRenderer     *renderer;

#if defined(TASKS_ENABLED) || defined(CONTACTS_ENABLED)
GtkTreePath         *path;
GtkTreeIter         iter;
gchar               date_str[BUFFER_SIZE];
guint32             j;
#endif

guint32             start_date, current_date;
gboolean            day_notes_flag, tasks_flag = FALSE;

#ifdef TASKS_ENABLED
gchar               *summary, *category;
guint32             julian_day;
#endif  /* TASKS_ENABLED */

#ifdef CONTACTS_ENABLED
gboolean            birthdays_flag;
guint32             date;
gint32              age;
GDate               *new_cdate;
gchar               tmpbuf[BUFFER_SIZE], template[BUFFER_SIZE];
gchar               *first_name, *last_name;
#endif  /* CONTACTS_ENABLED */


    start_date = config.lastrun_date;           /* - */
    current_date = utl_date_get_current_julian () + appGUI->check_ndays_events;     /* + */

    /* check day notes */
	day_notes_flag = cal_check_notes (start_date, current_date, appGUI);


#ifdef TASKS_ENABLED
    /* check tasks */

    tasks_flag = FALSE;

    if (appGUI->tsk->tasks_list_store != NULL) {
        path = gtk_tree_path_new_first();

        while (gtk_tree_model_get_iter (GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter, path) == TRUE) {
            gtk_tree_model_get (GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter, 
                                TA_COLUMN_DUE_DATE_JULIAN, &julian_day, TA_COLUMN_CATEGORY, &category, -1);

            j = start_date;

            while (j <= current_date) {
                if (j == julian_day && (tsk_get_category_state (category, STATE_EITHER, appGUI) == TRUE)) {
                    tasks_flag = TRUE;
                    break;
                }
                j++;
            };

            if (tasks_flag == TRUE) {
                g_free (category);
                break;
            }

            g_free (category);
            gtk_tree_path_next(path);
        }
        gtk_tree_path_free(path);

    }
#endif  /* TASKS_ENABLED */


#ifdef CONTACTS_ENABLED

    /* check birthdays */

    birthdays_flag = FALSE;

    if (appGUI->cnt->contacts_list_store != NULL) {

        path = gtk_tree_path_new_first();

        while (gtk_tree_model_get_iter (GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), &iter, path) == TRUE) {
            gtk_tree_model_get (GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), &iter, 
                                COLUMN_BIRTH_DAY_DATE, &date, -1);
            if (date != 0) {

                new_cdate = g_date_new_julian (date);

                if (new_cdate != NULL) {

                    age = g_date_get_year (appGUI->cal->date) - g_date_get_year (new_cdate);

                    if (age >= 0) {

                        j = start_date;

                        while (j <= current_date) {
                            g_date_set_year (new_cdate, julian_to_year(j));
                            date = g_date_get_julian (new_cdate);
                            if (j == date) {
                                birthdays_flag = TRUE;
                                break;
                            }
                            j++;
                        };
                    }

                    g_date_free(new_cdate);
                }
            }

            if (birthdays_flag == TRUE) break;

            gtk_tree_path_next(path);
        }
        gtk_tree_path_free(path);
    }
#endif  /* CONTACTS_ENABLED */

    /* any events available ? */

#ifdef CONTACTS_ENABLED
    if (day_notes_flag == FALSE && tasks_flag == FALSE && birthdays_flag == FALSE) {
#else
    if (day_notes_flag == FALSE && tasks_flag == FALSE) {
#endif  /* CONTACTS_ENABLED */
        return FALSE;           /* if not then quit */
    }
        
    appGUI->event_checker_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (appGUI->event_checker_window), _("Events"));
    gtk_window_set_position (GTK_WINDOW (appGUI->event_checker_window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size (GTK_WINDOW(appGUI->event_checker_window), 750, 650);
    gtk_window_set_modal (GTK_WINDOW (appGUI->event_checker_window), TRUE);
    g_signal_connect (G_OBJECT (appGUI->event_checker_window), "delete_event",
                      G_CALLBACK(button_event_checker_window_delete_cb), appGUI);
    g_signal_connect (G_OBJECT (appGUI->event_checker_window), "key_press_event",
                      G_CALLBACK (event_checker_key_press_cb), appGUI);
    gtk_container_set_border_width (GTK_CONTAINER (appGUI->event_checker_window), 8);

    vbox1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox1);
    gtk_container_add (GTK_CONTAINER (appGUI->event_checker_window), vbox1);

    scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
    gtk_widget_show (scrolledwindow);
    gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow, TRUE, TRUE, 0);

    gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    appGUI->event_checker_list_store = gtk_list_store_new (CHECK_EVENTS_NUM_COLUMNS, 
                                                           G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);

    appGUI->event_checker_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(appGUI->event_checker_list_store));
    gtk_widget_show (appGUI->event_checker_list);
    gtk_widget_set_can_default (appGUI->event_checker_list, TRUE);
    gtk_container_add (GTK_CONTAINER (scrolledwindow), appGUI->event_checker_list);
    g_signal_connect(G_OBJECT(appGUI->event_checker_list), "button_press_event",
                     G_CALLBACK(event_checker_list_dbclick_cb), appGUI);

    appGUI->event_checker_list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (appGUI->event_checker_list));

    /* create columns */

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes (_("Date"), renderer, 
                                                       "text", CE_COLUMN_DATE, 
                                                       NULL);
    gtk_tree_view_column_set_visible (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW(appGUI->event_checker_list), column);

    renderer = gtk_cell_renderer_text_new();
	g_object_set (G_OBJECT(renderer), "xpad", 8, NULL);
    julian_day_column = gtk_tree_view_column_new_with_attributes ("Julian", renderer, 
                                                                  "text", CE_COLUMN_DATE_JULIAN, 
                                                                  NULL);
    gtk_tree_view_column_set_visible (julian_day_column, FALSE);
    gtk_tree_view_append_column (GTK_TREE_VIEW(appGUI->event_checker_list), julian_day_column);
    gtk_tree_view_column_set_sort_column_id (julian_day_column, CE_COLUMN_DATE_JULIAN);

    renderer = gtk_cell_renderer_text_new();
	g_object_set (G_OBJECT(renderer), "xpad", 8, NULL);
    column = gtk_tree_view_column_new_with_attributes (_("Type"), renderer, 
                                                       "text", CE_COLUMN_EVENT_TYPE, 
                                                       NULL);
    gtk_tree_view_column_set_visible (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW(appGUI->event_checker_list), column);

    renderer = gtk_cell_renderer_text_new();
    g_object_set (G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    column = gtk_tree_view_column_new_with_attributes (_("Event"), renderer, 
                                                       "text", CE_COLUMN_EVENT_LINE, 
                                                       NULL);
    gtk_tree_view_column_set_visible (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW(appGUI->event_checker_list), column);

    /* insert day notes */

    if (day_notes_flag == TRUE) {
		cal_notes_foreach (start_date, current_date, add_note_to_event_window, appGUI);
    }

#ifdef TASKS_ENABLED

    /* insert tasks */

    if (tasks_flag == TRUE) {

        path = gtk_tree_path_new_first();

        while (gtk_tree_model_get_iter (GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter, path) == TRUE) {
            gtk_tree_model_get (GTK_TREE_MODEL(appGUI->tsk->tasks_list_store), &iter, 
                                TA_COLUMN_DUE_DATE_JULIAN, &julian_day, 
                                TA_COLUMN_SUMMARY, &summary, 
                                TA_COLUMN_CATEGORY, &category, -1);

            j = start_date;

            while (j <= current_date) {
                if (j == julian_day && (tsk_get_category_state (category, STATE_EITHER, appGUI) == TRUE)) {
                    gtk_list_store_append(appGUI->event_checker_list_store, &iter);
                    g_snprintf (date_str, BUFFER_SIZE, "%s\n(%s)", 
								julian_to_str (julian_day, config.date_format, config.override_locale_settings), 
								utl_get_julian_day_name (julian_day));
                    gtk_list_store_set(appGUI->event_checker_list_store, &iter, 
                                       CE_COLUMN_DATE, date_str,
                                       CE_COLUMN_DATE_JULIAN, julian_day, 
                                       CE_COLUMN_EVENT_TYPE, _("Task"),
                                       CE_COLUMN_EVENT_LINE, summary, -1);
                }
                j++;
            };

            g_free (summary);
            g_free (category);

            gtk_tree_path_next(path);
        }
        gtk_tree_path_free(path);
    }
#endif  /* TASKS_ENABLED */

#ifdef CONTACTS_ENABLED
    /* insert birthdays */

    if (birthdays_flag == TRUE) {

        g_snprintf(template, BUFFER_SIZE, "(%s)", _("None"));

        path = gtk_tree_path_new_first();

        while (gtk_tree_model_get_iter (GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), &iter, path) == TRUE) {
            gtk_tree_model_get (GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), &iter, 
                                COLUMN_BIRTH_DAY_DATE, &date, 
                                COLUMN_FIRST_NAME, &first_name,
                                COLUMN_LAST_NAME, &last_name, -1);

            if (date != 0) {

                new_cdate = g_date_new_julian (date);

                if (new_cdate != NULL) {

                    age = g_date_get_year (appGUI->cal->date) - g_date_get_year(new_cdate);

                    if (age >= 0) {

                        j = start_date;

                        while (j <= current_date) {
                            g_date_set_year (new_cdate, julian_to_year(j));
                            date = g_date_get_julian (new_cdate);
                            if (j == date) {
                                if (g_utf8_collate(first_name, template) == 0) {
                                    g_snprintf(tmpbuf, BUFFER_SIZE, "%s", last_name);
                                } else if (g_utf8_collate(last_name, template) == 0) {
                                    g_snprintf(tmpbuf, BUFFER_SIZE, "%s", first_name);
                                } else {
                                    g_snprintf(tmpbuf, BUFFER_SIZE, "%s %s", first_name, last_name);
                                }
                                gtk_list_store_append(appGUI->event_checker_list_store, &iter);
                                g_snprintf(date_str, BUFFER_SIZE, "%s\n(%s)", 
                                         julian_to_str (j, config.date_format, config.override_locale_settings), 
										 utl_get_julian_day_name (j));
                                gtk_list_store_set(appGUI->event_checker_list_store, &iter, 
                                                   CE_COLUMN_DATE, date_str,
                                                   CE_COLUMN_DATE_JULIAN, j, 
                                                   CE_COLUMN_EVENT_TYPE, _("Birthday"),
                                                   CE_COLUMN_EVENT_LINE, tmpbuf, -1);
                            }
                            j++;
                        };
                    }

                    g_date_free(new_cdate);
                }
            }

            g_free(first_name);
            g_free(last_name);

            gtk_tree_path_next(path);
        }
        gtk_tree_path_free(path);
    }

#endif  /* CONTACTS_ENABLED */

    g_signal_emit_by_name(julian_day_column, "clicked");

    hseparator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
    gtk_widget_show (hseparator);
    gtk_box_pack_start (GTK_BOX (vbox1), hseparator, FALSE, TRUE, 4);

    hbuttonbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
    gtk_widget_show (hbuttonbox);
    gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox, FALSE, TRUE, 0);
    gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
    gtk_box_set_spacing (GTK_BOX (hbuttonbox), 8);

    show_osmo_button = gtk_button_new_with_label (_("Show Osmo"));
    gtk_widget_show (show_osmo_button);
    gtk_widget_set_can_focus(show_osmo_button, FALSE);
    g_signal_connect(show_osmo_button, "clicked", G_CALLBACK(button_event_checker_show_osmo_window_cb), appGUI);
    gtk_container_add(GTK_CONTAINER(hbuttonbox), show_osmo_button);

    close_button = gtk_button_new_with_mnemonic (_("_Close"));
    gtk_widget_show (close_button);
    gtk_widget_set_can_focus(close_button, FALSE);
    g_signal_connect(close_button, "clicked", G_CALLBACK(button_event_checker_window_close_cb), appGUI);
    gtk_container_add(GTK_CONTAINER(hbuttonbox), close_button);

    gtk_widget_show(appGUI->event_checker_window);

    return TRUE;
}

/*------------------------------------------------------------------------------*/

