/*
 * unity-webapps-indicator-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-indicator-context.h"
#include "unity-webapps-context-private.h"

#include "unity-webapps-sanitizer.h"

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

#include "unity-webapps-debug.h"

typedef struct _unity_webapps_indicator_action_data {
  UnityWebappsContext *context;

  UnityWebappsIndicatorCallback callback;
  gpointer user_data;
} unity_webapps_indicator_action_data;

static void
_indicator_context_action_invoked (UnityWebappsGenIndicator *indicator,
				   const gchar *label,
				   gpointer user_data)
{
  UnityWebappsContext *context;
  unity_webapps_indicator_action_data *data;
  
  context = (UnityWebappsContext *)user_data;
  g_return_if_fail(context != NULL);

  if (!G_IS_OBJECT(context))
    return;

  data = g_hash_table_lookup (context->priv->indicator_context->menu_callbacks_by_name, label);
  
  if ((data != NULL) && (data->callback != NULL))
    {
      UNITY_WEBAPPS_NOTE (INDICATOR, "Menu action invoked: %s", label);
      data->callback(context, data->user_data);
      return;
    }
  
  data = g_hash_table_lookup (context->priv->indicator_context->indicator_callbacks_by_name, label);
  
  if ((data != NULL) && (data->callback != NULL))
    {
      UNITY_WEBAPPS_NOTE (INDICATOR, "Indicator action invoked: %s", label);
      data->callback(context, data->user_data);
      return;
    }

}


static void
presence_changed_notify (GObject *object,
			 GParamSpec *pspec,
			 gpointer user_data)
{
  unity_webapps_indicator_action_data *data;
  
  data = (unity_webapps_indicator_action_data *)user_data;
  g_return_if_fail(data != NULL);

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

  if (data->callback != NULL)
    data->callback (data->context, data->user_data);
}

UnityWebappsIndicatorContext *
unity_webapps_indicator_context_new (UnityWebappsContext *main_context, GError **error)
{
  UnityWebappsIndicatorContext *context;
  
  context = g_malloc0 (sizeof (UnityWebappsIndicatorContext));
  context->indicator_rate = 0;
  
  context->menu_callbacks_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  context->indicator_callbacks_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  
  context->indicator_proxy = 
    unity_webapps_gen_indicator_proxy_new_sync (unity_webapps_service_get_connection (main_context->priv->service) ,
						G_DBUS_PROXY_FLAGS_NONE,
						main_context->priv->context_name,
						UNITY_WEBAPPS_INDICATOR_PATH, 
						NULL /* Cancellable */,
						error);
  
  if (error && (*error != NULL))
    {
      g_critical ("Error creating indicator context proxy object for %s: %s", main_context->priv->context_name, (*error)->message);
      
      return NULL;
    }
  
  g_signal_connect (context->indicator_proxy, "action-invoked", G_CALLBACK (_indicator_context_action_invoked), main_context);

  
  return context;
}

void 
unity_webapps_indicator_context_free (UnityWebappsIndicatorContext *context)
{
  g_return_if_fail (context != NULL);

  g_hash_table_destroy (context->menu_callbacks_by_name);
  g_hash_table_destroy (context->indicator_callbacks_by_name);
 

  g_object_unref (G_OBJECT (context->indicator_proxy));
  
  g_free (context);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(show_indicator,indicator,Indicator,INDICATOR,"Indicator succesfully showed");

#define MAXIMUM_NAME_LENGTH 45

void
unity_webapps_indicator_show_indicator (UnityWebappsContext *context,
					const gchar *name)
{
  gchar *sanitized_name;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (name != NULL);
  
  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }
  
  sanitized_name = unity_webapps_sanitizer_limit_string_argument (name, MAXIMUM_NAME_LENGTH);
  unity_webapps_gen_indicator_call_show_indicator (context->priv->indicator_context->indicator_proxy,
						   sanitized_name,
						   context->priv->interest_id,
						   NULL /* Cancellable */,
						   show_indicator_complete_callback,
						   context);

  g_free (sanitized_name);

  UNITY_WEBAPPS_NOTE (INDICATOR, "Showed indicator: %s", name);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(clear_indicator,indicator,Indicator,INDICATOR,"Indicator succesfully cleared");

void
unity_webapps_indicator_clear_indicator (UnityWebappsContext *context,
					 const gchar *name)
{
  gchar *sanitized_name;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (name != NULL);
  
  sanitized_name = unity_webapps_sanitizer_limit_string_argument (name, MAXIMUM_NAME_LENGTH);

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }
  
  g_return_if_fail (name != NULL);
  
  unity_webapps_gen_indicator_call_clear_indicator (context->priv->indicator_context->indicator_proxy,
						    sanitized_name,
						    context->priv->interest_id,
						    NULL /* Cancellable */,
						    clear_indicator_complete_callback,
						    context);
  
  g_free (sanitized_name);
  UNITY_WEBAPPS_NOTE (INDICATOR, "Cleared indicator: %s", name);
  
}

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

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }

  unity_webapps_gen_indicator_call_clear_indicators (context->priv->indicator_context->indicator_proxy,
						     context->priv->interest_id,
                                                     NULL /* Cancellable */,
                                                     clear_indicator_complete_callback,
                                                     context);
  UNITY_WEBAPPS_NOTE (INDICATOR, "Clearing indicators");
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(add_action,indicator,Indicator,INDICATOR,"Action succesfully added");
#define MAXIMUM_LABEL_LENGTH 60

void
unity_webapps_indicator_add_action (UnityWebappsContext *context,
				    const gchar *label,
				    UnityWebappsIndicatorCallback callback,
				    gpointer user_data)
{
  unity_webapps_indicator_action_data *data;
  gchar *sanitized_label;

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

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }
  
  sanitized_label = unity_webapps_sanitizer_limit_string_argument (label, MAXIMUM_LABEL_LENGTH);

  unity_webapps_gen_indicator_call_add_action (context->priv->indicator_context->indicator_proxy,
					       sanitized_label,
					       context->priv->interest_id,
					       NULL /* Cancellable */,
					       add_action_complete_callback,
					       context);
  
  
  // TODO: Analyze the error case with duplicates.
  data = g_malloc0 (sizeof (unity_webapps_indicator_action_data));
  data->callback = callback;
  data->user_data = user_data;

  g_hash_table_insert (context->priv->indicator_context->menu_callbacks_by_name, g_strdup (label), data);
  
  g_free (sanitized_label);
  
  UNITY_WEBAPPS_NOTE (INDICATOR, "Added action: %s", label);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_property,indicator,Indicator,INDICATOR,"Property succesfully updated");


#define MAXIMUM_PROPERTY_LENGTH 40
#define MAXIMUM_VALUE_LENGTH 1024


void 
unity_webapps_indicator_set_property (UnityWebappsContext *context, 
				      const gchar *name, 
				      const gchar *property, 
				      const gchar *value)
{
  gchar *sanitized_name, *sanitized_property, *sanitized_value;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (name != NULL);
  g_return_if_fail (property != NULL);
  g_return_if_fail (value != NULL);

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }
  
  sanitized_name = unity_webapps_sanitizer_limit_string_argument (name, MAXIMUM_NAME_LENGTH);
  sanitized_property = unity_webapps_sanitizer_limit_string_argument (property, MAXIMUM_PROPERTY_LENGTH);
  sanitized_value = unity_webapps_sanitizer_limit_string_argument (value, MAXIMUM_VALUE_LENGTH);
  
  unity_webapps_gen_indicator_call_set_property (context->priv->indicator_context->indicator_proxy,
						 sanitized_name,
						 sanitized_property,
						 sanitized_value,
						 context->priv->interest_id,
						 NULL /* Cancellable */,
						 set_property_complete_callback,
						 context);
  
  g_free (sanitized_name);
  g_free (sanitized_property);
  g_free (sanitized_value);
  
  UNITY_WEBAPPS_NOTE (INDICATOR, "Setting property of indicator %s: %s->%s", name, property, value);
}


UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(set_property_icon,indicator,Indicator,INDICATOR,"Icon Property succesfully updated");


void 
unity_webapps_indicator_set_property_icon (UnityWebappsContext *context, 
				      const gchar *name, 
				      const gchar *property, 
				      const gchar *value)
{
  gchar *sanitized_name, *sanitized_property, *sanitized_value;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (name != NULL);
  g_return_if_fail (property != NULL);
  g_return_if_fail (value != NULL);

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }

  sanitized_name = unity_webapps_sanitizer_limit_string_argument (name, MAXIMUM_NAME_LENGTH);
  sanitized_property = unity_webapps_sanitizer_limit_string_argument (property, MAXIMUM_PROPERTY_LENGTH);
  sanitized_value = unity_webapps_sanitizer_limit_string_argument (value, MAXIMUM_VALUE_LENGTH);
  
  
  unity_webapps_gen_indicator_call_set_property (context->priv->indicator_context->indicator_proxy,
						 sanitized_name,
						 sanitized_property,
						 sanitized_value,
						 context->priv->interest_id,
						 NULL /* Cancellable */,
						 set_property_icon_complete_callback,
						 context);
  
  g_free (sanitized_name);
  g_free (sanitized_property);
  g_free (sanitized_value);
  
  UNITY_WEBAPPS_NOTE (INDICATOR, "Setting icon property of indicator %s: %s->%s", name,
		      property, value);
}

void 
unity_webapps_indicator_set_callback (UnityWebappsContext *context, 
				      const gchar *name, 
				      UnityWebappsIndicatorCallback callback,
				      gpointer user_data)
{
  UnityWebappsIndicatorContext *indicator_context;
  gchar *sanitized_name;
  unity_webapps_indicator_action_data *data;

  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (name != NULL);
  g_return_if_fail (callback != NULL);
  
  // The hash table takes ownership of this.
  sanitized_name = unity_webapps_sanitizer_limit_string_argument (name, MAXIMUM_NAME_LENGTH);
		   
  
  indicator_context = context->priv->indicator_context;
  
  data = g_malloc0 (sizeof (unity_webapps_indicator_action_data));
  data->callback = callback;
  data->user_data = user_data;
  
  g_hash_table_insert (indicator_context->indicator_callbacks_by_name, 
		       (gpointer) sanitized_name, 
		       data);
}

gchar *
unity_webapps_indicator_get_presence (UnityWebappsContext *context)
{
  g_return_val_if_fail (context != NULL, NULL);
  g_return_val_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context), NULL);
  
  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return FALSE;
    }
  return g_strdup (unity_webapps_gen_indicator_get_presence (context->priv->indicator_context->indicator_proxy));
}

static void
_free_presence_callback_data (gpointer user_data,
			      GClosure *closure)
{
  unity_webapps_indicator_action_data *data;
  
  data = (unity_webapps_indicator_action_data *)user_data;

  g_free (data);
}

void 
unity_webapps_indicator_on_presence_changed_callback (UnityWebappsContext *context,
						      UnityWebappsIndicatorCallback callback,
						      gpointer user_data)
{
  unity_webapps_indicator_action_data *data;
  g_return_if_fail (context != NULL);
  g_return_if_fail (UNITY_WEBAPPS_IS_CONTEXT (context));
  g_return_if_fail (callback != NULL);
  
  data = g_malloc0 (sizeof (unity_webapps_indicator_action_data));
  data->callback = callback;
  data->user_data = user_data;
  data->context = context;
  
  g_signal_connect_data (context->priv->indicator_context->indicator_proxy, "notify::presence", G_CALLBACK (presence_changed_notify), data, _free_presence_callback_data, 0);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(remove_action,indicator,Indicator,INDICATOR,"Action succesfully removed");

void
unity_webapps_indicator_remove_action (UnityWebappsContext *context, const gchar *label)
{
  gchar *sanitized_label;

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

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }
  
  sanitized_label = unity_webapps_sanitizer_limit_string_argument (label, MAXIMUM_LABEL_LENGTH);
  
  unity_webapps_gen_indicator_call_remove_action (context->priv->indicator_context->indicator_proxy,
						 sanitized_label,
						 context->priv->interest_id,
						 NULL /* Cancellable */,
						 remove_action_complete_callback,
						 context);
  
  g_hash_table_remove (context->priv->indicator_context->menu_callbacks_by_name, sanitized_label);
  
  UNITY_WEBAPPS_NOTE (INDICATOR, "Removing action: %s", label);

  g_free (sanitized_label);
}

UNITY_WEBAPPS_DEFINE_VOID_DBUS_HANDLER(remove_actions,indicator,Indicator,INDICATOR,"Action succesfully removed");

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

  if (unity_webapps_rate_check_indicator_rate_limit (context) == FALSE)
    {
      return;
    }
  
  unity_webapps_gen_indicator_call_remove_actions (context->priv->indicator_context->indicator_proxy,
						  context->priv->interest_id,
						  NULL /* Cancellable */,
						  remove_actions_complete_callback,
						  context);
  
  g_hash_table_remove_all (context->priv->indicator_context->menu_callbacks_by_name);
  
  UNITY_WEBAPPS_NOTE (INDICATOR, "Removing all actions");
}
