
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include <libintl.h>
#include <fam.h>
#include <libnotify/notify.h>
#include <slapt.h>

#define _(x) gettext(x)
#define TIMEOUT_FAM 1000*5 /* 5 sec */
#define UPDATE_NOTIFICATION_FILE "pending_updates"
#define NOTIFICATION_DEFAULT "default"
#define NOTIFICATION_IGNORE "ignore"
#define NOTIFICATION_SHOW_UPDATES "show updates"
#define SUN_UPDATE_ICON "/usr/share/pixmaps/slapt-update-notifier/slapt-update-notifier-update.png"

struct slapt_update_notifier {
  GtkStatusIcon *tray_icon;
  GtkWidget *tray_icon_image;
  GdkPixbuf *pixbuf;
  FAMConnection fc;
  FAMRequest frq;
  GtkWidget *menu;
};

static char *working_dir = NULL;
static struct slapt_update_notifier *sun = NULL;
static void hide_sun (void);

static void run_gslapt (const char *action)
{
  gchar *argv[4];
#if defined(HAS_GNOMESU)
  argv[0] = "/usr/bin/gnomesu";
  argv[1] = "-c";
  if ( strcmp(action,"upgrade") == 0 ) {
    argv[2] = "/usr/sbin/gslapt --upgrade";
  } else {
    argv[2] = "/usr/sbin/gslapt";
  }
  argv[3] = NULL;
#elif defined(HAS_GKSU)
  argv[0] = "/usr/bin/gksu";
  if ( strcmp(action,"upgrade") == 0 ) {
    argv[1] = "/usr/sbin/gslapt --upgrade";
  } else {
    argv[1] = "/usr/sbin/gslapt";
  }
  argv[2] = NULL;
#elif defined(HAS_KDESU)
  argv[0] = "/usr/bin/kdesu";
  if ( strcmp(action,"upgrade") == 0 ) {
    argv[1] = "/usr/sbin/gslapt --upgrade";
  } else {
    argv[1] = "/usr/sbin/gslapt";
  }
  argv[2] = NULL;
#else
  #error unable to create command to run gslapt
#endif

  g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, NULL);
  hide_sun();
}

void tray_clicked (GtkStatusIcon *status_icon, gpointer data)
{
  run_gslapt("upgrade");
}

void tray_menu (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer data)
{
  gtk_menu_popup(
    GTK_MENU(sun->menu),
    NULL,NULL,NULL,NULL,
    button,activate_time
  );
}

static void notify_callback(NotifyNotification *handle, const char *action, void *user_data)
{
  GMainLoop *loop = (GMainLoop*)user_data;

  if ( strcmp(action,NOTIFICATION_SHOW_UPDATES) == 0 ) {
    gtk_widget_hide_all(GTK_WIDGET(sun->tray_icon));
    run_gslapt("upgrade");
  }

  if (handle != NULL) {
    GError *error = NULL;
    if (notify_notification_close(handle, &error) != TRUE) {
      fprintf(stderr, "failed to send notification: %s\n", error->message);
      g_error_free (error);
    }
  }

  if (loop != NULL)
    g_main_loop_quit(loop);

}

gboolean show_notification (gpointer data)
{
  NotifyNotification *n;

  GMainLoop *loop;
  loop = g_main_loop_new(NULL, FALSE);

  n = notify_notification_new (
    _("New updates available"),
    _("Click on the update icon to see the available updates"),
    "info", NULL
  );

  /* notify_notification_set_timeout(n, NOTIFY_EXPIRES_NEVER); */
  notify_notification_add_action(n, NOTIFICATION_DEFAULT, "default",
    NOTIFY_ACTION_CALLBACK(notify_callback), loop, NULL
  );

  GError *error = NULL;
  if (notify_notification_show(n, &error) != TRUE)
  {
    fprintf(stderr, "failed to send notification: %s\n", error->message);
    g_error_free (error);
  }
  g_object_set_data(G_OBJECT(sun->tray_icon), "notification", n);
  g_object_set_data(G_OBJECT(sun->tray_icon), "notification_loop", loop);

  return FALSE;
}

static gboolean fam_check()
{

  while(FAMPending(&sun->fc)) {
    FAMEvent fe;

    if (FAMNextEvent(&sun->fc,&fe) < 0) {
      fprintf(stderr,"FAMNextEvent failed\n");
      break;
    }

    if (fe.code == FAMChanged) {

      if (strcmp(fe.filename,UPDATE_NOTIFICATION_FILE) == 0) {
        if ( ! gtk_status_icon_is_embedded (sun->tray_icon) ) {
          gtk_status_icon_set_visible(sun->tray_icon, TRUE);
          g_timeout_add(5000,show_notification,sun);
        }
      }

    /* if the notification file goes away, hide */
    } else if (fe.code == FAMDeleted) {

      if (strcmp(fe.filename,UPDATE_NOTIFICATION_FILE) == 0) {
        if ( gtk_status_icon_is_embedded (sun->tray_icon) ) {
          hide_sun();
        }
      }

    }

  }

  return TRUE;
}

static int fam_init()
{
  if (FAMOpen(&sun->fc) == -1) {
    fprintf(stderr,"Failed to open fam connection\n");
    if (errno)
      perror("FAMOpen");
    return -1;
  }

  if (FAMMonitorDirectory(&sun->fc,working_dir,&sun->frq,(void *)sun) == -1) {
    fprintf(stderr,"Failed to monitor directory\n");
    if (errno)
      perror("FAMMonitorDirectory");
    return -1;
  }

  return 0;
}

void tray_destroy ()
{

  if (sun->pixbuf)
    g_object_unref(G_OBJECT(sun->pixbuf));

  gtk_widget_destroy(sun->tray_icon_image);
  gtk_widget_destroy(GTK_WIDGET(sun->tray_icon));
}

static void hide_sun (void)
{
  NotifyNotification *n = g_object_get_data(G_OBJECT(sun->tray_icon),"notification");
  GMainLoop *loop = g_object_get_data(G_OBJECT(sun->tray_icon), "notification_loop");

  gtk_status_icon_set_visible(sun->tray_icon, FALSE);

  if ( n != NULL ) {
    GError *error = NULL;
    if (notify_notification_close(n, &error) != TRUE) {
      fprintf(stderr, "failed to send notification: %s\n", error->message);
      g_error_free (error);
    }
  }
  
  if ( loop != NULL )
    g_main_loop_quit(loop);

}

void menuitem_hide_callback (GObject *g, void *data)
{
  hide_sun();
}

int main (int argc, char *argv[])
{
  slapt_rc_config *config = slapt_read_rc_config("/etc/slapt-get/slapt-getrc");
  GtkWidget *menuitem = NULL;

#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);
#endif

  gtk_init (&argc, &argv);

  notify_init("slapt-update-notifier");

  working_dir = strdup(config->working_dir);

  sun = malloc(sizeof *sun);

  sun->tray_icon        = gtk_status_icon_new ();
  sun->pixbuf           = gdk_pixbuf_new_from_file(SUN_UPDATE_ICON, NULL);
  sun->tray_icon_image  = gtk_image_new_from_pixbuf(sun->pixbuf);
  gtk_status_icon_set_from_pixbuf (sun->tray_icon, sun->pixbuf);
  gtk_status_icon_set_tooltip (sun->tray_icon, _("Updates available"));
  gtk_status_icon_set_visible(sun->tray_icon, FALSE);

  g_signal_connect(G_OBJECT(sun->tray_icon), "activate", G_CALLBACK(tray_clicked), &sun);
  g_signal_connect(G_OBJECT(sun->tray_icon), "popup-menu", G_CALLBACK(tray_menu), &sun);

  sun->menu = gtk_menu_new();
  menuitem = gtk_menu_item_new_with_label(_("Hide"));
  gtk_menu_shell_append(GTK_MENU_SHELL(sun->menu),menuitem);
  g_signal_connect(G_OBJECT(menuitem),"activate",G_CALLBACK(menuitem_hide_callback),sun);
  gtk_widget_show_all(sun->menu);

  if (fam_init(sun) == -1) {
    fprintf(stderr,"Failed to initialize fam\n");
    return -1;
  }

  g_timeout_add(TIMEOUT_FAM,(GSourceFunc)fam_check,sun);

  gtk_main();

  tray_destroy(sun);

  free(sun);

  return 0;
}

