/*
 *  Authors: Rodney Dawes <dobey@ximian.com>
 *
 *  Copyright 2004-2007 Novell, Inc. (www.novell.com)
 *  Copyright 2008-2009 Rodney Dawes
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of version 2 of the GNU General Public License
 *  as published by the Free Software Foundation
 *
 *  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 Street #330, Boston, MA 02111-1307, USA.
 *
 */

#include <config.h>

#include "evolution-webcal-notify.h"

#define WEBCAL_BASE_URI "webcal://"

#define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
#define TASKS_CONFIG_PREFIX "/apps/evolution/tasks"

#define SELECTED_CALENDARS_KEY CALENDAR_CONFIG_PREFIX "/display/selected_calendars"
#define SELECTED_TASKS_KEY CALENDAR_CONFIG_PREFIX "/tasks/selected_tasks"

#define CALENDAR_SOURCES_KEY CALENDAR_CONFIG_PREFIX "/sources"
#define TASKS_SOURCES_KEY TASKS_CONFIG_PREFIX "/sources"

typedef struct {
  GtkWidget * dialog;
  GtkWidget * image;
  GtkIconTheme * theme;
} EIcalShareDialog;

enum {
  E_ICALSHARE_TIMEOUT_DAYS,
  E_ICALSHARE_TIMEOUT_HOURS,
  E_ICALSHARE_TIMEOUT_MINUTES,
  E_ICALSHARE_TIMEOUT_WEEKS
};

static GtkWidget * e_webcal_label_new (const gchar * str) {
  GtkWidget * label;

  label = gtk_label_new (str);
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);

  return label;
}

static void e_webcal_label_size_allocate_cb (GtkWidget *widget,
					     GtkAllocation *allocation,
					     gpointer user_data) {
  gint width, height;
  GtkWidget *label;

  label = user_data;

  gtk_widget_get_size_request (label, &width, &height);

  if (allocation->width > 1 &&
      allocation->width != (width + label->allocation.x)) {
    gtk_widget_set_size_request (label,
				 allocation->width - label->allocation.x,
				 -1);
    gtk_container_check_resize (GTK_CONTAINER (widget));
  }
}

static void e_webcal_label_handle_resize (GtkWidget * label,
					  GtkWidget * container) {
  g_signal_connect (G_OBJECT (container), "size-allocate",
		    G_CALLBACK (e_webcal_label_size_allocate_cb), label);
}

static void e_webcal_icon_theme_changed (GtkIconTheme * theme,
					 EIcalShareDialog * dialog) {
  gtk_window_set_default_icon_name ("x-office-calendar");
  gtk_image_set_from_icon_name (GTK_IMAGE (dialog->image),
				"x-office-calendar", GTK_ICON_SIZE_DIALOG);
}

static void e_webcal_change_adjustment (GtkComboBox * toption,
                    GtkWidget * spinner) {
  if (toption != NULL) {
	switch (gtk_combo_box_get_active (toption)) {
    case E_ICALSHARE_TIMEOUT_DAYS:
      gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinner), 1, 6);
      gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), 1);
      break;
    case E_ICALSHARE_TIMEOUT_HOURS:
      gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinner), 1, 23);
      gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), 12);
      break;
    case E_ICALSHARE_TIMEOUT_MINUTES:
      gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinner), 1, 59);
      gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), 30);
      break;
    case E_ICALSHARE_TIMEOUT_WEEKS:
      gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinner), 1, 23);
      gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), 1);
      break;
    }
  }
}

void e_webcal_display_error (const gchar * title, const gchar * message,
			     GtkWidget * parent) {
  GtkWidget * dialog;
  GtkWidget * label;
  GtkWidget * image;
  GtkWidget * hbox, * vbox;
  GdkPixbuf * pixbuf;
  gchar * markup;

  dialog = gtk_dialog_new ();

  gtk_window_set_title (GTK_WINDOW (dialog), title);
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
  gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
  gtk_window_set_default_size (GTK_WINDOW (dialog), 256, -1);

  gtk_widget_realize (dialog);

  gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
			  GTK_STOCK_OK, GTK_RESPONSE_OK,
			  NULL);

  pixbuf = gtk_widget_render_icon (dialog, GTK_STOCK_DIALOG_ERROR,
				   GTK_ICON_SIZE_DIALOG, "error-dialog");
  gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf);
  g_object_unref (pixbuf);

  gdk_window_set_decorations (GDK_WINDOW (dialog->window), GDK_DECOR_BORDER);

  gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 12);
  gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 0);
  hbox = gtk_hbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
		      TRUE, TRUE, 0);
  gtk_widget_show (hbox);

  image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR,
				    GTK_ICON_SIZE_DIALOG);
  gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
  gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
  gtk_widget_show (image);

  vbox = gtk_vbox_new (FALSE, 12);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);

  markup = g_strdup_printf ("<big><b>%s</b></big>", title);
  label = gtk_label_new (markup);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);
  g_free (markup);

  label = gtk_label_new (message);
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_label_set_max_width_chars (GTK_LABEL (label), 80);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
  case GTK_RESPONSE_OK:
    gtk_widget_destroy (dialog);
    break;
  }
}

static void select_source (ESource * source, icalcomponent_kind kind) {
  const gchar * key = NULL;
  GConfClient * config = NULL;
  GSList * list;

  g_return_if_fail (source != NULL);

  if (kind == ICAL_VEVENT_COMPONENT)
    key = SELECTED_CALENDARS_KEY;
  else if (kind == ICAL_VTODO_COMPONENT)
    key = SELECTED_TASKS_KEY;
  else
    return;

  config = gconf_client_get_default ();

  list = gconf_client_get_list (config, key, GCONF_VALUE_STRING, NULL);
  list = g_slist_append (list, (char *) e_source_peek_uid (source));
  gconf_client_set_list (config, key, GCONF_VALUE_STRING, list, NULL);

  g_slist_free (list);

  g_object_unref (config);
}

void e_webcal_query_user (const gchar * name, const gchar * desc,
                          const gchar * caluri,
                          gboolean has_events, gboolean has_tasks) {
  EIcalShareDialog * dialog;
  GtkObject * spinadj;
  GtkWidget * tspinb, * toption;
  GtkWidget * vbox, * hbox;
  GtkWidget * lbox, * wbox, * xbox;
  GtkWidget * rlabel, * clabel;
  GtkWidget * cbutton;
  GtkWidget * label, * button;
  SoupURI * tmpuri;
  gchar * tmpname;
  gchar * mrkname, * ref_str;
  gint ref_timeout, ref_multi;
  ESource * source;
  ESourceGroup * events_group = NULL;
  ESourceGroup * tasks_group = NULL;
  ESourceList * events_sources, * tasks_sources;
  GdkColor color;
  GSList * l;
  gchar * scolor;
  guint32 icolor = 0;

  tmpuri = soup_uri_new (caluri);
  tmpname = g_path_get_basename (tmpuri->path);

  /* If we are already subscribed, pop up an error dialog */
  if (!has_events && !has_tasks) {
    gchar * msg;

    msg = g_strdup_printf (_("No events or tasks were found in the calendar "
			     "\"%s\"."),
			   name ? name : tmpname);
    e_webcal_display_error (_("<b>No Tasks or Events Found</b>"), msg, NULL);
    g_free (msg);

    g_free (tmpname);
    soup_uri_free (tmpuri);
    
    gtk_main_quit ();
    return;
  }

  events_sources = e_source_list_new_for_gconf_default (CALENDAR_SOURCES_KEY);

  for (l = e_source_list_peek_groups (events_sources); l != NULL; l = l->next) {
    if (!strcmp (e_source_group_peek_base_uri (l->data), WEBCAL_BASE_URI)) {
      events_group = l->data;
      break;
    }
  }

  if (!events_group) {
    /* Create the Webcal source group */
    events_group = e_source_group_new ("On The Web", WEBCAL_BASE_URI);
    e_source_list_add_group (events_sources, events_group, -1);
  }

  tasks_sources  = e_source_list_new_for_gconf_default (TASKS_SOURCES_KEY);

  for (l = e_source_list_peek_groups (tasks_sources); l != NULL; l = l->next) {
    if (!strcmp (e_source_group_peek_base_uri (l->data), WEBCAL_BASE_URI)) {
      tasks_group = l->data;
      break;
    }
  }

  if (!tasks_group) {
    /* Create the Webcal source group */
    tasks_group = e_source_group_new ("On The Web", WEBCAL_BASE_URI);
    e_source_list_add_group (tasks_sources, tasks_group, -1);
  }

  if (has_events)
    if (e_source_group_peek_source_by_name (events_group,
					    name ? name : tmpname)) {
      has_events = FALSE;
    }

  if (has_tasks)
    if (e_source_group_peek_source_by_name (tasks_group,
					    name ? name : tmpname)) {
      has_tasks = FALSE;
    }

  /* If we are already subscribed, pop up an error dialog */
  if (!has_events && !has_tasks) {
    gchar * msg;

    msg = g_strdup_printf (_("You are already subscribed to the calendar "
			     "\"%s\". Please subscribe to another calendar."),
			   name ? name : tmpname);
    e_webcal_display_error (_("<b>Already Subscribed</b>"), msg, NULL);
    g_free (msg);

    g_free (tmpname);
    soup_uri_free (tmpuri);
    
    g_object_unref (events_sources);
    g_object_unref (tasks_sources);

    gtk_main_quit ();
    return;
  }

  source = e_source_new (name ? name : tmpname, caluri);

  dialog = g_new0 (EIcalShareDialog, 1);

  dialog->theme = gtk_icon_theme_get_default ();

  g_signal_connect (G_OBJECT (dialog->theme), "changed",
		    G_CALLBACK (e_webcal_icon_theme_changed), dialog);

  dialog->dialog = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (dialog->dialog),
			_("Subscribe to Calendar"));
  gtk_dialog_set_has_separator (GTK_DIALOG (dialog->dialog), FALSE);
  gtk_window_set_default_size (GTK_WINDOW (dialog->dialog), 376, 144);

  gtk_window_set_default_icon_name ("x-office-calendar");

  gtk_widget_realize (dialog->dialog);

  /* More Sane than Stupid Pet Tricks */
  gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog->dialog)->action_area), 12);
  gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog->dialog)->vbox), 0);
  gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog->dialog)->action_area), 6);

  /* Dialog Buttons */
  /*
     We need documentation, a Help button is rather useless if all it
     does, is the exact same thing as Cancel
  */
  /*
  label = gtk_button_new_from_stock (GTK_STOCK_HELP);
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog->dialog), label,
				GTK_RESPONSE_HELP);
  gtk_widget_show (label);
  */

  label = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog->dialog), label,
				GTK_RESPONSE_CANCEL);
  gtk_widget_show (label);

  button = gtk_button_new ();
  label = gtk_alignment_new (0.5, 0.5, 0, 0);
  gtk_container_add (GTK_CONTAINER (button), label);
  gtk_widget_show (label);
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_container_add (GTK_CONTAINER (label), hbox);
  gtk_widget_show (hbox);
  label = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
  gtk_widget_show (label);
  label = gtk_label_new_with_mnemonic (_("_Subscribe"));
  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
  gtk_widget_show (label);
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog->dialog), button,
				GTK_RESPONSE_OK);
  gtk_widget_show (button);

  /* Main Box */
  hbox = gtk_hbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog->dialog)->vbox), hbox,
		      TRUE, TRUE, 0);
  gtk_widget_show (hbox);

  /* Lame Box for Pretty Icon so It is Top-aligned */
  vbox = gtk_vbox_new (FALSE, 6);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);

  /* Pretty icon */
  dialog->image = gtk_image_new_from_icon_name ("x-office-calendar",
						GTK_ICON_SIZE_DIALOG);
  gtk_box_pack_start (GTK_BOX (vbox), dialog->image, FALSE, FALSE, 0);
  gtk_widget_show (dialog->image);

  /* Box for Name and Description */
  vbox = gtk_vbox_new (FALSE, 12);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
  gtk_widget_show (vbox);

  /* Name */
  mrkname = g_strdup_printf ("<b>%s</b>", name ? name : tmpname);
  label = e_webcal_label_new (mrkname);
  e_webcal_label_handle_resize (label, vbox);
  g_free (mrkname);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  /* Description */
  label = e_webcal_label_new (desc ? desc : _("No Description"));
  e_webcal_label_handle_resize (label, vbox);
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  /* Box for extra input label and widget boxes */
  xbox = gtk_hbox_new (FALSE, 12);
  gtk_box_pack_start (GTK_BOX (vbox), xbox, FALSE, FALSE, 0);
  gtk_widget_show (xbox);

  /* Box for labels of extra inputs */
  lbox = gtk_vbox_new (TRUE, 6);
  gtk_box_pack_start (GTK_BOX (xbox), lbox, FALSE, FALSE, 0);
  gtk_widget_show (lbox);

  /* Refresh entry label */
  rlabel = gtk_label_new_with_mnemonic (_("_Refresh Every:"));
  gtk_misc_set_alignment (GTK_MISC (rlabel), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (lbox), rlabel, FALSE, FALSE, 0);
  gtk_widget_show (rlabel);

  /* Color picker label */
  clabel = gtk_label_new_with_mnemonic (_("C_olor:"));
  gtk_misc_set_alignment (GTK_MISC (clabel), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (lbox), clabel, FALSE, FALSE, 0);
  gtk_widget_show (clabel);

  /* Box for widgets of extra inputs */
  wbox = gtk_vbox_new (TRUE, 6);
  gtk_box_pack_start (GTK_BOX (xbox), wbox, FALSE, FALSE, 0);
  gtk_widget_show (wbox);

  /* Box for Refresh timeout */
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_start (GTK_BOX (wbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  /* We need to ask the user when to Refresh the calendar */
  spinadj = gtk_adjustment_new (12, 1, 23, 1, 1, 0);
  tspinb = gtk_spin_button_new (GTK_ADJUSTMENT (spinadj), 12, 0);
  gtk_box_pack_start (GTK_BOX (hbox), tspinb, FALSE, FALSE, 0);
  gtk_widget_show (tspinb);

  gtk_label_set_mnemonic_widget (GTK_LABEL (rlabel), tspinb);

  toption = gtk_combo_box_new_text();
  gtk_box_pack_start (GTK_BOX (hbox), toption, FALSE, FALSE, 0);
  gtk_widget_show (toption);

  gtk_combo_box_append_text (GTK_COMBO_BOX (toption), _("Days"));
  gtk_combo_box_append_text (GTK_COMBO_BOX (toption), _("Hours"));
  gtk_combo_box_append_text (GTK_COMBO_BOX (toption), _("Minutes"));
  gtk_combo_box_append_text (GTK_COMBO_BOX (toption), _("Weeks"));

  gtk_combo_box_set_active (GTK_COMBO_BOX (toption), E_ICALSHARE_TIMEOUT_HOURS);

  /* If the user changes the units of time, we want to update the spin
     button with more appropriate ranges for the time to refresh */
  g_signal_connect (GTK_COMBO_BOX (toption), "changed",
            G_CALLBACK (e_webcal_change_adjustment), tspinb);

  /* Color button for the calendar properties */
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_start (GTK_BOX (wbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  cbutton = gtk_color_button_new ();
  gtk_box_pack_start (GTK_BOX (hbox), cbutton, FALSE, FALSE, 0);
  gtk_widget_show (cbutton);

  scolor = g_strdup_printf ("#%s", e_source_get_property (source, "color"));
  gdk_color_parse (scolor, &color);
  gtk_color_button_set_color (GTK_COLOR_BUTTON (cbutton), &color);
  g_free (scolor);

  gtk_label_set_mnemonic_widget (GTK_LABEL (clabel), cbutton);

  g_free (tmpname);
  soup_uri_free (tmpuri);

  switch (gtk_dialog_run (GTK_DIALOG (dialog->dialog))) {
  case GTK_RESPONSE_HELP:
    break;
  case GTK_RESPONSE_OK:
	switch (gtk_combo_box_get_active (GTK_COMBO_BOX (toption)))
	{
    case E_ICALSHARE_TIMEOUT_DAYS:
      ref_multi = 1440;
      break;
    case E_ICALSHARE_TIMEOUT_HOURS:
      ref_multi = 60;
      break;
    case E_ICALSHARE_TIMEOUT_MINUTES:
      ref_multi = 1;
      break;
    case E_ICALSHARE_TIMEOUT_WEEKS:
      ref_multi = 1440 * 7;
      break;
    }
    ref_timeout = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (tspinb));
    ref_str = g_strdup_printf ("%d", ref_timeout * ref_multi);
    e_source_set_property (source, "refresh", ref_str);
    g_free (ref_str);

    gtk_color_button_get_color (GTK_COLOR_BUTTON (cbutton), &color);
    icolor = ((color.red >> 8) << 16) | ((color.green >> 8) << 8) | (color.blue >> 8);
    e_source_set_color (source, icolor);

    if (!strncmp (caluri, WEBCAL_BASE_URI, 9)) {
      e_source_set_relative_uri (source, caluri + 9);
    } else if (!strncmp (caluri, "http://", 7)) {
      e_source_set_relative_uri (source, caluri + 7);
    }

    if (has_events) {
      e_source_group_add_source (events_group, source, -1);
      e_source_list_sync (events_sources, NULL);
      select_source (source, ICAL_VEVENT_COMPONENT);
    }

    if (has_tasks) {
      e_source_group_add_source (tasks_group, source, -1);
      e_source_list_sync (tasks_sources, NULL);
      select_source (source, ICAL_VTODO_COMPONENT);
    }

    /* We fall through to the CANCEL/default cases to handle all the unreffing
       and widget destruction */
  case GTK_RESPONSE_CANCEL:
  default:
    g_object_unref (events_sources);
    g_object_unref (tasks_sources);
    g_object_unref (source);

    gtk_widget_destroy (dialog->dialog);
    break;
  }
}
