/* an example of a history side bar plugin for SkipStone
 * Maher <muhri@muhri.net> */

#include "../../src/skipstone.h"

typedef struct
{
	gchar *url;
	gchar *domain;
	GNode *node;
}DomainData;

typedef struct
{
	GtkWidget *clist;
	GtkWidget *paned;
	GtkWidget *sync;
	SkipStone *ss;
	GtkWidget *vbox;
	gint compacted;
     	GtkCTreeNode *cnode;
	
}SideBar;

static gint lists_made = 0, nodes_made = 0, state_shown = 1;
static GSList *net = NULL, *com = NULL, *org = NULL, *others = NULL, 
       *domains = NULL, *history, *instances = NULL;
static GNode *comroot = NULL, *orgroot = NULL, *othersroot = NULL, *netroot = NULL,
       *root;

static void create_plugin(SkipStone *skipstone);
static GtkWidget *create_config(void);
static void save_config(GtkWidget *my_widget);
static gboolean on_history_clist_button_press_event(GtkWidget *, GdkEventButton *, SkipStone *);
static void load_from_popup(GtkWidget *w, SkipStone *skipstone);
static GtkWidget *popup_menu(GtkWidget *clist, SkipStone *skipstone, gchar *location);
static void on_plugin_destroy(GtkWidget *, SideBar *);
static void resync_clist(GtkWidget *, SideBar *);
static void insert_in_clist(SideBar *);
static gboolean on_paned_button_press(GtkWidget *, GdkEventButton *,SideBar *);
static gchar *get_domain(gchar *url);
static void add_domain(gchar *url, gchar *domain);
static void make_lists(SideBar *sb);
static void make_nodes(SideBar *sb);
static gboolean NodeFunc(GtkCTree *ctree, guint depth, GNode *node,
			 GtkCTreeNode *cnode, gpointer data);
static void NodeFree(GNode *node, gpointer data);
static void NodeInsert(GNode *node, SideBar *sb);
static void make_root_ctree_nodes(SideBar *sb);

/* convienince macros & definitions */

#define PLUGIN_NAME "HistorySideBar"

static SkipStonePlugin plugin =
{
	  PLUGIN_NAME, /* plugin name */
	  PLUGIN_LEFT_OF_MOZEMBED, /* position left of embedding widget */
	  create_plugin,/* create plugin function */
       	  create_config,
       	  save_config,
	  1 /* do we create this plugin for each window */
};

static void add_domain(gchar *url, gchar *domain)
{
	GSList *l;
	GNode *has_node = NULL;
     	DomainData *dd;	
	
     	for (l = domains; l ; l=l->next) {
		DomainData *dd = (DomainData *)l->data;
		  if (!strcmp(domain,dd->domain)) {		    
			  has_node = dd->node;
			  break;
		  }
	}
	
     	dd = g_new0(DomainData,1);
     	dd->url = url;
     	dd->domain = domain;
     	dd->node = g_node_new(dd);
     	
     	if (has_node != NULL) {
		g_node_insert(has_node,-1,dd->node);
	} else {
	     	g_node_insert(root,-1,dd->node);
		domains = g_slist_append(domains,dd);
	}
     
	return;
}

static void save_config(GtkWidget *my_widget)
{

     GtkWidget *shown;
     g_return_if_fail( my_widget != NULL );
     shown=gtk_object_get_data(GTK_OBJECT(my_widget), "state");
     state_shown=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shown));
     skipstone_set_config_value_as_int(PLUGIN_NAME,"state",state_shown);
}

static GtkWidget *create_config(void)
{
     GtkWidget *retval, *shown;
     shown = gtk_check_button_new_with_label("Show SideBar?");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shown),
				  skipstone_get_config_value_as_int
				  (PLUGIN_NAME,"state"));
     retval=gtk_vbox_new(FALSE,5);
     gtk_object_set_data(GTK_OBJECT(retval), "state", shown);
     gtk_box_pack_start(GTK_BOX(retval), shown, FALSE, FALSE, 0);
     gtk_widget_show_all(retval);
     return retval;
}

static gboolean NodeFunc(GtkCTree *ctree, guint depth, GNode *node,
			 GtkCTreeNode *cnode, gpointer data)
{
	DomainData *dd;
	gchar *text = NULL;
	dd = (DomainData *)node->data;
	if (node->children == NULL) 
	  text = dd->url;
	else
	  text = dd->domain;
	gtk_ctree_set_node_info(ctree,cnode,text,5,NULL,NULL,NULL,NULL,FALSE,FALSE);
	gtk_ctree_node_set_row_data(ctree,cnode,text);
	return TRUE;
}


static gboolean on_paned_button_press(GtkWidget *w, GdkEventButton *event, SideBar *sb)
{
	g_return_val_if_fail(sb != NULL, FALSE);
	if (event->button == 2) {
		if (!sb->compacted) {
			sb->compacted = 1;
			gtk_widget_hide(sb->vbox);
			gtk_widget_set_usize(GTK_WIDGET(sb->paned),5,5);
			skipstone_queue_resize(sb->ss);
			state_shown = 0;
		} else {
			sb->compacted = 0;
			gtk_widget_show(sb->vbox);
			gtk_widget_set_usize(GTK_WIDGET(sb->paned),200,200);
			skipstone_queue_resize(sb->ss);
			state_shown = 1;
		}
		skipstone_set_config_value_as_int(PLUGIN_NAME,"state",state_shown);
		return TRUE;
	} else {
		return FALSE;
	}
}				  		 	  

static void create_plugin(SkipStone *skipstone)
{
	GtkWidget *sw;
	SideBar *sb;	
	
	sb = g_new0(SideBar,1);
	sb->compacted  = 0;
	sb->ss = skipstone;
	sb->clist = gtk_ctree_new(1,0);
	gtk_signal_connect(GTK_OBJECT(sb->clist), "destroy",
			   GTK_SIGNAL_FUNC(on_plugin_destroy),
			   sb);
	gtk_signal_connect(GTK_OBJECT(sb->clist), "button_press_event",
			   GTK_SIGNAL_FUNC(on_history_clist_button_press_event),
			   skipstone);
	sb->vbox = gtk_vbox_new(FALSE,0);
	if (history == NULL) history = skipstone_get_history();
	insert_in_clist(sb);
	sw = gtk_scrolled_window_new(NULL,NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), 
				       GTK_POLICY_AUTOMATIC,
				       GTK_POLICY_AUTOMATIC);
	gtk_container_add(GTK_CONTAINER(sw),sb->clist);
	gtk_box_pack_start(GTK_BOX(sb->vbox),sw,TRUE,TRUE,0);
	sb->sync = gtk_button_new_with_label("Sync");
	gtk_signal_connect(GTK_OBJECT(sb->sync), "clicked",
			   GTK_SIGNAL_FUNC(resync_clist),
			   sb);
	gtk_box_pack_start(GTK_BOX(sb->vbox), sb->sync, FALSE, FALSE, 0);
	sb->paned = gtk_hpaned_new();
	gtk_signal_connect(GTK_OBJECT(sb->paned), "button_press_event",
			   GTK_SIGNAL_FUNC(on_paned_button_press),
			   sb);	
	gtk_paned_pack1(GTK_PANED(sb->paned), sb->vbox, TRUE, TRUE);

	
	if (state_shown) {
		gtk_widget_set_usize(sb->paned,200,200);
		gtk_widget_show_all(sb->paned);		
	}  else {
		gtk_widget_set_usize(sb->paned,5,5);
		gtk_widget_show(sb->paned);
		sb->compacted = 1;
	}
	
	skipstone_pack_plugin(skipstone,sb->paned);
	instances = g_slist_append(instances, sb);
	return;
}

static void insert_in_clist(SideBar *sb)
{
	

	if (!lists_made) {
		make_lists(sb);
		lists_made = 1;
	} 
	
	if (!nodes_made) {
		make_nodes(sb);
		nodes_made = 1;
	}
     	
     	make_root_ctree_nodes(sb);
	
}

static void make_lists(SideBar *sb)
{
	GSList *l;
	for (l = history; l; l = l->next) {
		gchar *tmp;
		gchar *url;
		
		url = (gchar *)l->data;
		
		if ((tmp = strstr((gchar *)url,".com")) != NULL) 
		  com = g_slist_append(com,url);
		else if ((tmp = strstr((gchar *)url,".net")) != NULL)
		  net = g_slist_append(net,url);
		else if ((tmp = strstr((gchar *)url,".org")) != NULL)
		  org = g_slist_append(org,url);
		else
		  others = g_slist_append(others,url);
		while (gtk_events_pending())
			  gtk_main_iteration();
	}
	
}

static void NodeInsert(GNode *node, SideBar *sb)
{
    if (!node || node == NULL) return;
     gtk_ctree_insert_gnode(GTK_CTREE(sb->clist),sb->cnode,NULL,node,(GtkCTreeGNodeFunc)NodeFunc,NULL);
}

static void make_root_ctree_nodes(SideBar *sb)
{
	gchar *text[1];	
	GtkCTreeNode *netnode,*orgnode,*othersnode,*comnode;

	g_return_if_fail(sb->clist != NULL);
	
	gtk_clist_freeze(GTK_CLIST(sb->clist));
	gtk_clist_clear(GTK_CLIST(sb->clist));	
	
	text[0] = "DotCom";
	comnode=gtk_ctree_insert_node(GTK_CTREE(sb->clist),NULL,NULL,text,5,NULL,NULL,NULL,NULL,FALSE,FALSE);
     	gtk_ctree_node_set_selectable(GTK_CTREE(sb->clist),comnode,FALSE);
     	sb->cnode = comnode;
	g_node_children_foreach(comroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeInsert,sb);
     	
     	text[0]="DotNet";
	netnode=gtk_ctree_insert_node(GTK_CTREE(sb->clist),NULL,NULL,text,5,NULL,NULL,NULL,NULL,FALSE,FALSE);
     	gtk_ctree_node_set_selectable(GTK_CTREE(sb->clist),netnode,FALSE);
	sb->cnode = netnode;
     	g_node_children_foreach(netroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeInsert,sb);
     
     	text[0]="DotOrg";     
	orgnode=gtk_ctree_insert_node(GTK_CTREE(sb->clist),NULL,NULL,text,5,NULL,NULL,NULL,NULL,FALSE,FALSE);
     	gtk_ctree_node_set_selectable(GTK_CTREE(sb->clist),orgnode,FALSE);
	sb->cnode = orgnode;
	g_node_children_foreach(orgroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeInsert,sb);
     
     	text[0]="Others";
	othersnode=gtk_ctree_insert_node(GTK_CTREE(sb->clist),NULL,NULL,text,5,NULL,NULL,NULL,NULL,FALSE,FALSE);
     	gtk_ctree_node_set_selectable(GTK_CTREE(sb->clist),othersnode,FALSE);
	sb->cnode = othersnode;
     	g_node_children_foreach(othersroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeInsert,sb);     
	
     	gtk_ctree_sort_recursive(GTK_CTREE(sb->clist), NULL);
	gtk_clist_thaw(GTK_CLIST(sb->clist));
}

static void make_nodes(SideBar *sb)
{
	GSList *l;

     	comroot = g_node_new(NULL);
	root = comroot;
	
	for (l = com; l ; l = l->next) {
		gchar *domain = NULL;
	     	domain = get_domain(l->data);
		if (domain) add_domain(l->data,domain);
	}
	

	g_slist_free(domains);
	domains = NULL;
     
     	netroot = g_node_new(NULL);
	root = netroot;
	
	for (l = net; l ; l=l->next) {
		gchar *domain = NULL;
	     	domain = get_domain(l->data);
		if (domain) add_domain(l->data,domain);

	}
	
	g_slist_free(domains);
	domains = NULL;
	
	
     	orgroot = g_node_new(NULL);
	root = orgroot;
	
	for (l = org; l; l=l->next) {
		gchar *domain = NULL;
	     	domain = get_domain(l->data);
		if (domain) add_domain(l->data,domain);
	}       	
	
	g_slist_free(domains);
	domains = NULL;
	
	othersroot = g_node_new(NULL);
	root = othersroot;
	for (l = others; l; l=l->next) {
		gchar *domain = NULL;
	     	domain = get_domain(l->data);
		if (domain) add_domain(l->data,domain);

	}       
	
     	g_slist_free(domains);
	domains = NULL;
}


static gchar *get_domain(gchar *url)
{
     const gchar *stuff[] = {"ftp://","http://","https://"};
     gint i;
     gchar *string = g_strdup(url);
     gchar *s = string;
     gchar *a = NULL;
     gchar *b = NULL;
     gchar *c = NULL;
     gchar *ret = NULL;
     for (i = 0; i < 3; i++) {
	  if (g_strncasecmp(stuff[i],url,strlen(stuff[i])) == 0) {
	       s += strlen(stuff[i]);
	        		
	  }
	   	
     }
     ret = s;
     a = strchr(s,'/');
     if (a) {
	  *a = '\0'; /* end it here */	   	
     }
     
     a = strrchr(s,'.'); /* backwards */
     while (b != a) {
	  if (b) c = b+1;
	  b = strchr(s,'.');
	  s = b+1;	   	
     }
          
     if (c) {
	  ret = g_strdup(c);
     } else {
	  ret = g_strdup(ret);
     }
     
     g_free(string);
	    
     return ret;
}
  
static void resync_clist(GtkWidget *b, SideBar *sb)
{
	GSList *l;
	
	g_return_if_fail(sb != NULL);     
	
	g_slist_free(history);	
	history = NULL;
	history = skipstone_get_history();
	
	lists_made = 0;
	nodes_made = 0;
	
	g_slist_free(com);	
	g_slist_free(others);
	g_slist_free(org);
	g_slist_free(net);

	g_node_children_foreach(comroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeFree,NULL);
	g_node_children_foreach(orgroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeFree,NULL);
	g_node_children_foreach(netroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeFree,NULL);
	g_node_children_foreach(othersroot,G_TRAVERSE_ALL,(GNodeForeachFunc)NodeFree,NULL);

	g_node_destroy(comroot);
	g_node_destroy(netroot);
	g_node_destroy(orgroot);
	g_node_destroy(othersroot);
	
	com = NULL;
	net = NULL;
	org = NULL;
	others = NULL;
	comroot = NULL;
	netroot = NULL;
	orgroot = NULL;
	othersroot = NULL;	
	
	for (l = instances; l ; l=l->next) {
		SideBar *sb = (SideBar *)l->data;
		if (sb)
		  insert_in_clist(sb);
	}
		
	while (gtk_events_pending()) gtk_main_iteration();

}

static void NodeFree(GNode *node, gpointer data)
{
	DomainData *dd;
	if (!node || node == NULL) return;
	dd = (DomainData *)node->data;
     	if (!dd) return;
	if (dd->domain)
	  g_free(dd->domain);
     	g_free(dd);
}

static void on_plugin_destroy(GtkWidget *w, SideBar *sb)
{
	g_return_if_fail(sb != NULL);
	instances = g_slist_remove(instances,sb);
	g_free(sb); /* bye */
	sb = NULL;
}

SkipStonePlugin *init_plugin(void)
{
	/* get config here */
	state_shown = skipstone_get_config_value_as_int(PLUGIN_NAME,"state");
	if (state_shown == -1) { /* it wasn't set before */
		state_shown = 1;	
		skipstone_set_config_value_as_int(PLUGIN_NAME,"state",state_shown);
	}
	return &plugin;
}

static gboolean on_history_clist_button_press_event(GtkWidget *clist,GdkEventButton *event,SkipStone *skipstone) {
	GList *selection;

     	g_return_val_if_fail(clist != NULL, FALSE);
	g_return_val_if_fail(skipstone != NULL, FALSE);
	
	selection = GTK_CLIST(clist)->selection;
	if (selection == NULL || g_list_length(selection) == 0)	return FALSE;

	if (event->type == GDK_2BUTTON_PRESS) {
		gchar *location;
		GtkCTreeNode *row;

		row = g_list_nth_data(selection,0);
		location = gtk_ctree_node_get_row_data(GTK_CTREE(clist),
						       row);
		skipstone_load_url_cb(skipstone,location);
		return TRUE;


	} else if (event->button == 3) {
		gchar *location;
		GtkCTreeNode *row;
		row = g_list_nth_data(selection,0);
		location = gtk_ctree_node_get_row_data(GTK_CTREE(clist),
						       row);
	     	gtk_menu_popup(GTK_MENU(popup_menu(clist,skipstone,location)), NULL, NULL,NULL,NULL,GDK_BUTTON3_MASK,event->time);
		return TRUE;
	}
	return FALSE;
}


static GtkWidget *popup_menu(GtkWidget *clist, SkipStone *skipstone, gchar *location) {
	GtkWidget *menu, *m1, *m2;
	menu = gtk_menu_new();
	m1 = gtk_menu_item_new_with_label("Open ...");
	gtk_object_set_data(GTK_OBJECT(m1), "location", location);
	gtk_signal_connect(GTK_OBJECT(m1), "activate", GTK_SIGNAL_FUNC(load_from_popup), skipstone);
	m2 = gtk_menu_item_new_with_label("Open in a new window ...");
	gtk_object_set_data(GTK_OBJECT(m2), "location", location);
	gtk_signal_connect(GTK_OBJECT(m2), "activate", GTK_SIGNAL_FUNC(load_from_popup), NULL);
	gtk_menu_append(GTK_MENU(menu), m1);
	gtk_menu_append(GTK_MENU(menu), m2);
	gtk_widget_show_all(menu);
	return menu;
}
					  
static void load_from_popup(GtkWidget *w, SkipStone *skipstone)
{
     	gchar *url = gtk_object_get_data(GTK_OBJECT(w), "location");
	if (skipstone) 
	  skipstone_load_url_cb(skipstone,url);
	else
	  skipstone_new_window(url);
	return;
}
