/*
 * unity-webapps-context.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 <stdio.h>

#include "unity-webapps-context.h"

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

#include "unity-webapps-context-private.h"
#include "unity-webapps-sanitizer.h"
#include "unity-webapps-debug.h"

#include "unity-webapps-dbus-util.h"
#include "unity-webapps-rate.h"



#include "unity-webapps-wire-version.h"

static void initable_iface_init (GInitableIface *initable_iface);
static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);

G_DEFINE_TYPE_WITH_CODE(UnityWebappsContext, unity_webapps_context, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, initable_iface_init) G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, async_initable_iface_init));

#define UNITY_WEBAPPS_CONTEXT_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), UNITY_WEBAPPS_TYPE_CONTEXT, UnityWebappsContextPrivate))

enum {
  PROP_0,
  PROP_NAME,
  PROP_DOMAIN,
  PROP_ICON_URL,
  PROP_SERVICE,
  PROP_CONTEXT_NAME,
  PROP_MIMETYPES
};

enum {
  UNITY_WEBAPPS_INVALID_INTEREST_ID = -1
};

static void
unity_webapps_context_on_preview_requested (UnityWebappsGenContext *context_proxy,
					    gint interest_id,
					    gpointer user_data)
{
  UnityWebappsContext *context;
  const gchar *preview_data;
  
  context = (UnityWebappsContext *)user_data;
  
  if (context->priv->took_interest == FALSE)
    {
      return;
    }
  
  if (context->priv->interest_id != interest_id)
    {
      return;
    }
  
  if (context->priv->preview_callback == NULL)
    {
      return;
    }
  
  preview_data = context->priv->preview_callback (context, context->priv->preview_user_data);
  
  if (preview_data == NULL)
    {
      return;
    }
  
  unity_webapps_gen_context_call_preview_ready (context->priv->context_proxy,
						interest_id,
						preview_data,
						NULL /* Cancellable */,
						NULL /* Callback */,
						NULL /* User Data */);
  
}

typedef struct _unity_webapps_context_action_data {
  UnityWebappsContextActionCallback callback;
  gpointer user_data;
} unity_webapps_context_action_data;

static void
_context_action_invoked (UnityWebappsGenContext *context_proxy,
			 const gchar *label,
			 gpointer user_data)
{
  UnityWebappsContext *context;
  unity_webapps_context_action_data *data;
  
  context = (UnityWebappsContext *)user_data;
  g_return_if_fail(context != NULL);

  if (!G_IS_OBJECT(context))
      return;
  
  data = (unity_webapps_context_action_data *)g_hash_table_lookup (context->priv->action_callbacks_by_name, label);
  
  if ((data != NULL) && (data->callback != NULL))
    {
      UNITY_WEBAPPS_NOTE (CONTEXT, "Action invoked: %s", label);
      
      data->callback (context, data->user_data);
      return;
    }
  
  UNITY_WEBAPPS_NOTE (CONTEXT, "Action invoked, but we do not have a handler: %s", label);
}

static void
_accept_data_changed_callback (UnityWebappsGenContext *gen_context,
                               const gchar * const *files,
                               gpointer user_data)
{
  UnityWebappsContext *context = (UnityWebappsContext *)user_data;

  g_signal_emit_by_name (context, "accept-data-changed", files);
}

static gboolean
unity_webapps_context_daemon_initable_init (GInitable *initable,
					    GCancellable *cancellable,
					    GError **error)
{
  UnityWebappsContext *context = (UnityWebappsContext *)initable;
  gchar *context_name = NULL, *version = NULL;

  if (context->priv->context_name == NULL)
    {
      unity_webapps_gen_service_call_get_context_sync (UNITY_WEBAPPS_GEN_SERVICE (unity_webapps_service_get_proxy (context->priv->service)),
						       context->priv->name,
						       context->priv->domain,
						       context->priv->icon_url ? context->priv->icon_url : "none",
						       context->priv->mime_types ? context->priv->mime_types : "none",
						       &context_name,
						       &version,
						       cancellable,
						       error);
      
      if (error && (*error != NULL))
	{
	  g_critical ("Error calling GetContext method when constructing new context proxy: %s", (*error)->message);
	  
	  return FALSE;
	}
      
      context->priv->took_interest = TRUE;
  
    }
  else
    {
      context_name = context->priv->context_name;
    }
  
  if (version && (g_strcmp0 (UNITY_WEBAPPS_WIRE_PROTOCOL_VERSION, version) != 0))
    {
      g_set_error (error, g_quark_from_string ("unity-webapps"), -1, "Wire protocol version mismatch between context (%s) and client (%s)",
		   version, UNITY_WEBAPPS_WIRE_PROTOCOL_VERSION);
      
      context->priv->took_interest = FALSE;
      
      return FALSE;
    }
  
  context->priv->global_rate = 0;
      
  context->priv->context_proxy = unity_webapps_gen_context_proxy_new_sync (unity_webapps_service_get_connection (context->priv->service),
									   G_DBUS_PROXY_FLAGS_NONE,
									   context_name,
									   UNITY_WEBAPPS_CONTEXT_PATH,
									   cancellable,
									   error);
  
  
  if (error && (*error != NULL))
    {
      g_critical ("Error creating context proxy object for %s: %s", context_name, (*error)->message);
      
      g_free (context_name);
      
      context->priv->context_proxy = NULL;
      
      return FALSE;
    }

  UNITY_WEBAPPS_NOTE (CONTEXT, "Created context proxy");
  
  if (context->priv->took_interest)
    { 
      unity_webapps_gen_context_call_add_interest_sync (context->priv->context_proxy, &(context->priv->interest_id), 
							NULL /* Cancellable */,
							NULL /* ERROR TODO FIXME */);
      
      g_signal_connect (context->priv->context_proxy, "preview-requested", G_CALLBACK (unity_webapps_context_on_preview_requested), context);
    }

  context->priv->context_name = context_name;
  
  context->priv->notification_context = unity_webapps_notification_context_new (context, error);
  
  if (context->priv->notification_context == NULL)
    {
      g_critical ("Failed to create notification context proxy");
      g_object_unref (G_OBJECT(context->priv->context_proxy));
      g_free (context_name);
      
      return FALSE;
    }
  
  context->priv->indicator_context = unity_webapps_indicator_context_new (context, error);
  
  if (context->priv->indicator_context == NULL)
    {
      g_critical("Failed to create indicator context proxy");
      
      unity_webapps_notification_context_free (context->priv->notification_context);
      g_object_unref (G_OBJECT (context->priv->context_proxy));
      g_free (context_name);
      
      return FALSE;	      
    }

  context->priv->music_player_context = unity_webapps_music_player_context_new (context, error);
  
  if (context->priv->music_player_context == NULL)
    {
      g_critical("Failed to create music player context proxy");
      
      unity_webapps_notification_context_free (context->priv->notification_context);
      unity_webapps_indicator_context_free (context->priv->indicator_context);
      g_object_unref (G_OBJECT (context->priv->context_proxy));
      g_free (context_name);

      return FALSE;	      
    }
  
  context->priv->launcher_context = unity_webapps_launcher_context_new (context, error);
  
  if (context->priv->launcher_context == NULL)
    {
      g_critical ("Failed to create launcher context proxy");
      
      unity_webapps_notification_context_free (context->priv->notification_context);
      unity_webapps_indicator_context_free (context->priv->indicator_context);
      unity_webapps_music_player_context_free (context->priv->music_player_context);
      g_object_unref (G_OBJECT (context->priv->context_proxy));
      g_free (context_name);
      
      return FALSE;
    }

  g_signal_connect (context->priv->context_proxy, "application-action-invoked",
		    G_CALLBACK (_context_action_invoked),
		    context);

  g_signal_connect (context->priv->context_proxy, "accept-data-changed",
		    G_CALLBACK (_accept_data_changed_callback),
		    context);
  
  context->priv->remote_ready = TRUE;

  return TRUE;
}

static void
initable_iface_init (GInitableIface *initable_iface)
{
  initable_iface->init = unity_webapps_context_daemon_initable_init;
}

static void
async_initable_iface_init (GAsyncInitableIface *async_initable_iface)
{
  // Default implementation
}

static void
unity_webapps_context_finalize (GObject *object)
{
  UnityWebappsContext *context = UNITY_WEBAPPS_CONTEXT (object);  
  GError *error;
  
  error = NULL;
  
  if (context->priv->took_interest == TRUE)
    {
      unity_webapps_gen_context_call_lost_interest_sync (context->priv->context_proxy,
							 context->priv->interest_id,
                                                         context->priv->user_abandoned,
							 NULL,
							 &error);
      
      if (error)
	{
	  g_warning ("Error calling lost interest for context: %s",
		     error->message);
	  
	  g_error_free (error);
	  
	  error = NULL;
	}
    }
  
  if (context->priv->notification_context)
    {
      unity_webapps_notification_context_free (context->priv->notification_context);
    }
  if (context->priv->indicator_context)
    {
      unity_webapps_indicator_context_free (context->priv->indicator_context);
    }
  if (context->priv->music_player_context)
    {
      unity_webapps_music_player_context_free (context->priv->music_player_context);
    }
  if (context->priv->launcher_context)
    {
      unity_webapps_launcher_context_free (context->priv->launcher_context);
    }
  
  g_free (context->priv->name);
  g_free (context->priv->domain);
  g_free (context->priv->icon_url);
  g_free (context->priv->mime_types);
  
  g_free (context->priv->context_name);
  
  g_hash_table_destroy (context->priv->action_callbacks_by_name);
  
  g_object_unref (G_OBJECT (context->priv->context_proxy));

  g_object_unref (G_OBJECT (context->priv->service));

}

static void
unity_webapps_context_get_property (GObject *object,
				    guint prop_id,
				    GValue *value,
				    GParamSpec *pspec)
{
  UnityWebappsContext *context;
  
  context = UNITY_WEBAPPS_CONTEXT(object);
  
  switch (prop_id)
  {
  case PROP_NAME:
    g_value_set_string (value, context->priv->name);
    break;
  case PROP_MIMETYPES:
    g_value_set_string (value, context->priv->mime_types);
    break;
  case PROP_DOMAIN:
    g_value_set_string (value, context->priv->domain);
    break;
  case PROP_ICON_URL:
    g_value_set_string (value, context->priv->icon_url);
    break;
  case PROP_SERVICE:
    g_value_set_object (value, context->priv->service);
    break;
  case PROP_CONTEXT_NAME:
    g_value_set_string (value, context->priv->context_name);
    break;
    
  }
}

static void
unity_webapps_context_set_property (GObject *object,
				    guint prop_id,
				    const GValue *value,
				    GParamSpec *pspec)
{
  UnityWebappsContext *context;
  
  context = UNITY_WEBAPPS_CONTEXT (object);

  switch (prop_id)
    {
    case PROP_NAME:
      g_return_if_fail (context->priv->name == NULL);
      context->priv->name = g_value_dup_string (value);
      break;
    case PROP_MIMETYPES:
      context->priv->mime_types = g_value_dup_string (value);
      break;
    case PROP_DOMAIN:
      g_return_if_fail (context->priv->domain == NULL);
      context->priv->domain = g_value_dup_string (value);
      break;
    case PROP_ICON_URL:
      g_return_if_fail (context->priv->icon_url == NULL);
      context->priv->icon_url = g_value_dup_string (value);
      break;
    case PROP_SERVICE:
      g_return_if_fail (context->priv->service == NULL);
      context->priv->service = g_value_dup_object (value);
      break;
    case PROP_CONTEXT_NAME:
      g_return_if_fail (context->priv->context_name == NULL);
      context->priv->context_name = g_value_dup_string (value);
      break;
    }
}

static void
unity_webapps_context_class_init (UnityWebappsContextClass *klass)
{
  GObjectClass *object_class;
  
  object_class = G_OBJECT_CLASS (klass);
  
  object_class->finalize = unity_webapps_context_finalize;
  object_class->set_property = unity_webapps_context_set_property;
  object_class->get_property = unity_webapps_context_get_property;
  
  g_object_class_install_property (object_class, PROP_NAME,
				   g_param_spec_string ("name",
							"Name",
							"Name of the context",
							NULL,
							G_PARAM_READWRITE | 
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_MIMETYPES,
				   g_param_spec_string ("mime-types",
							"Mime Types",
							"Capable of opening files with the given content type",
							NULL,
							G_PARAM_READWRITE |
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_DOMAIN,
				   g_param_spec_string ("domain",
							"Domain",
							"Domain of the context",
							NULL,
							G_PARAM_READWRITE | 
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_ICON_URL,
				   g_param_spec_string ("icon-url",
							"Icon URL",
							"Icon URL for the context",
							NULL,
							G_PARAM_READWRITE | 
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_SERVICE,
				   g_param_spec_object ("service",
							"Service",
							"The UnityWebappsService for the context",
							UNITY_WEBAPPS_TYPE_SERVICE,
							G_PARAM_READWRITE |
							G_PARAM_CONSTRUCT_ONLY |
							G_PARAM_STATIC_STRINGS));
  
  g_object_class_install_property (object_class, PROP_CONTEXT_NAME,
				   g_param_spec_string ("context-name",
							"Context DBus Name",
							"The DBus Name for the context",
							NULL,
							G_PARAM_READWRITE |
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));

  g_signal_new ("accept-data-changed",
    G_TYPE_FROM_CLASS (object_class),
    G_SIGNAL_RUN_LAST,
    G_STRUCT_OFFSET (UnityWebappsContextClass, accept_data_changed),
    NULL,
    NULL,
    g_cclosure_marshal_generic,
    G_TYPE_NONE,
    1, G_TYPE_STRV);

  g_type_class_add_private (object_class ,(sizeof (UnityWebappsContextPrivate)));
}

static void
unity_webapps_context_init(UnityWebappsContext *context)
{
  context->priv = UNITY_WEBAPPS_CONTEXT_GET_PRIVATE (context);

  context->priv->service = NULL;
  
  context->priv->context_proxy = NULL;

  context->priv->notification_context = NULL;
  context->priv->indicator_context = NULL;
  context->priv->music_player_context = NULL;
  
  context->priv->global_rate = 0;
  
  context->priv->name = NULL;
  context->priv->domain = NULL;
  context->priv->icon_url = NULL;
  context->priv->mime_types = NULL;
  
  context->priv->context_name = NULL;
  context->priv->took_interest = FALSE;
  context->priv->user_abandoned = TRUE;
  
  context->priv->interest_id = UNITY_WEBAPPS_INVALID_INTEREST_ID;
  
  context->priv->preview_callback = NULL;
  
  context->priv->action_callbacks_by_name = g_hash_table_new_full (g_str_hash, g_str_equal,
								   g_free, g_free);
  
  context->priv->remote_ready = FALSE;

}

typedef struct _unity_webapps_context_new_data {
  UnityWebappsContextReadyCallback callback;
  gpointer user_data;
  
  UnityWebappsContext *webapps_context;
  
  GMainContext *context;

  // Async init comes in two flavors: new or new_lazy (where a placeholder
  // gobject is created in the first place) before actually performing the async
  // init. The context ready callback is then called in those two contexts
  // with different "relashionship" to the created gobject (different
  // ref counts): one is a plain new one, and the other is a placeholder
  // + the newly async created one (ref counted).
  gboolean webapps_object_needs_unref;

} unity_webapps_context_new_data;

static gboolean
complete_in_idle_cb (gpointer user_data)
{
  unity_webapps_context_new_data *data;

  data = (unity_webapps_context_new_data *)user_data;

  if (!data || !data->webapps_context)
    return FALSE;

  // first unref what should be unrefed
  if (data->webapps_object_needs_unref == TRUE)
    g_object_unref (G_OBJECT (data->webapps_context));

  // ... then see what we have left (object might already have been
  // unrefed by an abandoned for example.
  if (data->callback && G_IS_OBJECT(data->webapps_context))
    data->callback (data->webapps_context, data->user_data);

  return FALSE;
}

static void
unity_webapps_context_ready (GObject *source_object,
			     GAsyncResult *res,
			     gpointer user_data)
{
  GAsyncInitable *initable = G_ASYNC_INITABLE (source_object);
  UnityWebappsContext *context;
  GSource *source;
  unity_webapps_context_new_data *data;
  GError *error;
  
  error = NULL;
  
  context = UNITY_WEBAPPS_CONTEXT (g_async_initable_new_finish (initable, res, &error)); // TODO: Error 
 
  if (error != NULL)
    {
      g_critical ("Error in unity_webapps_context_ready: %s", error->message);
      g_error_free (error);
      
      return;
    }
  
  data = (unity_webapps_context_new_data *)user_data;
  
  data->webapps_context = context;
  
  source = g_idle_source_new ();
  g_source_set_priority (source, G_PRIORITY_DEFAULT);
  g_source_set_callback (source, complete_in_idle_cb, data, g_free);
  
  g_source_attach (source, data->context);
  g_source_unref (source);
}


void
unity_webapps_context_new (UnityWebappsService *service, const gchar *name, const gchar *domain, const gchar *icon_url, const gchar *mime_types, UnityWebappsContextReadyCallback callback, gpointer user_data)
{
  unity_webapps_context_new_data *data;

  g_return_if_fail (service != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_SERVICE(service));
  g_return_if_fail (name != NULL);
  g_return_if_fail (domain != NULL);
  g_return_if_fail (icon_url != NULL);
  g_return_if_fail (callback != NULL);
  
  data = g_malloc0 (sizeof (unity_webapps_context_new_data));
  data->user_data = user_data;
  data->callback = callback;  
  data->context = g_main_context_get_thread_default () ? g_main_context_get_thread_default () : g_main_context_default ();
  data->webapps_object_needs_unref = FALSE;

  g_async_initable_new_async (UNITY_WEBAPPS_TYPE_CONTEXT,
			      G_PRIORITY_LOW,
			      NULL /* Cancellable */,
			      unity_webapps_context_ready,
			      data,
			      "name", name,
			      "domain", domain,
			      "icon-url", icon_url,
			      "mime-types", mime_types,
			      "service", service,
			      NULL);
}

UnityWebappsContext *
unity_webapps_context_new_lazy (UnityWebappsService *service,
				const gchar *name,
				const gchar *domain,
				const gchar *icon_url,
				const gchar *mime_types)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_SERVICE (service), NULL);
  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (domain != NULL, NULL);
  g_return_val_if_fail (icon_url != NULL, NULL);
  
  return g_object_new (UNITY_WEBAPPS_TYPE_CONTEXT,
		       "service", service,
		       "name", name,
		       "domain", domain,
		       "icon-url", icon_url,
		       "mime-types", mime_types,
		       NULL);
}

void 
unity_webapps_context_prepare (UnityWebappsContext *context, 
			       UnityWebappsContextReadyCallback callback,
			       gpointer user_data)
{
  unity_webapps_context_new_data *data;

  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == TRUE)
    {
      return;
    }
  
  data = g_malloc0 (sizeof (unity_webapps_context_new_data));
  data->user_data = user_data;
  data->callback = callback;
  data->context = g_main_context_get_thread_default () ? g_main_context_get_thread_default () : g_main_context_default ();
  data->webapps_object_needs_unref = TRUE;

  // extension can unref object from another thread, during initialization
  g_async_initable_init_async (G_ASYNC_INITABLE (context),
			       G_PRIORITY_HIGH,
			       NULL,
			       unity_webapps_context_ready,
			       data);
}


UnityWebappsContext *
unity_webapps_context_new_sync (UnityWebappsService *service,
				const gchar *name,
				const gchar *domain,
				const gchar *icon_url,
				const gchar *mime_types)
{
  g_return_val_if_fail (service != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_SERVICE(service), NULL);
  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (domain != NULL, NULL);
  g_return_val_if_fail (icon_url != NULL, NULL);


  return UNITY_WEBAPPS_CONTEXT (g_initable_new (UNITY_WEBAPPS_TYPE_CONTEXT,
						NULL /* Cancellable */,
						NULL, /* Error */
						"name", name,
						"domain", domain,
						"icon-url", icon_url,
						"mime-types", mime_types,
						"service", service,
						NULL));
}

void 
unity_webapps_context_destroy (UnityWebappsContext *context, gboolean user_abandoned)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  context->priv->user_abandoned = user_abandoned;
  g_object_unref (G_OBJECT (context));
}

const gchar *
unity_webapps_context_get_name (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  return unity_webapps_gen_context_get_name (context->priv->context_proxy);
}

const gchar *
unity_webapps_context_get_domain (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  return unity_webapps_gen_context_get_domain (context->priv->context_proxy);
}

const gchar *
unity_webapps_context_get_desktop_name (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  return unity_webapps_gen_context_get_desktop_name (context->priv->context_proxy);
}

gchar *
unity_webapps_context_get_icon_name (UnityWebappsContext *context)
{
  GError *error;
  gchar *icon_name;

  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  error = NULL;
  
  unity_webapps_gen_context_call_get_icon_name_sync (context->priv->context_proxy,
						&icon_name,
						NULL /* Cancellable */,
						&error);
  
  if (error != NULL)
    {
      g_critical ("Failure to invoke proxy method in unity_webapps_context_get_icon_name: %s", error->message);
      g_error_free (error);
      
      return NULL;
    }
  
  return icon_name;
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(add_icon,context,Context,CONTEXT,"Icon succesfully added");

void
unity_webapps_context_add_icon (UnityWebappsContext *context, const gchar *icon_url, gint size)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_add_icon (context->priv->context_proxy,
					   icon_url,
					   size,
					   NULL /* Cancellable */,
					   add_icon_complete_callback,
					   context);
  
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_homepage,context,Context,CONTEXT,"Homepage succesfully updated");

void
unity_webapps_context_set_homepage (UnityWebappsContext *context, const gchar *homepage)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_set_homepage (context->priv->context_proxy,
					       homepage,
					       NULL /* Cancellable */,
					       set_homepage_complete_callback,
					       context);
}


UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(raise,context,Context,CONTEXT,"Raise succesfully requested");

void
unity_webapps_context_raise (UnityWebappsContext *context)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_raise (context->priv->context_proxy,
                                        NULL,
					NULL /* Cancellable */,
					raise_complete_callback,
					context);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(raise_interest,context,Context,CONTEXT,"Interest raise succesfully requested");

void
unity_webapps_context_raise_interest (UnityWebappsContext *context,
				      gint interest_id)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_raise_interest (context->priv->context_proxy,
						 interest_id,
						 NULL /* Cancellable */,
						 raise_interest_complete_callback,
						 context);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(close,context,Context,CONTEXT,"Close succesfully requested");

void
unity_webapps_context_close (UnityWebappsContext *context)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_close (context->priv->context_proxy,
						NULL /* Cancellable */,
					     close_complete_callback,
					     context);

}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(close_interest,context,Context,CONTEXT,"Close interest succesfully requested");

void
unity_webapps_context_close_interest (UnityWebappsContext *context,
				      gint interest_id)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_close_interest (context->priv->context_proxy,
						 interest_id,
						 NULL /* Cancellable */,
						 close_interest_complete_callback,
						 context);
}



typedef struct _unity_webapps_context_raise_callback_data {
  UnityWebappsContext *context;
  UnityWebappsContextRaiseCallback callback;

  gpointer user_data;
} unity_webapps_context_raise_callback_data;

typedef struct _unity_webapps_context_close_callback_data {
  UnityWebappsContext *context;
  UnityWebappsContextCloseCallback callback;

  gpointer user_data;
} unity_webapps_context_close_callback_data;


static void
_close_callback (UnityWebappsGenContext *gen_context,
                 gint interest_id,
                 gpointer user_data)
{
  unity_webapps_context_close_callback_data *data;
  UnityWebappsContext *context;
  UnityWebappsContextCloseCallback callback;

  data = (unity_webapps_context_close_callback_data *)user_data;
  g_return_if_fail (data != NULL);

  callback = data->callback;
  g_return_if_fail (callback != NULL);

  context = data->context;
  g_return_if_fail (context != NULL);

  if (!G_IS_OBJECT(context))
      return;

  if (context->priv->took_interest == FALSE)
    return;

  if ((context->priv->interest_id == interest_id) ||
      (interest_id == UNITY_WEBAPPS_INVALID_INTEREST_ID))
    {
      callback(data->context, data->user_data);
    }
}

static void
_raise_callback (UnityWebappsGenContext *gen_context,
                 gint interest_id,
                 const gchar * const *files,
                 gpointer user_data)
{
  unity_webapps_context_raise_callback_data *data;
  UnityWebappsContext *context;
  UnityWebappsContextRaiseCallback callback;
  const gchar * const *it;

  data = (unity_webapps_context_raise_callback_data *)user_data;
  g_return_if_fail (data != NULL);

  callback = data->callback;
  g_return_if_fail (callback != NULL);

  context = data->context;
  g_return_if_fail (context != NULL);

  if (!G_IS_OBJECT(context))
      return;

  if (context->priv->took_interest == FALSE)
    return;

  if ((context->priv->interest_id == interest_id) ||
      (interest_id == UNITY_WEBAPPS_INVALID_INTEREST_ID))
    {
      if (files && files[0])
        {
          for (it = files; *it; it++)
            callback(data->context, *it, data->user_data);
        }
      else
        callback(data->context, NULL, data->user_data);
    }
}

static void
_free_close_callback_data (gpointer user_data,
			   GClosure *closure)
{
  unity_webapps_context_close_callback_data *data;
  
  data = (unity_webapps_context_close_callback_data *)user_data;
  
  g_free (data);
}

static void
_free_raise_callback_data (gpointer user_data,
			   GClosure *closure)
{
  unity_webapps_context_raise_callback_data *data;
  
  data = (unity_webapps_context_raise_callback_data *)user_data;
  
  g_free (data);
}

void 
unity_webapps_context_on_raise_callback (UnityWebappsContext *context,
					 UnityWebappsContextRaiseCallback callback,
					 gpointer user_data)
{
  unity_webapps_context_raise_callback_data *data;
  
  g_return_if_fail(context != NULL);
  g_return_if_fail(UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_raise_callback_data));
  data->context = context;
  data->callback = callback;
  data->user_data = user_data;
  
  g_signal_connect_data (context->priv->context_proxy, "raise-requested", G_CALLBACK (_raise_callback), data, _free_raise_callback_data, 0);
}

void 
unity_webapps_context_on_close_callback (UnityWebappsContext *context,
					 UnityWebappsContextCloseCallback callback,
					 gpointer user_data)
{
  unity_webapps_context_close_callback_data *data;
  
  g_return_if_fail(context != NULL);
  g_return_if_fail(UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_close_callback_data));
  data->context = context;
  data->callback = callback;
  data->user_data = user_data;
  
  g_signal_connect_data (context->priv->context_proxy, "close-requested", G_CALLBACK (_close_callback), data, _free_close_callback_data, 0);
}

gboolean
unity_webapps_context_get_view_is_active (UnityWebappsContext *context, gint interest_id)
{
  gboolean is_active;
  GError *error;
  
  g_return_val_if_fail (context != NULL, FALSE);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), FALSE);

  if (context->priv->remote_ready == FALSE)
    {
      return FALSE;
    }
  
  error = NULL;
  
  unity_webapps_gen_context_call_get_view_is_active_sync (context->priv->context_proxy,
							  interest_id,
							  &is_active,
							  NULL /* Cancellable */,
							  &error);
  
  if (error != NULL)
    {
      g_critical ("Failure to invoke proxy method in unity_webapps_context_get_view_is_active: %s", error->message);
      
      g_error_free (error);
      
      return FALSE;
    }
  
  return is_active;
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_view_is_active,context,Context,CONTEXT,"Interest activity succesfully updated");

void
unity_webapps_context_set_view_is_active (UnityWebappsContext *context, gboolean is_active)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  if (context->priv->took_interest == FALSE)
    {
      return;
    }
  
  
  unity_webapps_gen_context_call_set_view_is_active (context->priv->context_proxy,
						     context->priv->interest_id,
						     is_active,
						     NULL /* Cancellable */,
						     set_view_is_active_complete_callback,
						     context);
}

typedef struct _unity_webapps_context_notify_data {
  gpointer user_data;
  gint interest_id;
  UnityWebappsContextNotifyCallback callback;
  UnityWebappsContextViewNotifyCallback view_callback;
  UnityWebappsContextLocationNotifyCallback location_callback;
  UnityWebappsContextWindowNotifyCallback window_callback;
  UnityWebappsContextPreviewReadyCallback preview_callback;
  UnityWebappsContext *context;
} unity_webapps_context_notify_data;

static void
on_notify_view_is_active (UnityWebappsGenContext *context,
			  gint interest_id,
			  gboolean view_is_active,
			  gpointer user_data)
{
  unity_webapps_context_notify_data *data;

  data = (unity_webapps_context_notify_data *)user_data;
  g_return_if_fail (data != NULL);
  g_return_if_fail (data->context != NULL);

  if (!G_IS_OBJECT(data->context))
      return;

  if (!data->view_callback)
    return;

  data->view_callback(data->context, interest_id, view_is_active, data->user_data);
}

static void 
_free_context_notify_data (gpointer user_data,
			   GClosure *closure)
{
  unity_webapps_context_notify_data *data;
  
  data = (unity_webapps_context_notify_data *)user_data;
  
  g_free (data);
}

void 
unity_webapps_context_on_view_is_active_changed (UnityWebappsContext *context,
						 UnityWebappsContextViewNotifyCallback callback,
						 gpointer user_data)
{
  unity_webapps_context_notify_data *data;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_notify_data));
  data->user_data = user_data;
  data->view_callback = callback;
  data->context = context;
  
  g_signal_connect_data (context->priv->context_proxy,
			 "view-is-active-changed",
			 G_CALLBACK (on_notify_view_is_active),
			 data,
			 _free_context_notify_data,
			 0);
}

gchar *
unity_webapps_context_get_view_location (UnityWebappsContext *context, gint interest_id)
{
  gchar *location;
  GError *error;
  
  g_return_val_if_fail (context != NULL, FALSE);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), FALSE);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }

  error = NULL;
  
  unity_webapps_gen_context_call_get_view_location_sync (context->priv->context_proxy,
							  interest_id,
							  &location,
							  NULL /* Cancellable */,
							  &error);
  
  if (error != NULL)
    {
      g_critical ("Failure to invoke proxy method in unity_webapps_context_get_view_location: %s", error->message);
      
      g_error_free (error);
      
      return FALSE;
    }
  
  return location;
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_view_location,context,Context,CONTEXT,"Interest location succesfully upadated");

void
unity_webapps_context_set_view_location (UnityWebappsContext *context, const gchar *location)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  if (context->priv->took_interest == FALSE)
    {
      return;
    }
  
  
  unity_webapps_gen_context_call_set_view_location (context->priv->context_proxy,
						    context->priv->interest_id,
						    location,
						    NULL /* Cancellable */,
						    set_view_location_complete_callback,
						    context);
}

static void
on_notify_view_location (UnityWebappsGenContext *context,
			  gint interest_id,
			  const gchar *location,
			  gpointer user_data)
{
  unity_webapps_context_notify_data *data;

  data = (unity_webapps_context_notify_data *)user_data;
  g_return_if_fail (data != NULL);
  g_return_if_fail (data->context != NULL);

  if (!G_IS_OBJECT(data->context))
      return;

  if (!data->location_callback)
    return;

  data->location_callback(data->context, interest_id, location, data->user_data);
}

void 
unity_webapps_context_on_view_location_changed (UnityWebappsContext *context,
						 UnityWebappsContextLocationNotifyCallback callback,
						 gpointer user_data)
{
  unity_webapps_context_notify_data *data;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_notify_data));
  data->user_data = user_data;
  data->location_callback = callback;
  data->context = context;
  
  g_signal_connect_data (context->priv->context_proxy,
			 "view-location-changed",
			 G_CALLBACK (on_notify_view_location),
			 data,
			 _free_context_notify_data,
			 0);
}

const gchar *
unity_webapps_context_get_context_name (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  return context->priv->context_name;
}

UnityWebappsContext *
unity_webapps_context_new_for_context_name (UnityWebappsService *service,
					    const gchar *context_name)
{
  g_return_val_if_fail (service != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_SERVICE(service), NULL);
  g_return_val_if_fail (context_name != NULL, NULL);
  
  return UNITY_WEBAPPS_CONTEXT (g_initable_new (UNITY_WEBAPPS_TYPE_CONTEXT,
						NULL /* Cancellable */,
						NULL /* Error */,
						"context-name",
						context_name,
						"service", service,
						NULL));

}

GVariant *
unity_webapps_context_list_interests (UnityWebappsContext *context)
{
  GVariant *out_interests;
  GError *error;
  
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  error = NULL;
  
  unity_webapps_gen_context_call_list_interests_sync (context->priv->context_proxy,
						     &out_interests,
						     NULL /* Cancellable */,
						     &error);
  
  if (error != NULL)
    {
      g_warning ("Failure calling ListInterests: %s", error->message);
      g_error_free (error);
      
      return NULL;
    }
  
  return out_interests;
}

static void
on_notify_interests_changed (UnityWebappsGenService *gen_service,
			     gint interest_id,
			    gpointer user_data)
{
  unity_webapps_context_notify_data *data;
  UnityWebappsContext *context;

  data = (unity_webapps_context_notify_data *)user_data;
  g_return_if_fail (data != NULL);

  context = data->context;
  g_return_if_fail (context != NULL);

  if (!G_IS_OBJECT (context))
    return;

  data->callback(data->context, interest_id, data->user_data);
}

void
unity_webapps_context_on_interest_appeared (UnityWebappsContext *context,
					   UnityWebappsContextNotifyCallback callback,
					   gpointer user_data)
{
  unity_webapps_context_notify_data *data;
  
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_notify_data));
  data->user_data = user_data;
  data->callback = callback;
  data->context = context;
  
  g_signal_connect_data (context->priv->context_proxy,
			 "interest-appeared",
			 G_CALLBACK (on_notify_interests_changed),
			 data,
			 _free_context_notify_data,
			 0);
}

void
unity_webapps_context_on_interest_vanished (UnityWebappsContext *context,
					   UnityWebappsContextNotifyCallback callback,
					   gpointer user_data)
{
  unity_webapps_context_notify_data *data;
  
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_notify_data));
  data->user_data = user_data;
  data->callback = callback;
  data->context = context;
  
  g_signal_connect_data (context->priv->context_proxy,
			 "interest-vanished",
			 G_CALLBACK (on_notify_interests_changed),
			 data,
			 _free_context_notify_data,
			 0);
}


gchar *
unity_webapps_context_get_interest_owner (UnityWebappsContext *context, gint interest_id)
{
  GError *error;
  gchar *interest_owner;

  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  if (context->priv->remote_ready == FALSE)
    {
      return NULL;
    }
  
  error = NULL;
  
  unity_webapps_gen_context_call_get_interest_owner_sync (context->priv->context_proxy,
							  interest_id,
							  &interest_owner,
							  NULL /* Cancellable */,
							  &error);
  
  if (error != NULL)
    {
      g_critical ("Failure to invoke proxy method in unity_webapps_context_get_interest_owner: %s", error->message);
      g_error_free (error);
      
      return NULL;
    }
  
  return interest_owner;
}

guint64
unity_webapps_context_get_view_window (UnityWebappsContext *context, gint interest_id)
{
  guint64 window;
  GError *error;
  
  g_return_val_if_fail (context != NULL, FALSE);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), FALSE);

  if (context->priv->remote_ready == FALSE)
    {
      return FALSE;
    }
  
  error = NULL;
  
  unity_webapps_gen_context_call_get_view_window_sync (context->priv->context_proxy,
							  interest_id,
							  &window,
							  NULL /* Cancellable */,
							  &error);
  
  if (error != NULL)
    {
      g_critical ("Failure to invoke proxy method in unity_webapps_context_get_view_window: %s", error->message);
      
      g_error_free (error);
      
      return FALSE;
    }
  
  return window;
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_view_window,context,Context,CONTEXT,"Interest window succesfully updated");

void
unity_webapps_context_set_view_window (UnityWebappsContext *context, guint64 window)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  if (context->priv->took_interest == FALSE)
    {
      return;
    }
  
  
  unity_webapps_gen_context_call_set_view_window (context->priv->context_proxy,
						  context->priv->interest_id,
						  window,
						  NULL /* Cancellable */,
						  set_view_window_complete_callback,
						  context);
  
}

static void
on_notify_view_window (UnityWebappsGenContext *context,
			  gint interest_id,
		       guint64 window,
			  gpointer user_data)
{
  unity_webapps_context_notify_data *data;

  data = (unity_webapps_context_notify_data *)user_data;
  g_return_if_fail (data != NULL);
  g_return_if_fail (data->context != NULL);

  if (!G_IS_OBJECT (data->context))
    return;

  if (data->window_callback != NULL)
    data->window_callback(data->context, interest_id, window, data->user_data);
}

void 
unity_webapps_context_on_view_window_changed (UnityWebappsContext *context,
					      UnityWebappsContextWindowNotifyCallback callback,
					      gpointer user_data)
{
  unity_webapps_context_notify_data *data;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_notify_data));
  data->user_data = user_data;
  data->window_callback = callback;
  data->context = context;
  
  g_signal_connect_data (context->priv->context_proxy,
			 "view-window-changed",
			 G_CALLBACK (on_notify_view_window),
			 data,
			 _free_context_notify_data,
			 0);
}

void
unity_webapps_context_set_preview_requested_callback (UnityWebappsContext *context,
						      UnityWebappsContextPreviewCallback callback,
						      gpointer user_data)
{
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  context->priv->preview_callback = callback;
  context->priv->preview_user_data = user_data;
}

static void
request_preview_complete_callback (GObject *source_object,
				   GAsyncResult *res,
				   gpointer user_data)
{
  UnityWebappsGenContext *context_proxy;
  gchar *preview_data;
  unity_webapps_context_notify_data *data;
  UnityWebappsContext *context;
  GError *error;

  context_proxy = UNITY_WEBAPPS_GEN_CONTEXT (source_object);

  data = (unity_webapps_context_notify_data *)user_data;
  g_return_if_fail (data != NULL);

  context = data->context;
  g_return_if_fail (context != NULL);

  if (!G_IS_OBJECT(context))
      return;
  
  error = NULL;
  
  unity_webapps_gen_context_call_request_preview_finish (context_proxy, &preview_data,
							 res, &error);
  
  if (error != NULL)
    {

      g_warning ("Error calling RequestPreview method: %s", error->message);
      g_error_free (error);
      g_free (data);
      
      return;
    }
  
  if (data->preview_callback)
    data->preview_callback (data->context, data->interest_id, preview_data, data->user_data);
  
  g_free (data);
  g_free (preview_data);
}

void
unity_webapps_context_request_preview (UnityWebappsContext *context,
				       gint interest_id,
				       UnityWebappsContextPreviewReadyCallback callback,
				       gpointer user_data)
{
  unity_webapps_context_notify_data *data;
  
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);

  if (context->priv->remote_ready == FALSE)
    return;
  
  data = g_malloc0 (sizeof (unity_webapps_context_notify_data));
  
  data->preview_callback = callback;
  data->context = context;
  data->interest_id = interest_id;
  data->user_data = user_data;
  
  unity_webapps_gen_context_call_request_preview (context->priv->context_proxy,
						  interest_id,
						  NULL /* Cancellable */,
						  request_preview_complete_callback,
						  data);
  

}

static void
remove_action_complete_callback (GObject *source_object,
			      GAsyncResult *res,
			      gpointer user_data)
{
  UnityWebappsGenContext *proxy;
  GError *error;
  
  proxy = UNITY_WEBAPPS_GEN_CONTEXT (source_object);
  
  error = NULL;
  
  unity_webapps_gen_context_call_remove_application_action_finish (proxy, res, &error);
  
  if (error != NULL)
    {
      g_warning ("Error calling RemoveApplicationAction method of Context context: %s", error->message);
      
      g_error_free (error);
      
      return;
    }
  
  UNITY_WEBAPPS_NOTE (INDICATOR, "Received response, action succesfully removeded");
}

gchar** unity_webapps_context_get_application_accept_data (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);

  // TODO: Rate limit
  if (context->priv->remote_ready == FALSE)
    return NULL;

  gchar **mimes = NULL;
  GError *error = NULL;

  unity_webapps_gen_context_call_get_application_accept_data_sync (context->priv->context_proxy,
                                                                   &mimes, NULL, &error);
  if (error)
    {
      g_warning ("Error calling GetApplicationAcceptData method of Context context: %s", error->message);

      g_error_free (error);

      return NULL;
    }

  return mimes;
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_application_accept_data, context, Context, CONTEXT, "actions succesfully added");

void unity_webapps_context_set_application_accept_data (UnityWebappsContext *context,
                                                        UnityWebappsStrWrapperDesc *wmimes,
                                                        gint len)
{
  gint i;
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  // TODO: Rate limit
  if (context->priv->remote_ready == FALSE || len <= 0)
    return;

  const gchar * mimes[len + 1];

  for (i = 0; i < len; i++)
    mimes[i] = wmimes[i].str;
  mimes[len] = NULL;

  unity_webapps_gen_context_call_set_application_accept_data (context->priv->context_proxy,
                                                              mimes,
                                                              context->priv->interest_id,
                                                              NULL,
                                                              set_application_accept_data_complete_callback,
                                                              context);
}

#define MAXIMUM_ACTION_LABEL_LENGTH 400

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(add_application_actions, context, Context, CONTEXT, "actions succesfully added");

void unity_webapps_context_add_application_actions (UnityWebappsContext *context,
                                                    UnityWebappsApplicationActionDesc *actions,
                                                    gint len)
{
  gint i, k;
  gchar *sanitized_path;
  const char *path;
  unity_webapps_context_action_data *data;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  // TODO: Rate limit?

  if (context->priv->remote_ready == FALSE || len <= 0)
    return;

  const gchar * paths[len + 1];

  for (i = 0, k = 0; i < len; i++)
    {
      path = actions[i].path;

      if (!path)
        continue;

      paths[k] = path;

      sanitized_path = unity_webapps_sanitizer_limit_string_argument (path, MAXIMUM_ACTION_LABEL_LENGTH);

      data = g_malloc0 (sizeof (unity_webapps_context_action_data));
      data->callback = actions[i].callback;
      data->user_data = actions[i].user_data;

      g_hash_table_insert (context->priv->action_callbacks_by_name, sanitized_path, data);

      UNITY_WEBAPPS_NOTE (CONTEXT, "Adding application action: %s", path);

      k++;
    }
  paths[k] = NULL;

  unity_webapps_gen_context_call_add_application_actions (context->priv->context_proxy,
                                                          paths,
                                                          context->priv->interest_id,
                                                          NULL,
                                                          add_application_actions_complete_callback,
                                                          context);
}

void
unity_webapps_context_remove_application_action (UnityWebappsContext *context,
						 const gchar *path)
{
  gchar *sanitized_path;

  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  sanitized_path = unity_webapps_sanitizer_limit_string_argument (path, MAXIMUM_ACTION_LABEL_LENGTH);
  
  unity_webapps_gen_context_call_remove_application_action (context->priv->context_proxy,
							    sanitized_path,
							    context->priv->interest_id,
							    NULL /* Cancellable */,
							    remove_action_complete_callback,
							    context);
  
  g_hash_table_remove (context->priv->action_callbacks_by_name, sanitized_path);
  
  UNITY_WEBAPPS_NOTE(CONTEXT, "Removing application action: %s", sanitized_path);
  g_free (sanitized_path);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(remove_application_actions,context,Context,CONTEXT,"Actions succesfully cleared");


void
unity_webapps_context_remove_application_actions (UnityWebappsContext *context)
						
{
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));

  if (context->priv->remote_ready == FALSE)
    return;
  
  unity_webapps_gen_context_call_remove_application_actions (context->priv->context_proxy,
							    context->priv->interest_id,
							    NULL /* Cancellable */,
							    remove_application_actions_complete_callback,
							    context);

  g_hash_table_remove_all (context->priv->action_callbacks_by_name);
}

UnityWebappsService *
unity_webapps_context_get_service (UnityWebappsContext *context)
{
  return context->priv->service;
}

gint
unity_webapps_context_get_focus_interest (UnityWebappsContext *context)
{

  if (context->priv->remote_ready == FALSE)
    {
      return 0;
    }

  return unity_webapps_gen_context_get_focus_interest (context->priv->context_proxy);
}

gboolean
unity_webapps_context_shutdown (UnityWebappsContext *context)
{
  GError *error;
  if (context->priv->remote_ready == FALSE)
    {
      return FALSE;
    }
  
  error = NULL;
  unity_webapps_gen_context_call_shutdown_sync (context->priv->context_proxy, NULL,
						&error);
  
  if (error != NULL)
    {
      g_critical ("Error shutting down context: %s", error->message);
      g_error_free (error);
      
      return FALSE;
    }
  
  return TRUE;
}


gint
unity_webapps_context_get_interest_id (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, UNITY_WEBAPPS_INVALID_INTEREST_ID);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), UNITY_WEBAPPS_INVALID_INTEREST_ID);

  if (   context->priv->remote_ready == FALSE
      || context->priv->took_interest == FALSE)
    {
      return UNITY_WEBAPPS_INVALID_INTEREST_ID;
    }

  return context->priv->interest_id;
}
