/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * service-tracker.c
 * Copyright (C) Canonical LTD 2011
 * 
 * Author: Robert Carr <racarr@canonical.com>
 * 
 unity-webapps is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * unity-webapps 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <gtk/gtk.h>

#include "unity-webapps-service.h"
#include "unity-webapps-context.h"

static UnityWebappsService *service = NULL;
static GMainLoop *mainloop = NULL;

static GtkWidget *main_window = NULL;
static GtkWidget *main_box = NULL;
static GtkWidget *pane_alignment = NULL;
static GtkWidget *tree_view = NULL;
static GtkTreeStore *tree_store = NULL;

static GtkWidget *current_pane = NULL;

static GHashTable *contexts_by_name = NULL;

typedef struct _ContextHashData {
  UnityWebappsContext *context;
  GtkTreeRowReference *row_reference;
	
  GHashTable *interests_by_id;
	
  GtkWidget *pane;
  GtkWidget *interests_label;
} ContextHashData;

typedef struct _InterestHashData {
  ContextHashData *context_data;
  GtkTreeRowReference *row_reference;
	
  gint id;
  
  GtkWidget *pane;
  GtkLabel *active_label;
  GtkLabel *location_label;
  GtkImage *preview_image;
} InterestHashData;

static gboolean
parse_data_uri (const gchar *data_uri,
		gchar **mimetype,
		gchar **data)
{
  gchar **split;
  int result;
  
  g_assert (g_str_has_prefix (data_uri, "data:") == TRUE);

  split = g_strsplit (data_uri+5,
		      ";base64,",
		      2);

  result = g_strv_length (split);
  
  if (result != 2)
    {
      g_warning ("Failed to parse data uri: \n %s \n", data_uri);

      *mimetype = NULL;
      *data = NULL;
      
      g_strfreev (split);

      return FALSE;
    }
  
  *mimetype = split[0];
  *data = split[1];
  
  g_free (split);
  
  if (g_str_has_prefix (*mimetype, "image/") == FALSE)
    {
      g_warning ("Data URI does not have an image mimetype");

      g_free (mimetype);
      g_free (data);

      *mimetype = NULL;
      *data = NULL;
      
      return FALSE;
    }
  
  return TRUE;
}

static GdkPixbuf *
scale_preview_pixbuf (GdkPixbuf *pixbuf)
{
  GdkPixbuf *scaled_pixbuf;
  gint width, height;
  gdouble ratio;
  
  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);
  
  ratio = 160.0/height;
  
  scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, width*ratio,
										   160, GDK_INTERP_HYPER);
  
  return scaled_pixbuf;
}

static GdkPixbuf *
preview_image_from_data_uri (const gchar *data_uri)
{
  GdkPixbufLoader *loader;
  GdkPixbuf *pixbuf, *unscaled_pixbuf;
  gchar *mimetype, *data;
  guchar *decoded;
  gsize decoded_length;
  gboolean parsed, wrote;
  GError *error;

  mimetype = NULL;
  data = NULL;

  pixbuf = NULL;
  loader = NULL;
  
  parsed = parse_data_uri (data_uri, &mimetype, &data);
  
  if (parsed == FALSE)
	{
	  g_warning ("Failed to parse data uri");
	  return NULL;
	}
  
  decoded = g_base64_decode (data, &decoded_length);
  
  g_assert (decoded != NULL);
  
  loader = gdk_pixbuf_loader_new ();
  
  error = NULL;
  wrote = gdk_pixbuf_loader_write (loader, decoded,
								   decoded_length,
								   &error);
  if (wrote == FALSE)
	{
	  if (error == NULL)
		{
		  goto out;
		}
	  g_warning ("Failed to write data to GdkPixbuf: %s", error->message);
	  g_error_free (error);
	  
	  goto out;
	}
  
  printf("Wrote %zu bytes\n", decoded_length);
  
  error = NULL;

  if (error != NULL)
	{
	  g_warning("Failed to close pixbuf loader: %s", error->message);
	  g_error_free (error);
	  
	  goto out;
	}
  
  unscaled_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);  
  
  pixbuf = scale_preview_pixbuf (unscaled_pixbuf);
  
  g_object_unref (G_OBJECT (unscaled_pixbuf));

  gdk_pixbuf_loader_close (loader, NULL); 
  

								   
 out:  
  if (loader != NULL)
	{
	  g_object_unref (G_OBJECT (loader));
	}
  g_free (mimetype);
  g_free (data);
  g_free (decoded);
  
  return pixbuf;
}

static InterestHashData *
interest_hash_data_new (ContextHashData *context_data, GtkTreeIter *iter, gint id)
{
  InterestHashData *data;
  GtkTreePath *path;
  
  data = g_malloc0 (sizeof (InterestHashData));

  path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_store), iter);
  
  data->row_reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (tree_store), path);
  data->context_data = context_data;
  data->id = id;
  
  gtk_tree_path_free (path);
  
  return data;
}

void
interest_hash_data_free (InterestHashData *data)
{
  if (data->row_reference)
	{
	  gtk_tree_row_reference_free (data->row_reference);
	}
  
  g_object_unref (G_OBJECT (data->pane));
  
  g_free (data);
}
						 

static ContextHashData *
context_hash_data_new (UnityWebappsContext *context,
					   GtkTreeIter *iter)
{
  GtkTreePath *path;
  ContextHashData *data;
  
  data = g_malloc0 (sizeof (ContextHashData));
  
  path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_store), iter);
  
  data->context = context;
  data->row_reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (tree_store), path);
  data->interests_by_id = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)interest_hash_data_free);
  
  gtk_tree_path_free (path);
  
  return data;
}

static void
context_hash_data_free (ContextHashData *data)
{
  if (data->row_reference)
	{
	  gtk_tree_row_reference_free (data->row_reference);
	}
	
  g_object_unref (G_OBJECT (data->context));
  g_object_unref (G_OBJECT (data->pane));
	
  g_hash_table_destroy (data->interests_by_id);
  
  g_free (data);
}

static UnityWebappsContext *
get_context_proxy_for_name (const gchar *name, GtkTreeIter *iter)
{
  UnityWebappsContext *context;
  ContextHashData *data;
  
  data = g_hash_table_lookup (contexts_by_name, name);
  
  if (data != NULL)
	{
	  return data->context;
	}
  
  context = unity_webapps_context_new_for_context_name (service, name);
  data = context_hash_data_new  (context, iter);
  
  g_hash_table_insert (contexts_by_name, g_strdup (name), data);
  
  return context;
}

static void
builder_set_label_text (GtkBuilder *builder,
						const gchar *label_name,
						const gchar *label_text)
{
  GtkLabel *label;
  
  label = GTK_LABEL (gtk_builder_get_object (builder, label_name));
  
  gtk_label_set_text (label, label_text);
}

static GdkPixbuf *
render_webapp_icon_for_name (const gchar *icon_name)
{
  GtkIconTheme *theme;
  GtkIconInfo *info;
  GdkPixbuf *pb;
  
  if (icon_name == NULL)
	{
	  return NULL;
	}
  
  theme = gtk_icon_theme_new ();

  gtk_icon_theme_set_custom_theme (theme, "unity-webapps");
  
  info = gtk_icon_theme_lookup_icon (theme, icon_name, 64, 0);
  
  if (info == NULL)
	{
	  g_object_unref (G_OBJECT (theme));
	  
	  return NULL;
	}
  
  pb = gtk_icon_info_load_icon (info, NULL); /* TODO: FIXME: ERROR */
  
  gtk_icon_info_free (info);
  g_object_unref (G_OBJECT (theme));
  
  return pb;
}

static void
set_context_pane_labels (UnityWebappsContext *context, GtkBuilder *builder, ContextHashData *data)
{
  gchar *name_label, *domain_label, *interests_label, *desktop_name_label;
  const gchar *context_name; 
  const gchar *context_domain;
  const gchar *context_desktop_name;
  gint num_interests;
  
  context_name = unity_webapps_context_get_name (context);
  context_domain = unity_webapps_context_get_domain (context);
  context_desktop_name = unity_webapps_context_get_desktop_name (context);
  
  num_interests = 1; // TODO: FIX ME
  
  name_label = g_strdup_printf ("Name: %s", context_name);
  domain_label = g_strdup_printf("Domain: %s", context_domain);
  desktop_name_label = g_strdup_printf("Desktop Name: %s", context_desktop_name);
  interests_label = g_strdup_printf("Number of Interests: %d", num_interests);
  
  builder_set_label_text (builder, "domain-label", domain_label);
  builder_set_label_text (builder, "name-label", name_label);
  builder_set_label_text (builder, "desktop-name-label", desktop_name_label);
  builder_set_label_text (builder, "num-interests-label", interests_label);
  
  data->interests_label = GTK_WIDGET (gtk_builder_get_object (builder, "num-interests-label"));
  
  g_free (name_label);
  g_free (domain_label);
  g_free (desktop_name_label);
  g_free (interests_label);
}

static void
set_context_pane_icon (UnityWebappsContext *context,
					   GtkBuilder *builder)
{
  GtkImage *image;
  GdkPixbuf *icon_pb;
  gchar *icon_name;
  
  icon_name = unity_webapps_context_get_icon_name (context);
  
  icon_pb = render_webapp_icon_for_name (icon_name);
  
  if (icon_pb != NULL)
	{
	  image = GTK_IMAGE (gtk_builder_get_object (builder, "icon-image"));
	  
	  gtk_image_set_from_pixbuf (image, icon_pb);
  
	  g_object_unref (G_OBJECT (icon_pb));
	}

  g_free (icon_name);
}

static void
on_raise_button_clicked (GtkWidget *button,
						 gpointer user_data)
{
  UnityWebappsContext *context;
  
  context = (UnityWebappsContext *)user_data;
  
  unity_webapps_context_raise (context);
}


static void
on_close_button_clicked (GtkWidget *button,
						 gpointer user_data)
{
  UnityWebappsContext *context;
  
  context = (UnityWebappsContext *)user_data;
  
  unity_webapps_context_close (context);
}

static GtkWidget *
make_pane_for_context (UnityWebappsContext *context, ContextHashData *data)
{
  GtkWidget *main_widget;
  GtkBuilder *builder;
  GError *error;
  
  builder = gtk_builder_new ();
  
  error = NULL;
  
  gtk_builder_add_from_file (builder, "context-pane.xml", &error);
  
  if (error != NULL)
	{
	  g_error ("Error loading context-pane.xml: %s", error->message);
	  g_error_free (error);
	  
	  return NULL;
	}
  
  set_context_pane_labels (context, builder, data);  
  set_context_pane_icon (context, builder);
  
  g_signal_connect (gtk_builder_get_object (builder, "raise-button"),
					"clicked",
					G_CALLBACK (on_raise_button_clicked),
					context);

  g_signal_connect (gtk_builder_get_object (builder, "close-button"),
					"clicked",
					G_CALLBACK (on_close_button_clicked),
					context);
  
  main_widget = GTK_WIDGET (g_object_ref (gtk_builder_get_object (builder, "box1")));
  
  return main_widget;
}

static void
on_preview_ready (UnityWebappsContext *context,
				  gint interest_id,
				  const gchar *preview_data,
				  gpointer user_data)
{
  GdkPixbuf *pixbuf;
  InterestHashData *data;
  
  data = (InterestHashData *)user_data;
  
  pixbuf = preview_image_from_data_uri (preview_data);
  
  if (pixbuf == NULL)
	{
	  return;
	}
  
  gtk_image_set_from_pixbuf (data->preview_image, pixbuf);
  
  g_object_unref (G_OBJECT (pixbuf));

}

static void
on_preview_clicked (GtkWidget *button,
					gpointer user_data)
{
  InterestHashData *data;
  
  data = (InterestHashData *)user_data;
  
  unity_webapps_context_request_preview (data->context_data->context,
										 data->id,
										 on_preview_ready,
										 data);
  
  
}

static void
on_interest_raise_clicked (GtkWidget *button,
						   gpointer user_data)
{
  UnityWebappsContext *context;
  InterestHashData *data;

  data = (InterestHashData *)user_data;
  
  context = data->context_data->context;
  
  unity_webapps_context_raise_interest (context, data->id);
}

static GtkWidget *
make_pane_for_interest_id (UnityWebappsContext *context,
						   gint interest_id,
						   InterestHashData *data)
{
  GtkWidget *main_widget;
  GtkBuilder *builder;
  gchar *message, *owner, *location;
  gboolean is_active;
  GError *error;
  
  builder = gtk_builder_new ();
  
  error = NULL;
  
  gtk_builder_add_from_file (builder, "interest-pane.xml", &error);
  
  if (error != NULL)
	{
	  g_error ("Error loading interest-pane.xml: %s", error->message);
	  g_error_free (error);
	  
	  return NULL;
	}
  
  set_context_pane_icon (context, builder);
  
  owner = unity_webapps_context_get_interest_owner (context, interest_id);
  message = g_strdup_printf ("Owner: %s", owner);
  
  gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (builder, "owner-label")),
					  message);
  
  is_active = unity_webapps_context_get_view_is_active (context, interest_id);

  data->active_label = GTK_LABEL (gtk_builder_get_object (builder, "view-active-label"));
  data->location_label = GTK_LABEL (gtk_builder_get_object (builder, "location-label"));
  data->preview_image = GTK_IMAGE (gtk_builder_get_object (builder, "preview-image"));
  
  location = unity_webapps_context_get_view_location (context, interest_id);
  
  if (location && (strlen(location) > 0))
	{
	  gchar *lm;

	  lm = g_strdup_printf("Location: %s", location);
	  gtk_label_set_text (data->location_label, lm);
	  
	  g_free (lm);
	}


  
  if (is_active)
	{
	  gtk_label_set_text (data->active_label,
						  "View is Active: Yes");
	}
  
  g_signal_connect (gtk_builder_get_object (builder, "preview-button"),
					"clicked",
					G_CALLBACK (on_preview_clicked),
					data);
  g_signal_connect (gtk_builder_get_object (builder, "raise-button"),
					"clicked",
					G_CALLBACK (on_interest_raise_clicked),
					data);
					
  
  main_widget = GTK_WIDGET (g_object_ref (gtk_builder_get_object (builder, "box1")));
  
  g_free (owner);
  g_free (message);
  
  return main_widget;
}

static gint
get_interest_by_tree_path (UnityWebappsContext *context, GtkTreePath *path)
{
  ContextHashData *data;
  const gchar *context_name;
  GHashTableIter iter;
  gpointer key, value;
  
  context_name = unity_webapps_context_get_context_name (context);
  
  data = g_hash_table_lookup (contexts_by_name, context_name);
  
  if (data == NULL)
	{
	  return -1;
	}
  
  g_hash_table_iter_init (&iter, data->interests_by_id);
  
  while (g_hash_table_iter_next (&iter, &key, &value))
	{
	  InterestHashData *interest_data;
	  GtkTreePath *s_path;
	  
	  interest_data = (InterestHashData *)value;
	  
	  s_path = gtk_tree_row_reference_get_path (interest_data->row_reference);
	  
	  if (gtk_tree_path_compare (path, s_path) == 0)
		{
		  gtk_tree_path_free (s_path);
		  return GPOINTER_TO_INT(key); 
		}
	  gtk_tree_path_free (s_path);
	}
  
  return -1;
  
}

static UnityWebappsContext *
get_context_by_tree_path (GtkTreePath *path)
{
  GHashTableIter iter;
  gpointer key, value;
  
  if (path == NULL)
	{
	  return NULL;
	}
  
  g_hash_table_iter_init (&iter, contexts_by_name);
  
  while (g_hash_table_iter_next (&iter, &key, &value))
	{
	  ContextHashData *data;
	  GtkTreePath *s_path;
	  
	  data = (ContextHashData *)value;
	  
	  s_path = gtk_tree_row_reference_get_path (data->row_reference);
	  
	  if (gtk_tree_path_compare (path, s_path) == 0)
		{
		  gtk_tree_path_free (s_path);
		  return data->context;
		}
	  gtk_tree_path_free (s_path);
	}
  
  return NULL;
}

static void
append_interests (UnityWebappsContext *context,
				  ContextHashData *hash_data,
				  GtkTreeIter *iter)
{
  GVariant *interests;
  GVariant *interest_variant;
  GVariantIter *variant_iter;
  
  interests = unity_webapps_context_list_interests (context);
  
  if (interests == NULL)
	{
	  return;
	}
  
  variant_iter = g_variant_iter_new (interests);
  
  while ((interest_variant = g_variant_iter_next_value (variant_iter)))
	{
	  GtkWidget *interest_pane;
	  InterestHashData *data;
	  gchar *message;
	  GtkTreeIter new_iter;
	  gint interest_id;
	  
	  interest_id = g_variant_get_int32 (interest_variant);
	  
	  gtk_tree_store_insert (tree_store,
							 &new_iter,
							 iter,
							 0);
	  
	  message = g_strdup_printf ("Interest %d", interest_id);
	  
	  gtk_tree_store_set (tree_store, &new_iter, 0, message, -1);
	  
	  data = interest_hash_data_new (hash_data, &new_iter, interest_id);
	  
	  interest_pane = make_pane_for_interest_id (context, interest_id, data);
	  
	  data->pane = interest_pane;
	  
	  g_hash_table_insert (hash_data->interests_by_id, GINT_TO_POINTER (interest_id), data);
	  
	  g_free (message);
	}
  
  g_variant_iter_free (variant_iter);
  g_variant_unref (interests);
}

static void
on_interests_changed (UnityWebappsContext *context,
					  gint interest_id,
					  gpointer user_data)
{
  ContextHashData *hash_data = (ContextHashData *)user_data;
  GtkTreePath *path;
  gchar *label_text;
  GtkTreeIter context_tree_iter;
  GHashTableIter hash_iter;
  gpointer key, value;
  
  g_hash_table_iter_init (&hash_iter, hash_data->interests_by_id);
  
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
	{
	  InterestHashData *interest_hash_data;
	  GtkTreePath *path;
	  GtkTreeIter iter;
	  
	  interest_hash_data = (InterestHashData *)value;
	  
	  path = gtk_tree_row_reference_get_path (interest_hash_data->row_reference);
	  gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path);
	  
	  gtk_tree_store_remove (tree_store, &iter);
	  
	  gtk_tree_path_free (path);
	}

  g_hash_table_remove_all (hash_data->interests_by_id);
  
  path = gtk_tree_row_reference_get_path (hash_data->row_reference);
  
  gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &context_tree_iter, path);

  append_interests (context, hash_data, &context_tree_iter);
  
  label_text = g_strdup_printf ("Number of Interests: %d", g_hash_table_size (hash_data->interests_by_id));
  gtk_label_set_text (GTK_LABEL (hash_data->interests_label), label_text);
  
  g_free (label_text);
  gtk_tree_path_free (path);
}

static void
on_view_is_active_changed (UnityWebappsContext *context,
						   gint interest_id,
						   gboolean is_active,
						   gpointer user_data)
{
  ContextHashData *data;
  InterestHashData *interest_data;
  
  data = (ContextHashData *)user_data;
  
  interest_data = (InterestHashData *)g_hash_table_lookup (data->interests_by_id, GINT_TO_POINTER (interest_id));
  
  if (interest_data == NULL)
	{
	  return;
	}
  
  if (is_active)
	{
	  gtk_label_set_text (interest_data->active_label, "View is Active: Yes");
	}
  else
	{
	  gtk_label_set_text (interest_data->active_label, "View is Active: No");
	}
}

static void
on_view_location_changed (UnityWebappsContext *context,
						  gint interest_id,
						  const gchar *location,
						  gpointer user_data)
{
  ContextHashData *data;
  InterestHashData *interest_data;
  gchar *message;
  
  data = (ContextHashData *)user_data;
  
  interest_data = (InterestHashData *)g_hash_table_lookup (data->interests_by_id, GINT_TO_POINTER (interest_id));
  
  if (interest_data == NULL)
	{
	  return;
	}
  
  message = g_strdup_printf ("Location: %s", location);
  
  gtk_label_set_text (interest_data->location_label, message);

  g_free (message);
}

static void
append_context_by_name (const gchar *context_name)
{
  UnityWebappsContext *context;
  const gchar *name;
  const gchar *domain;
  gchar *message;
  ContextHashData *data;
  GtkTreeIter iter;

  gtk_tree_store_append (tree_store, &iter, NULL);
  
  context = get_context_proxy_for_name (context_name, &iter);
  
  name = unity_webapps_context_get_name (context);
  domain = unity_webapps_context_get_domain (context);
  
  message = g_strdup_printf("Context (%s): %s at %s", context_name, name, domain);
  
  gtk_tree_store_set(tree_store, &iter, 0, message, -1);
  
  data = g_hash_table_lookup (contexts_by_name, context_name);
  
  append_interests (context, data, &iter);
  
  unity_webapps_context_on_interest_appeared (context, on_interests_changed, data);
  unity_webapps_context_on_interest_vanished (context, on_interests_changed, data);
  unity_webapps_context_on_view_is_active_changed (context, on_view_is_active_changed, data);
  unity_webapps_context_on_view_location_changed (context, on_view_location_changed, data);
  
  data->pane = make_pane_for_context (context, data);
  
  g_free (message);
}

static void
remove_context_by_name (const gchar *context_name)
{
  GtkTreePath *path;
  ContextHashData *data;
  GtkTreeIter iter;
  
  data = (gpointer)g_hash_table_lookup (contexts_by_name, context_name);
  
  if (data == NULL)
	{
	  return;
	}
  
  path = gtk_tree_row_reference_get_path (data->row_reference);
  
  gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path);
  
  gtk_tree_store_remove (tree_store, &iter);
  
  if (data->pane == current_pane)
	{
	  gtk_container_remove (GTK_CONTAINER (pane_alignment), data->pane);
	  
	  current_pane = NULL;
	}
  
  g_hash_table_remove (contexts_by_name, context_name);
  
  gtk_tree_path_free (path);
}

static void
populate_model_initially ()
{
  gchar **contexts;
  gchar *context;
  gint i;
  
  contexts = unity_webapps_service_list_contexts (service);
  
  i = 0;
  
  if (contexts == NULL)
	{
	  return;
	}
  
  for (context = contexts[i]; context != NULL; context = contexts[++i])
	{
	  if (strlen (context) == 0)
		{
		  continue;
		}
	  append_context_by_name (context);
	}
}

static void
on_context_appeared (UnityWebappsService *service,
					 const gchar *name,
					 gpointer user_data)
{
  append_context_by_name (name);
}

static void
on_context_vanished (UnityWebappsService *service,
					 const gchar *name,
					 gpointer user_data)
{
  remove_context_by_name (name);
}

static void
create_model ()
{
  tree_store = gtk_tree_store_new (1, G_TYPE_STRING);
  
  gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (tree_store));
  
  populate_model_initially ();
}

static void
setup_column ()
{
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;

  renderer = gtk_cell_renderer_text_new ();

  column = gtk_tree_view_column_new_with_attributes ("Object Name",
													 renderer,
													 "text", 0,
													 NULL);
  
  gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);

}

static void
set_pane_by_context (UnityWebappsContext *context)
{
  ContextHashData *data;
  const gchar *context_name;
	
  context_name = unity_webapps_context_get_context_name (context);
	
  data = g_hash_table_lookup (contexts_by_name, context_name);
  
  g_assert (data != NULL);
	
  if ((data == NULL) && (current_pane != NULL))
	{
	  gtk_container_remove (GTK_CONTAINER (pane_alignment), current_pane);
	  current_pane = NULL;
	  
	  return;
	}
  

  
  if (current_pane != NULL)
	{
	  if (current_pane == data->pane)
		{
		  return;
		}

	  gtk_container_remove (GTK_CONTAINER (pane_alignment), current_pane);
	  current_pane = NULL;
	}
  
  if (gtk_widget_get_parent (data->pane) != NULL)
	{
	  gtk_widget_reparent (data->pane, pane_alignment);
	}
  else
	{
	  gtk_container_add (GTK_CONTAINER (pane_alignment), data->pane);
	}
  
  gtk_widget_show_all (data->pane);
  
  current_pane = data->pane;
}

static void
set_pane_by_interest (UnityWebappsContext *context, gint interest_id)
{
  ContextHashData *data;
  InterestHashData *interest_data;
  const gchar *context_name;
	
  context_name = unity_webapps_context_get_context_name (context);
	
  data = g_hash_table_lookup (contexts_by_name, context_name);
  
  interest_data = g_hash_table_lookup (data->interests_by_id, GINT_TO_POINTER (interest_id));
  
  if (interest_data == NULL)
	{
	  return;
	}
	
  if ((data == NULL || interest_data == NULL) && (current_pane != NULL))
	{
	  gtk_container_remove (GTK_CONTAINER (pane_alignment), current_pane);
	  current_pane = NULL;
	  
	  return;
	}
  
  if (current_pane != NULL)
	{
	  if (current_pane == interest_data->pane)
		{
		  return;
		}

	  gtk_container_remove (GTK_CONTAINER (pane_alignment), current_pane);
	  current_pane = NULL;
	}
  
  if (gtk_widget_get_parent (interest_data->pane) != NULL)
	{
	  gtk_widget_reparent (interest_data->pane, pane_alignment);
	}
  else
	{
	  gtk_container_add (GTK_CONTAINER (pane_alignment), interest_data->pane);
	}
  
  gtk_widget_show_all (interest_data->pane);
  
  current_pane = interest_data->pane;
}

static void
on_tree_view_cursor_changed (GtkTreeView *tree_view,
							 gpointer user_data)
{
  GtkTreePath *path;
  UnityWebappsContext *context;
  gint depth;
  
  gtk_tree_view_get_cursor (tree_view, &path, NULL);
  
  if (path == NULL)
	{
	  return;
	}
  
  depth = gtk_tree_path_get_depth (path);
  
  if (depth == 1)
	{
	  context = get_context_by_tree_path (path);
	  
	  if (context == NULL)
		{
		  goto out;
		}
	  
	  set_pane_by_context (context);
	}
  else if (depth == 2)
	{
	  GtkTreePath *parent;
	  gint interest;
	  
	  parent = gtk_tree_path_copy (path);
	  gtk_tree_path_up (parent);
	  
	  context = get_context_by_tree_path (parent);
	  
	  interest = get_interest_by_tree_path (context, path);
	  
	  set_pane_by_interest (context, interest);
	  
	  gtk_tree_path_free (parent);
	}

 out:
  gtk_tree_path_free (path);
}

static void
setup_treeview ()
{
  tree_view = gtk_tree_view_new ();
  
  g_signal_connect (tree_view, "cursor-changed",
					G_CALLBACK (on_tree_view_cursor_changed),
					NULL);
  
  setup_column();

  gtk_container_add (GTK_CONTAINER (main_box), tree_view);
  
  create_model ();
}

static void
setup_main_box ()
{
  main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  
  setup_treeview();

  pane_alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
  gtk_container_add (GTK_CONTAINER (main_box), pane_alignment);
  
  gtk_container_add (GTK_CONTAINER (main_window), main_box);
}

static void
setup_main_window ()
{
  gtk_window_set_title (GTK_WINDOW (main_window), "Unity Webapps Tracker");
  
  setup_main_box ();
}

gint
main (gint argc, gchar **argv)
{
  gtk_init (&argc, &argv);
  
  contexts_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) context_hash_data_free);

  service = unity_webapps_service_new ();
  
  unity_webapps_service_on_context_appeared (service, on_context_appeared, NULL);
  unity_webapps_service_on_context_vanished (service, on_context_vanished, NULL);
  
  main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  
  setup_main_window ();

  gtk_window_set_default_size (GTK_WINDOW (main_window), 450, 250);
  
  gtk_widget_show_all (main_window);
  //  gtk_window_set_resizable (GTK_WINDOW (main_window), FALSE);
  
  mainloop = g_main_loop_new (NULL, FALSE);
  
  g_main_loop_run (mainloop);
  
  
  exit (0);
  
}
