/* -*- mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * gcal-ediat-dialog.c
 * Copyright (C) 2015 Erick Pérez Castellanos <erickpc@gnome.org>
 *
 * gnome-calendar 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.
 *
 * gnome-calendar 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 "gcal-edit-dialog.h"
#include "gcal-time-selector.h"
#include "gcal-date-selector.h"
#include "gcal-utils.h"

#include <libecal/libecal.h>
#include <glib/gi18n.h>

typedef struct
{
  gchar            *source_uid;
  gchar            *event_uid;
  gchar            *event_rid;
  gboolean          writable;

  GcalManager      *manager;

  GtkWidget        *titlebar;
  GtkWidget        *lock;
  GtkWidget        *source_image;
  GtkWidget        *source_label;

  GtkWidget        *delete_button;
  GtkWidget        *done_button;
  GtkWidget        *cancel_button;
  GtkWidget        *sources_button;
  GtkWidget        *sources_popover;

  GtkWidget        *summary_entry;

  GtkWidget        *start_date_selector;
  GtkWidget        *end_date_selector;
  GtkWidget        *all_day_check;
  GtkWidget        *start_time_selector;
  GtkWidget        *end_time_selector;
  GtkWidget        *location_entry;
  GtkWidget        *notes_text;

  /* actions */
  GMenu              *sources_menu;
  GSimpleAction      *action;
  GSimpleActionGroup *action_group;

  /* new data holders */
  ESource          *source; /* weak reference */
  ECalComponent    *component;

  /* flags */
  gboolean          format_24h;
  gboolean          event_is_new;
  gboolean          setting_event;
} GcalEditDialogPrivate;

static void        fill_sources_menu                      (GcalEditDialog    *dialog);

static void        on_calendar_selected                   (GtkWidget         *menu_item,
                                                           GVariant          *value,
                                                           gpointer           user_data);

static void        update_date                            (GtkEntry          *entry,
                                                           gpointer           user_data);

static void        update_location                        (GtkEntry          *entry,
                                                           GParamSpec        *pspec,
                                                           gpointer           user_data);

static void        update_notes                           (GtkTextBuffer     *buffer,
                                                           gpointer           user_data);

static void        update_summary                         (GtkEntry          *entry,
                                                           GParamSpec        *pspec,
                                                           gpointer           user_data);

static void        update_time                            (GtkEntry          *entry,
                                                           gpointer           user_data);

static void        gcal_edit_dialog_constructed           (GObject           *object);

static void        gcal_edit_dialog_finalize              (GObject           *object);

static void        gcal_edit_dialog_set_writable          (GcalEditDialog    *dialog,
                                                           gboolean           writable);

static void        gcal_edit_dialog_clear_data            (GcalEditDialog    *dialog);

static void        gcal_edit_dialog_action_button_clicked (GtkWidget         *widget,
                                                           gpointer           user_data);

static void        gcal_edit_dialog_all_day_changed       (GtkWidget         *widget,
                                                           gpointer           user_data);

G_DEFINE_TYPE_WITH_PRIVATE (GcalEditDialog, gcal_edit_dialog, GTK_TYPE_DIALOG)

static void
fill_sources_menu (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;
  GList *list;
  GList *aux;

  priv = gcal_edit_dialog_get_instance_private (dialog);

  if (priv->manager == NULL)
    return;

  list = gcal_manager_get_sources (priv->manager);
  priv->sources_menu = g_menu_new ();

  for (aux = list; aux != NULL; aux = aux->next)
    {
      ESource *source;
      GMenuItem *item;
      GdkRGBA color;
      GdkPixbuf *pix;

      source = E_SOURCE (aux->data);

      /* retrieve color */
      get_color_name_from_source (source, &color);
      pix = gcal_get_pixbuf_from_color (&color, 16);;

      /* menu item */
      item = g_menu_item_new (e_source_get_display_name (source), "select_calendar");
      g_menu_item_set_icon (item, G_ICON (pix));

      /* set insensitive for read-only calendars */
      if (gcal_manager_is_client_writable (priv->manager, source))
        {
          g_menu_item_set_action_and_target_value (item, "select_calendar", NULL);
        }
      else
        {
          g_menu_item_set_action_and_target_value (item, "select_calendar",
                                                   g_variant_new_string (e_source_get_uid (source)));
        }

      g_menu_append_item (priv->sources_menu, item);

      g_object_unref (pix);
      g_object_unref (item);
    }

  gtk_popover_bind_model (GTK_POPOVER (priv->sources_popover), G_MENU_MODEL (priv->sources_menu), "edit");

  /* HACK: show the popover menu icons */
  fix_popover_menu_icons (GTK_POPOVER (priv->sources_popover));

  g_list_free (list);
}

static void
on_calendar_selected (GtkWidget *menu_item,
                      GVariant  *value,
                      gpointer   user_data)
{
  GcalEditDialogPrivate *priv;
  GList *list;
  GList *aux;
  gchar *uid;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));
  list = gcal_manager_get_sources (priv->manager);

  /* retrieve selected calendar uid */
  g_variant_get (value, "s", &uid);

  /* search for any source with the given UID */
  for (aux = list; aux != NULL; aux = aux->next)
    {
      ESource *source;
      source = E_SOURCE (aux->data);

      if (g_strcmp0 (e_source_get_uid (source), uid) == 0)
      {
        GdkRGBA color;
        GdkPixbuf *pix;

        /* retrieve color */
        get_color_name_from_source (source, &color);

        pix = gcal_get_pixbuf_from_color (&color, 16);
        gtk_image_set_from_pixbuf (GTK_IMAGE (priv->source_image), pix);
        g_object_unref (pix);

        if (priv->source != NULL)
          g_set_object (&(priv->source), source);
        gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->titlebar),
                                     e_source_get_display_name (priv->source));
        break;
      }
    }

  g_free (uid);
  g_list_free (list);
}

static void
update_date (GtkEntry   *entry,
             gpointer    user_data)
{
  GcalEditDialogPrivate *priv;
  ECalComponentDateTime dtstart;
  ECalComponentDateTime dtend;
  icaltimetype *start_date;
  icaltimetype *end_date;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));

  if (priv->setting_event)
    return;

  start_date = gcal_edit_dialog_get_start_date (GCAL_EDIT_DIALOG (user_data));
  end_date = gcal_edit_dialog_get_end_date (GCAL_EDIT_DIALOG (user_data));

  /* check if the start & end dates are sane */
  if (icaltime_compare (*start_date, *end_date) != -1)
    {
      /* change the non-editing entry */
      if (GTK_WIDGET (entry) == priv->start_date_selector)
        {
          end_date->day = start_date->day;
          end_date->month = start_date->month;
          end_date->year = start_date->year;

          if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->all_day_check)))
            {
              end_date->day += 1;
              *end_date = icaltime_normalize (*end_date);
          }
        }
      else
        {
          start_date->day = end_date->day;
          start_date->month = end_date->month;
          start_date->year = end_date->year;

          if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->all_day_check)))
            {
              start_date->day -= 1;
              *start_date = icaltime_normalize (*start_date);
          }
        }

      /* update the entries with the new sane values */
      g_signal_handlers_block_by_func (priv->start_date_selector,
                                       update_date,
                                       user_data);
      g_signal_handlers_block_by_func (priv->end_date_selector,
                                       update_date,
                                       user_data);

      gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->start_date_selector),
                                start_date->day,
                                start_date->month,
                                start_date->year);
      gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->end_date_selector),
                                end_date->day,
                                end_date->month,
                                end_date->year);

      g_signal_handlers_unblock_by_func (priv->start_date_selector,
                                       update_date,
                                       user_data);
      g_signal_handlers_unblock_by_func (priv->end_date_selector,
                                       update_date,
                                       user_data);
    }

  /* update component */
  e_cal_component_get_dtstart (priv->component, &dtstart);
  e_cal_component_get_dtend (priv->component, &dtend);

  *(dtstart.value) = *start_date;
  if (dtstart.tzid != NULL)
    {
      icaltimezone* zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
      *(dtstart.value) = icaltime_convert_to_zone (*start_date, zone);
    }

  *(dtend.value) = *end_date;
  if (dtend.tzid != NULL)
    {
      icaltimezone* zone = icaltimezone_get_builtin_timezone_from_tzid (dtend.tzid);
      *(dtend.value) = icaltime_convert_to_zone (*end_date, zone);
    }

  e_cal_component_set_dtstart (priv->component, &dtstart);
  e_cal_component_set_dtend (priv->component, &dtend);
  e_cal_component_commit_sequence (priv->component);

  g_free (start_date);
  g_free (end_date);
  e_cal_component_free_datetime (&dtstart);
  e_cal_component_free_datetime (&dtend);
}


static void
update_location (GtkEntry   *entry,
                 GParamSpec *pspec,
                 gpointer    user_data)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));

  e_cal_component_set_location (priv->component, gtk_entry_get_text (entry));
  e_cal_component_commit_sequence (priv->component);
}

static void
update_notes (GtkTextBuffer *buffer,
              gpointer       user_data)
{
  GcalEditDialogPrivate *priv;
  GSList note;
  ECalComponentText text;
  gchar *note_text;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));

  g_object_get (G_OBJECT (buffer),
                "text", &note_text,
                NULL);

  text.value = note_text;
  text.altrep = NULL;
  note.data = &text;
  note.next = NULL;

  e_cal_component_set_description_list (priv->component, &note);
  e_cal_component_commit_sequence (priv->component);

  g_free (note_text);
}

static void
update_summary (GtkEntry   *entry,
                GParamSpec *pspec,
                gpointer    user_data)
{
  GcalEditDialogPrivate *priv;

  ECalComponentText summary;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));

  summary.value = gtk_entry_get_text (entry);
  summary.altrep = NULL;

  e_cal_component_set_summary (priv->component, &summary);
  e_cal_component_commit_sequence (priv->component);
}

static void
update_time (GtkEntry   *entry,
             gpointer    user_data)
{
  GcalEditDialogPrivate *priv;
  ECalComponentDateTime dtstart;
  ECalComponentDateTime dtend;
  icaltimetype *start_date;
  icaltimetype *end_date;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));

  if (priv->setting_event)
    return;

  start_date = gcal_edit_dialog_get_start_date (GCAL_EDIT_DIALOG (user_data));
  end_date = gcal_edit_dialog_get_end_date (GCAL_EDIT_DIALOG (user_data));

  /* check if the start & end dates are sane */
  if (icaltime_compare (*start_date, *end_date) > -1)
    {
      /* change the non-editing entry */
      if (GTK_WIDGET (entry) == priv->start_time_selector)
        {
          end_date->hour = start_date->hour + 1;
          end_date->minute = start_date->minute;
          *end_date = icaltime_normalize (*end_date);
        }
      else
        {
          start_date->hour = end_date->hour - 1;
          start_date->minute = end_date->minute;
          *start_date = icaltime_normalize (*start_date);
        }

      /* update the entries with the new sane values */
      g_signal_handlers_block_by_func (priv->start_time_selector,
                                       update_time,
                                       user_data);
      g_signal_handlers_block_by_func (priv->end_time_selector,
                                       update_time,
                                       user_data);

      /* updates date as well, since hours can change the current date */
      gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->start_time_selector),
                                start_date->hour,
                                start_date->minute);
      gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->start_date_selector),
                                start_date->day,
                                start_date->month,
                                start_date->year);
      gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->end_time_selector),
                                end_date->hour,
                                end_date->minute);
      gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->end_date_selector),
                                end_date->day,
                                end_date->month,
                                end_date->year);

      g_signal_handlers_unblock_by_func (priv->start_time_selector,
                                       update_time,
                                       user_data);
      g_signal_handlers_unblock_by_func (priv->end_time_selector,
                                       update_time,
                                       user_data);
    }

  g_debug ("Updating date: %s -> %s",
           icaltime_as_ical_string (*start_date),
           icaltime_as_ical_string (*end_date));

  /* update component */
  e_cal_component_get_dtstart (priv->component, &dtstart);
  e_cal_component_get_dtend (priv->component, &dtend);

  *(dtstart.value) = *start_date;
  if (dtstart.tzid != NULL)
    {
      icaltimezone* zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
      *(dtstart.value) = icaltime_convert_to_zone (*start_date, zone);
    }

  *(dtend.value) = *end_date;
  if (dtend.tzid != NULL)
    {
      icaltimezone* zone = icaltimezone_get_builtin_timezone_from_tzid (dtend.tzid);
      *(dtend.value) = icaltime_convert_to_zone (*end_date, zone);
    }

  e_cal_component_set_dtstart (priv->component, &dtstart);
  e_cal_component_set_dtend (priv->component, &dtend);
  e_cal_component_commit_sequence (priv->component);

  g_free (start_date);
  g_free (end_date);
  e_cal_component_free_datetime (&dtstart);
  e_cal_component_free_datetime (&dtend);
}

static void
gcal_edit_dialog_class_init (GcalEditDialogClass *klass)
{
  GObjectClass *object_class;
  GtkWidgetClass *widget_class;

  object_class = G_OBJECT_CLASS (klass);
  object_class->constructed = gcal_edit_dialog_constructed;
  object_class->finalize = gcal_edit_dialog_finalize;

  widget_class = GTK_WIDGET_CLASS (klass);
  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/edit-dialog.ui");

  /* Buttons */
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, done_button);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, cancel_button);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, delete_button);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, sources_button);
  /* Entries */
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, summary_entry);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, start_time_selector);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, start_date_selector);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, end_time_selector);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, end_date_selector);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, location_entry);
  /* Other */
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, notes_text);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, all_day_check);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, titlebar);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, lock);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, source_image);
  gtk_widget_class_bind_template_child_private (widget_class, GcalEditDialog, sources_popover);

  /* callbacks */
  gtk_widget_class_bind_template_callback (widget_class, gcal_edit_dialog_action_button_clicked);
  gtk_widget_class_bind_template_callback (widget_class, gcal_edit_dialog_all_day_changed);
  gtk_widget_class_bind_template_callback (widget_class, update_summary);
  gtk_widget_class_bind_template_callback (widget_class, update_location);
  gtk_widget_class_bind_template_callback (widget_class, update_date);
  gtk_widget_class_bind_template_callback (widget_class, update_time);
}

static void
gcal_edit_dialog_init (GcalEditDialog *self)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (self);

  priv->writable = TRUE;
  gtk_widget_init_template (GTK_WIDGET (self));
}

static void
gcal_edit_dialog_constructed (GObject* object)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (object));

  /* chaining up */
  G_OBJECT_CLASS (gcal_edit_dialog_parent_class)->constructed (object);

  gtk_window_set_title (GTK_WINDOW (object), "");

  /* titlebar */
  gtk_window_set_titlebar (GTK_WINDOW (object), priv->titlebar);

  /* actions */
  priv->action_group = g_simple_action_group_new ();
  gtk_widget_insert_action_group (GTK_WIDGET (object),
                                  "edit",
                                  G_ACTION_GROUP (priv->action_group));

  priv->action = g_simple_action_new ("select_calendar", G_VARIANT_TYPE_STRING);
  g_signal_connect (priv->action,
                    "activate",
                    G_CALLBACK (on_calendar_selected), object);

  g_action_map_add_action (G_ACTION_MAP (priv->action_group), G_ACTION (priv->action));

  /* bind title & symmary */
  g_object_bind_property (priv->summary_entry,
                          "text",
                          priv->titlebar,
                          "title",
                          G_BINDING_DEFAULT);

  /* bind all-day check button & time entries */
  g_object_bind_property (priv->all_day_check,
                          "active",
                          priv->start_time_selector,
                          "sensitive",
                          G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN);
  g_object_bind_property (priv->all_day_check,
                          "active",
                          priv->end_time_selector,
                          "sensitive",
                          G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN);

  /* action area, buttons */
  g_object_set_data (G_OBJECT (priv->cancel_button),
                     "response",
                     GINT_TO_POINTER (GTK_RESPONSE_CANCEL));
  g_object_set_data (G_OBJECT (priv->delete_button),
                     "response",
                     GINT_TO_POINTER (GCAL_RESPONSE_DELETE_EVENT));
  g_object_set_data (G_OBJECT (priv->done_button),
                     "response",
                     GINT_TO_POINTER (GCAL_RESPONSE_SAVE_EVENT));

  g_signal_connect (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->notes_text)), "changed", G_CALLBACK (update_notes),
                    object);

}

static void
gcal_edit_dialog_finalize (GObject *object)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (object));

  if (priv->source_uid != NULL)
    g_free (priv->source_uid);

  if (priv->event_uid != NULL)
    g_free (priv->event_uid);

  if (priv->source != NULL)
    g_object_unref (priv->source);

  if (priv->component != NULL)
    g_object_unref (priv->component);

  if (priv->action_group != NULL)
    g_object_unref (priv->action_group);

  if (priv->action != NULL)
    g_object_unref (priv->action);

  G_OBJECT_CLASS (gcal_edit_dialog_parent_class)->finalize (object);
}

static void
gcal_edit_dialog_set_writable (GcalEditDialog *dialog,
                               gboolean        writable)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);

  priv->writable = writable;

  if (! writable)
    {
      gtk_widget_show (priv->lock);
    }
  else
    {
      gtk_widget_hide (priv->lock);
    }

  gtk_editable_set_editable (GTK_EDITABLE (priv->summary_entry), writable);
  gtk_editable_set_editable (GTK_EDITABLE (priv->location_entry), writable);

  gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->notes_text), writable);

  gtk_widget_set_sensitive (priv->all_day_check, writable);
  gtk_widget_set_sensitive (priv->end_date_selector, writable);
  gtk_widget_set_sensitive (priv->start_date_selector, writable);

  if (!writable || (writable && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->all_day_check))))
    {
      gtk_widget_set_sensitive (priv->start_time_selector, FALSE);
      gtk_widget_set_sensitive (priv->end_time_selector, FALSE);
    }
  else
    {
      gtk_widget_set_sensitive (priv->start_time_selector, TRUE);
      gtk_widget_set_sensitive (priv->end_time_selector, TRUE);
    }

  gtk_button_set_label (GTK_BUTTON (priv->done_button),
                        writable ? _("Save") : _("Done"));

  /* add delete_button here */
  gtk_widget_set_sensitive (priv->delete_button, writable);
}

static void
gcal_edit_dialog_clear_data (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);

  /* summary */
  g_signal_handlers_block_by_func (priv->summary_entry,
                                   update_summary,
                                   dialog);
  gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), "");
  g_signal_handlers_unblock_by_func (priv->summary_entry,
                                     update_summary,
                                     dialog);

  /* date and time */
  gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->start_date_selector), 0, 0, 0);
  gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->start_time_selector), 0, 0);
  gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->end_date_selector), 0, 0, 0);
  gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->end_time_selector), 0, 0);

  /* location */
  g_signal_handlers_block_by_func (priv->location_entry,
                                   update_location,
                                   dialog);
  gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
  g_signal_handlers_unblock_by_func (priv->location_entry,
                                     update_location,
                                     dialog);

  /* notes */
  g_signal_handlers_block_by_func (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->notes_text)),
                                   update_notes,
                                   dialog);
  gtk_text_buffer_set_text (
      gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->notes_text)),
      "",
      -1);
  g_signal_handlers_unblock_by_func (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->notes_text)),
                                     update_notes,
                                     dialog);
}

static void
gcal_edit_dialog_action_button_clicked (GtkWidget *widget,
                                        gpointer   user_data)
{
  GcalEditDialogPrivate *priv;
  gint response;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));
  response = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
                                                  "response"));

  if (response == GCAL_RESPONSE_SAVE_EVENT &&
      priv->event_is_new)
    {
      response = GCAL_RESPONSE_CREATE_EVENT;
    }

  gtk_dialog_response (GTK_DIALOG (user_data), response);
}

static void
gcal_edit_dialog_all_day_changed (GtkWidget *widget,
                                  gpointer   user_data)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (user_data));

  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->all_day_check)))
    {
      gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->start_time_selector), 0, 0);
      gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->end_time_selector), 0, 0);
    }
}

/* Public API */
GtkWidget*
gcal_edit_dialog_new (gboolean format_24h)
{
  GtkWidget *dialog;
  GcalEditDialogPrivate *priv;

  dialog = g_object_new (GCAL_TYPE_EDIT_DIALOG, NULL);
  priv = gcal_edit_dialog_get_instance_private (GCAL_EDIT_DIALOG (dialog));

  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
  priv->format_24h = format_24h;

  gcal_time_selector_set_time_format (GCAL_TIME_SELECTOR (priv->start_time_selector), format_24h);
  gcal_time_selector_set_time_format (GCAL_TIME_SELECTOR (priv->end_time_selector), format_24h);

  return dialog;
}

void
gcal_edit_dialog_set_event_is_new (GcalEditDialog *dialog,
                                   gboolean        event_is_new)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);
  priv->event_is_new = event_is_new;
  gtk_widget_set_visible (GTK_WIDGET (priv->delete_button), !event_is_new);

  /* FIXME: implement moving events to other sources */
  gtk_widget_set_sensitive (GTK_WIDGET (priv->sources_button), event_is_new);
  gtk_button_set_relief (GTK_BUTTON (priv->sources_button), event_is_new ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE);
}

void
gcal_edit_dialog_set_event_data (GcalEditDialog *dialog,
                                 GcalEventData  *data)
{
  GcalEditDialogPrivate *priv;

  GdkRGBA color;
  GdkPixbuf *pix;

  const gchar *const_text = NULL;
  gboolean all_day;
  gchar *description;

  ECalComponentId *id;
  ECalComponentText e_summary;
  ECalComponentDateTime dtstart;
  ECalComponentDateTime dtend;

  priv = gcal_edit_dialog_get_instance_private (dialog);
  all_day = FALSE;

  priv->setting_event = TRUE;

  if (priv->source != NULL)
    g_clear_object (&(priv->source));
  priv->source = g_object_ref (data->source);

  if (priv->component != NULL)
    g_object_unref (priv->component);
  priv->component = e_cal_component_clone (data->event_component);

  if (priv->source_uid != NULL)
    g_free (priv->source_uid);
  priv->source_uid = g_strdup (e_source_get_uid (priv->source));

  id = e_cal_component_get_id (priv->component);

  if (priv->event_uid != NULL)
    g_free (priv->event_uid);
  priv->event_uid = g_strdup (id->uid);

  if (priv->event_rid != NULL)
    g_clear_pointer (&(priv->event_rid), g_free);
  if (id->rid != NULL)
    {
      priv->event_rid = g_strdup (id->rid);
    }

  e_cal_component_free_id (id);

  /* Clear event data */
  gcal_edit_dialog_clear_data (dialog);

  /* update sources list */
  if (priv->sources_menu != NULL)
    g_menu_remove_all (priv->sources_menu);

  fill_sources_menu (dialog);

  /* Load new event data */
  /* summary */
  e_cal_component_get_summary (priv->component, &e_summary);
  if (e_summary.value == NULL || g_strcmp0 (e_summary.value, "") == 0)
    gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), _("Unnamed event"));
  else
    gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), e_summary.value);

  /* dialog titlebar's title & subtitle */
  get_color_name_from_source (data->source, &color);

  pix = gcal_get_pixbuf_from_color (&color, 16);
  gtk_image_set_from_pixbuf (GTK_IMAGE (priv->source_image), pix);
  g_object_unref (pix);

  gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->titlebar),
                               e_source_get_display_name (data->source));

  /* retrieve start and end dates */
  e_cal_component_get_dtstart (priv->component, &dtstart);
  e_cal_component_get_dtend (priv->component, &dtend);

  /* check if it's an all-day event */
  all_day = (dtstart.value->is_date == 1 && (dtend.value != NULL ? dtend.value->is_date == 1 : FALSE));

  /* start date */
  gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->start_date_selector),
                               dtstart.value->day,
                               dtstart.value->month,
                               dtstart.value->year);

  /* start time */
  if (!all_day)
    {
      icaltimetype *date = gcal_dup_icaltime (dtstart.value);
      if (dtstart.tzid != NULL)
        {
          dtstart.value->zone =
            icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
        }
      *date = icaltime_convert_to_zone (*(dtstart.value),
                                        e_cal_util_get_system_timezone ());
      gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->start_time_selector),
                                   date->hour, date->minute);
      g_free (date);
    }

  /* end date */
  if (dtend.value != NULL)
    {
      gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->end_date_selector),
                                   dtend.value->day, dtend.value->month, dtend.value->year);

      if (!all_day)
        {
          icaltimetype *date = gcal_dup_icaltime (dtend.value);
          if (dtend.tzid != NULL)
            {
              dtend.value->zone =
                icaltimezone_get_builtin_timezone_from_tzid (dtend.tzid);
            }
          *date = icaltime_convert_to_zone (*(dtend.value),
                                            e_cal_util_get_system_timezone ());
          gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->end_time_selector),
                                       date->hour, date->minute);
          g_free (date);
        }
    }
  else
    {
      gcal_date_selector_set_date (GCAL_DATE_SELECTOR (priv->end_date_selector),
                                   dtstart.value->day, dtstart.value->month, dtstart.value->year);
      gcal_time_selector_set_time (GCAL_TIME_SELECTOR (priv->end_time_selector),
                                   dtstart.value->hour, dtstart.value->minute);
    }

  e_cal_component_free_datetime (&dtstart);
  e_cal_component_free_datetime (&dtend);

  /* all_day  */
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->all_day_check), all_day);


  /* location */
  e_cal_component_get_location (priv->component, &const_text);
  gtk_entry_set_text (GTK_ENTRY (priv->location_entry),
                      const_text != NULL ? const_text : "");

  /* notes */
  description = get_desc_from_component (priv->component, "\n");

  if (description != NULL)
    {
      gtk_text_buffer_set_text (
          gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->notes_text)),
          description,
          -1);

      g_free (description);
    }

  gcal_edit_dialog_set_writable (
      dialog,
      ! gcal_manager_is_client_writable (priv->manager,
                                         priv->source));

  priv->setting_event = FALSE;
}

void
gcal_edit_dialog_set_manager (GcalEditDialog *dialog,
                              GcalManager    *manager)
{
  GcalEditDialogPrivate *priv;

  g_return_if_fail (GCAL_IS_MANAGER (manager));
  priv = gcal_edit_dialog_get_instance_private (dialog);

  priv->manager = manager;
}

ECalComponent*
gcal_edit_dialog_get_component (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);
  g_object_ref (priv->component);
  return priv->component;
}

ESource*
gcal_edit_dialog_get_source (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);
  return priv->source;
}

const gchar*
gcal_edit_dialog_peek_source_uid (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);
  return priv->source_uid;
}

const gchar*
gcal_edit_dialog_peek_event_uid (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;

  priv = gcal_edit_dialog_get_instance_private (dialog);
  return priv->event_uid;
}

gchar*
gcal_edit_dialog_get_event_uuid (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;
  gchar *uuid;

  priv = gcal_edit_dialog_get_instance_private (dialog);

  if (priv->source_uid == NULL ||
      priv->event_uid == NULL)
    {
      return NULL;
    }

  if (priv->event_rid != NULL)
    {
      uuid = g_strdup_printf ("%s:%s:%s",
                              priv->source_uid,
                              priv->event_uid,
                              priv->event_rid);
    }
  else
    {
      uuid = g_strdup_printf ("%s:%s",
                              priv->source_uid,
                              priv->event_uid);
   }

  return uuid;
}

icaltimetype*
gcal_edit_dialog_get_start_date (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;
  icaltimetype *date;

  gint value1;
  gint value2;
  gint value3;

  priv = gcal_edit_dialog_get_instance_private (dialog);

  date = g_new0 (icaltimetype, 1);

  icaltime_set_timezone (date,
                         gcal_manager_get_system_timezone (priv->manager));

  gcal_date_selector_get_date (GCAL_DATE_SELECTOR (priv->start_date_selector),
                            &value1,
                            &value2,
                            &value3);
  date->day = value1;
  date->month = value2;
  date->year = value3;

  value1 = value2 = 0;
  gcal_time_selector_get_time (GCAL_TIME_SELECTOR (priv->start_time_selector),
                            &value1,
                            &value2);
  date->hour = value1;
  date->minute = value2;

  if (date->hour == 0 &&
      date->minute == 0)
    {
      date->is_date = 1;
    }

  return date;
}

icaltimetype*
gcal_edit_dialog_get_end_date (GcalEditDialog *dialog)
{
  GcalEditDialogPrivate *priv;
  icaltimetype *date;

  gint value1;
  gint value2;
  gint value3;

  priv = gcal_edit_dialog_get_instance_private (dialog);

  date = g_new0 (icaltimetype, 1);

  icaltime_set_timezone (date,
                         gcal_manager_get_system_timezone (priv->manager));

  gcal_date_selector_get_date (GCAL_DATE_SELECTOR (priv->end_date_selector),
                            &value1,
                            &value2,
                            &value3);
  date->day = value1;
  date->month = value2;
  date->year = value3;

  value1 = value2 = 0;
  gcal_time_selector_get_time (GCAL_TIME_SELECTOR (priv->end_time_selector),
                            &value1,
                            &value2);
  date->hour = value1;
  date->minute = value2;

  if (date->hour == 0 &&
      date->minute == 0)
    {
      date->is_date = 1;
    }

  return date;
}
