/* a SkipStone plugin to load favicons from sites if available */
/* Maher <muhri@muhri.net> */
#include "../../src/skipstone.h"
#include <gdk-pixbuf/gdk-pixbuf.h>

#define PLUGIN_NAME "FavIcon"
#define DEFAULT_FAVICON "mini_link.png"

typedef struct _FavIconP FavIconP;

static gint tryHarder = 0;

static void create_plugin(SkipStone *skipstone);
static void plugin_destroyed(GtkWidget *w, FavIconP *fip);

static void fetch_end(GtkWidget *w, FavIconP *fip);
static void fetch_start(GtkWidget *w, FavIconP *fip);
static void favicon_refetch(GtkWidget *widget, FavIconP *fip);
static void favicon_select(GtkWidget *widget, FavIconP *fip);
static void on_file_selected(GtkWidget *button, GtkWidget *fs);
static void save_favicon_config(GtkWidget *my_widget);
static void disconnect(GtkWidget *widget, FavIconP *fip);

static gchar *get_domain(gchar *url);

static gint icon_exists(gchar *filename);
static gint timeout(FavIconP *fip);
static gint on_button_press(GtkWidget *widget, GdkEventButton *ev, FavIconP *fip);

static GtkWidget *get_favicon(gchar  *path);
static GtkWidget *create_favicon_config(void);

extern gboolean GetFavIconURL(GtkMozEmbed *b, gchar **favicon_url);


static SkipStonePlugin plugin =
{
	  PLUGIN_NAME, /* plugin name */
	  PLUGIN_IN_TOOLBAR_BEFORE_ENTRY,
	  create_plugin,/* create plugin function */
       	  create_favicon_config, /*create config*/
       	  save_favicon_config, /*save config*/
	  1 /* do we create this plugin for each window */
};

struct _FavIconP {
   GtkWidget *evbox;
   GtkWidget *pixmap;
   GtkWidget *menu;
   SkipStone *ss;
   gchar *path;
   gint is_set;
   gint timer;
};


static GtkWidget *create_favicon_config(void)
{
   GtkWidget *retval = NULL;
   GtkWidget *checkButton = NULL;
   
   retval = gtk_vbox_new(FALSE, 5);
   checkButton = gtk_check_button_new_with_label("Try to fetch Favicons even if SHORTCUT url\n was not found in HTML source?");
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkButton), tryHarder);
   gtk_box_pack_start(GTK_BOX(retval), checkButton, FALSE, FALSE, 0);
   gtk_object_set_data(GTK_OBJECT(retval), "checkButton", checkButton);
   return retval;
}

static void save_favicon_config(GtkWidget *my_widget)
{
   GtkWidget *checkButton = NULL;
   g_return_if_fail(my_widget != NULL);
   checkButton  = gtk_object_get_data(GTK_OBJECT(my_widget), "checkButton");
   g_return_if_fail(checkButton != NULL);
   tryHarder = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkButton));
   skipstone_set_config_value_as_int(PLUGIN_NAME, "tryHarder", tryHarder);
   return;
}


static void fetch_start(GtkWidget *w, FavIconP *fip)  
{
   gchar *location, *domain, *saveto;
   GtkWidget *pixmap;
   gint loaded;
     
   if (!fip) return;
   if (!fip->ss->embed) return;
   if (!GTK_IS_MOZ_EMBED(fip->ss->embed)) return;

   location = gtk_moz_embed_get_location(fip->ss->embed);

   if (location == NULL) {
	return;
   }
   domain = get_domain(location);

   saveto = g_strdup_printf("%s/.skipstone/favicons/favico-%s",g_get_home_dir(),domain);
   g_free(location);
     
   if (fip->timer) {
      gtk_timeout_remove(fip->timer);
      fip->timer=0;
   }
   
   
   if (icon_exists(saveto)) 
     {
	pixmap = get_favicon(saveto);
	if (!pixmap) 
	  {
	     fip->is_set=0;
	     g_free(domain);
	     g_free(saveto);
	     return;
	  }
	
	if (fip->pixmap) {
	     gtk_widget_destroy(fip->pixmap);
	}
	gtk_widget_show(pixmap);
	fip->pixmap=pixmap;
	gtk_container_add(GTK_CONTAINER(fip->evbox), fip->pixmap);
	g_free(saveto);
	g_free(domain);
	fip->is_set = 1;
	return;
     }
   
   fip->is_set=0;
   gtk_widget_destroy(fip->pixmap);
   fip->pixmap = create_pixmap(fip->evbox,DEFAULT_FAVICON,&loaded);
   gtk_container_add(GTK_CONTAINER(fip->evbox), fip->pixmap);
   gtk_widget_show(fip->pixmap);
   
   g_free(saveto);
   g_free(domain);

}

	    

static void fetch_end(GtkWidget *w, FavIconP *fip)
{
   gchar *location = NULL;
   gchar *domain = NULL;
   gchar *favico = NULL;   
   gchar *saveto = NULL;
   GtkWidget *pixmap = NULL;

   if (!fip) return;
   if (fip->is_set) return;
   if (!GTK_IS_MOZ_EMBED(fip->ss->embed)) return;
   location = gtk_moz_embed_get_location(fip->ss->embed);
   if (location == NULL) return;
   domain = get_domain(location);
   g_free(location);
   GetFavIconURL(fip->ss->embed, &favico);
   /* No FavIcon but try the top site if the config value is set to 1*/
   if (!favico && tryHarder) 
     {
	gchar *absolute = g_strdup_printf("http://%s/favicon.ico",domain);
	saveto = g_strdup_printf("%s/.skipstone/favicons/favico-%s",g_get_home_dir(),domain);
	mozilla_save_image(fip->ss->embed, absolute, saveto);
	if (!icon_exists(saveto)) {
	   fip->path=saveto;
	   fip->timer=gtk_timeout_add(100, (GSourceFunc)timeout, fip);
	   g_free(domain);
	   g_free(absolute);
	   return;
	}
     }
   
   if (!favico) 
     {
	/*g_print("No favico for %s\n",domain);*/
	g_free(domain);
	return;
     }
   
   saveto = g_strdup_printf("%s/.skipstone/favicons/favico-%s",g_get_home_dir(),domain);
   
   /*   g_print("signal fetch location %s and domain %s favico should be at %s\n and will be saved to %s\n",location, domain, favico,saveto); */

   if (!icon_exists(saveto)) {
      mozilla_save_image(fip->ss->embed, favico, saveto);
      fip->path=saveto;
      fip->timer=gtk_timeout_add(100, (GSourceFunc)timeout, fip);
      g_free(domain);
      g_free(favico);
      return;
   }
   
   pixmap = get_favicon(saveto);
   
   if (!pixmap)
     {
	g_free(domain);
	g_free(saveto);
	g_free(favico);
	return;
     }
   
   if (fip->pixmap)  {
	gtk_widget_destroy(fip->pixmap);
   }
   
   gtk_widget_show(pixmap);
   fip->pixmap=pixmap;
   gtk_container_add(GTK_CONTAINER(fip->evbox), fip->pixmap);
   
   g_free(domain);
   g_free(favico);
   g_free(saveto);
   
}

static gint timeout(FavIconP *fip)
{
   GtkWidget *pixmap=NULL;
   if (!fip || !GTK_IS_WIDGET(fip->evbox)) return FALSE;

   if (icon_exists(fip->path)) 
     {
	pixmap = get_favicon(fip->path);
	if (!pixmap) return TRUE;
	if (fip->pixmap) {
	     gtk_widget_destroy(fip->pixmap);
	}
	gtk_widget_show(pixmap);
	fip->pixmap=pixmap;
	gtk_container_add(GTK_CONTAINER(fip->evbox), fip->pixmap);
	fip->is_set=1;
	g_free(fip->path);
	return FALSE;
     }
   return TRUE;
}


static GtkWidget *get_favicon(gchar *path)
{
   GdkPixbuf *pixbuf = NULL;
   GtkWidget *pixmap=NULL;
   GdkPixmap *gdkpixmap=NULL;
   GdkBitmap *mask=NULL;
   pixbuf = gdk_pixbuf_new_from_file(path, NULL);
   if (!pixbuf) return NULL;
   
   if (gdk_pixbuf_get_width(pixbuf) > 16 || gdk_pixbuf_get_height(pixbuf) > 16) 
     {
	GdkPixbuf *p =  gdk_pixbuf_scale_simple (pixbuf,16, 16, GDK_INTERP_NEAREST);
	gdk_pixbuf_unref(pixbuf);
	pixbuf=p;
     }
   
	
	
   gdk_pixbuf_render_pixmap_and_mask(pixbuf, &gdkpixmap, &mask, 100);
   if (gdkpixmap == NULL) 
     {
	gdk_pixbuf_unref(pixbuf);
	return NULL;
     }
   
   pixmap = gtk_pixmap_new(gdkpixmap, mask);
   if (!pixmap) 
     {
	gdk_pixbuf_unref(pixbuf);	
	return NULL;
     }
   
   gdk_pixbuf_unref(pixbuf);
   return pixmap;
}

static void create_plugin(SkipStone *skipstone)
{
   FavIconP *fip;
   gint loaded;
     
   fip = g_new0(FavIconP, 1);
   fip->menu=NULL;
   fip->is_set=0;
   fip->evbox = gtk_event_box_new();
   gtk_widget_set_events(fip->evbox, GDK_BUTTON_PRESS_MASK);
   fip->path=NULL;
   gtk_container_set_border_width(GTK_CONTAINER(fip->evbox), 4);
   fip->pixmap = create_pixmap(fip->evbox, DEFAULT_FAVICON,&loaded);
   fip->ss = skipstone;
   gtk_container_add(GTK_CONTAINER(fip->evbox), fip->pixmap);
   gtk_widget_show_all(fip->evbox);
   skipstone_add_plugin_to_toolbar(skipstone, fip->evbox, "Displays site's favicon if available, right click to force a certain favicon or to refetch a favicon");

   gtk_signal_connect(GTK_OBJECT(fip->evbox), "destroy", GTK_SIGNAL_FUNC(plugin_destroyed), fip);
   gtk_signal_connect_while_alive(GTK_OBJECT(fip->ss->embed), "unrealize", GTK_SIGNAL_FUNC(disconnect), fip, GTK_OBJECT(fip->evbox));
   gtk_signal_connect_while_alive(GTK_OBJECT(fip->ss->embed), "location", GTK_SIGNAL_FUNC(fetch_start), fip, GTK_OBJECT(fip->evbox));
   gtk_signal_connect_while_alive(GTK_OBJECT(fip->ss->embed), "net_stop", GTK_SIGNAL_FUNC(fetch_end), fip, GTK_OBJECT(fip->evbox));
     
   gtk_signal_connect(GTK_OBJECT(fip->evbox), "button_press_event", GTK_SIGNAL_FUNC(on_button_press), fip);
}

static void plugin_destroyed(GtkWidget *widget, FavIconP *fip)
{

     if (fip->timer) 
	  gtk_timeout_remove(fip->timer);
     fip->timer=0;
     g_free(fip);
     fip = NULL;

}

static void disconnect(GtkWidget *widget, FavIconP *fip)
{

   g_return_if_fail(fip != NULL); 
   g_return_if_fail(fip->ss->embed != NULL);

    gtk_signal_disconnect_by_func(GTK_OBJECT(fip->ss->embed), GTK_SIGNAL_FUNC(fetch_start), fip);
    gtk_signal_disconnect_by_func(GTK_OBJECT(fip->ss->embed), GTK_SIGNAL_FUNC(fetch_end), fip);

}


static gint icon_exists(gchar *filename)
{
   struct stat s;
   gint status;

   status = stat (filename, &s);
   if (status == 0 && S_ISREG(s.st_mode)) 
     return 1;
   return 0;
}

static gchar *get_domain(gchar *url)
{
     const gchar *stuff[] = {"ftp://","http://www.","http://","https://"};
     gint i;
     gchar *string = g_strdup(url);
     gchar *s = string;
     gchar *a = NULL;
     gchar *ret = NULL;
     g_return_val_if_fail(url != NULL, NULL);
     for (i = 0; i < 4; i++) {
	  if (g_strncasecmp(stuff[i],url,strlen(stuff[i])) == 0) {
	       s += strlen(stuff[i]);
	       break;		
	  }
	   	
     }
     ret = s;
     a = strchr(s,'/');
     if (a) 
	  *a = '\0'; /* end it here */	   	
     
     
          
     if (s) 
	  ret = g_strdup(s);
     else 
	  ret = g_strdup(ret);
     
     
     g_free(string);
	    
     return ret;
}

static gint on_button_press(GtkWidget *widget, GdkEventButton *ev, FavIconP *fip)
{
   GtkWidget *m1, *m2;
   if (ev->button != 3) return FALSE;
   if (fip->menu) {
	gtk_widget_destroy(fip->menu);
   }
   fip->menu = gtk_menu_new();
   m1 = gtk_menu_item_new_with_label("Refetch FavIcon");
   m2 = gtk_menu_item_new_with_label("Force a FavIcon");
   gtk_signal_connect(GTK_OBJECT(m1), "activate",
		      GTK_SIGNAL_FUNC(favicon_refetch), fip);
   gtk_signal_connect(GTK_OBJECT(m2), "activate",
		      GTK_SIGNAL_FUNC(favicon_select), fip);
   gtk_menu_append(GTK_MENU(fip->menu), m1);
   gtk_menu_append(GTK_MENU(fip->menu), m2);
   gtk_widget_show_all(fip->menu);
   gtk_menu_popup(GTK_MENU(fip->menu), NULL, NULL, NULL, NULL, GDK_BUTTON3_MASK, ev->time);
   return TRUE;
}


static void favicon_refetch(GtkWidget *widget, FavIconP *fip)
{
   gchar *location, *domain, *path;
   location = gtk_moz_embed_get_location(fip->ss->embed);
   if (location == NULL) return;
   domain = get_domain(location);
   g_free(location);
   path = g_strdup_printf("%s/.skipstone/favicons/favico-%s",g_get_home_dir(),domain);
   unlink(path);
   g_free(path);
   g_free(domain);
   gtk_moz_embed_reload(fip->ss->embed, 0);
}

static void favicon_select(GtkWidget *widget, FavIconP *fip)
{
   GtkWidget *fs;
   fs = gtk_file_selection_new("Select FavIcon Image:");
   gtk_object_set_data(GTK_OBJECT(fs), "fip", fip);
   gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button),
		      "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(fs));
   gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), 
		      "clicked", GTK_SIGNAL_FUNC(on_file_selected), fs);
   gtk_widget_show(fs);
   
}

static void on_file_selected(GtkWidget *button, GtkWidget *fs)
{
   FavIconP *fip;
   gchar *location, *domain, *path;
   G_CONST_RETURN gchar *fname;
     GdkPixbuf *test;

     fip = gtk_object_get_data(GTK_OBJECT(fs), "fip");
   g_return_if_fail(fip != NULL);
   fname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs));
   if (!icon_exists((gchar *)fname)) 
     {
	skipstone_error_dialog("Error", "Please choose a file, not a directory", TRUE);
	return;
     }
   
   test = gdk_pixbuf_new_from_file(fname, NULL);
   if (!test) 
     {
	skipstone_error_dialog("Error", "File chosen is not an image file or corrupted", TRUE);
	return;
     } else 
     	gdk_pixbuf_unref(test);
     
   location = gtk_moz_embed_get_location(fip->ss->embed);
   domain = get_domain(location);
   g_free(location);
   path = g_strdup_printf("%s/.skipstone/favicons/favico-%s",g_get_home_dir(),domain);

   unlink(path);
   
   if (symlink(fname,path) > 1)
     {
	gchar *error = g_strdup_printf("Could not symlink %s to %s",fname, path);
	skipstone_error_dialog("Error",error, TRUE);
	g_free(path);
	g_free(domain);
	return;
     }
   g_free(path);
   g_free(domain);
   gtk_widget_destroy(fs);  
   gtk_moz_embed_reload(fip->ss->embed, 0);
}



SkipStonePlugin *init_plugin(void)
{
   gchar *dir = g_strconcat(g_get_home_dir(), "/.skipstone/favicons", NULL);
   if ((tryHarder = skipstone_get_config_value_as_int(PLUGIN_NAME, "tryHarder"))  == -1)
     {
	tryHarder = 0;
	skipstone_set_config_value_as_int(PLUGIN_NAME, "tryHarder", tryHarder);
     }
   
   mkdir(dir, 0755);
   g_free(dir);
   return &plugin;
}

