/*
 * unity-webapps-action-manager.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 <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <gio/gio.h>

#include <libdbusmenu-glib/menuitem.h>
#include <libdbusmenu-glib/server.h>

#include "unity-webapps-action-manager.h"
#include "unity-webapps-interest-manager.h"
#include "unity-webapps-interest-tracker.h"

#include "../unity-webapps-debug.h"

struct _UnityWebappsActionManagerPrivate {
  UnityWebappsInterestTracker *interest_tracker;

  DbusmenuServer *menu_server;
  DbusmenuMenuitem *menu_item;

  UnityWebappsInterestManager *interest_manager;
  
  GHashTable *actions_by_path;
  GHashTable *infos_by_interest_id;
  
  gchar *menu_path;

  gboolean use_hierarchy;
  gboolean track_activity;
};

typedef struct _UnityWebappsActionInfo {
  UnityWebappsActionManager *manager;
  DbusmenuMenuitem *item;

  gchar *path;
  
  guint ref_count;
  
  struct _UnityWebappsActionInfo *parent;
} UnityWebappsActionInfo;

typedef struct _UnityWebappsHasActionData {
  UnityWebappsActionManager *manager;
  const gchar *path;
} UnityWebappsHasActionData;

G_DEFINE_TYPE(UnityWebappsActionManager, unity_webapps_action_manager, G_TYPE_OBJECT)

enum {
  ACTION_INVOKED,
  LAST_SIGNAL
};

enum {
  PROP_0,
  PROP_MENU_OBJECT_PATH,
  PROP_USE_HIERARCHY,
  PROP_TRACK_ACTIVITY,
  PROP_INTEREST_TRACKER
};

static guint signals[LAST_SIGNAL] = { 0 };

static UnityWebappsActionInfo * add_menuitem_by_action_path (UnityWebappsActionManager *manager, const gchar *path);

#define UNITY_WEBAPPS_ACTION_MANAGER_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), UNITY_WEBAPPS_TYPE_ACTION_MANAGER, UnityWebappsActionManagerPrivate))

static void
unity_webapps_action_info_ref (UnityWebappsActionInfo *info)
{
  info->ref_count = info->ref_count + 1;
  
  UNITY_WEBAPPS_NOTE (ACTION, "Referenced action (%s) new count is %u",
		      info->path, info->ref_count);
  
  if (info->parent != NULL)
    {
      unity_webapps_action_info_ref (info->parent);
    }
}

static void
unity_webapps_action_info_unref (UnityWebappsActionInfo *info)
{
  UnityWebappsActionInfo *parent;

  info->ref_count = info->ref_count - 1;
  
  UNITY_WEBAPPS_NOTE (ACTION, "Unreferenced action (%s) new count is %u",
		      info->path, info->ref_count);

  parent = info->parent;
  
  if (info->ref_count == 0)
    {
      dbusmenu_menuitem_child_delete (dbusmenu_menuitem_get_parent (info->item),
				      info->item);
      
      g_hash_table_remove (info->manager->priv->actions_by_path, info->path);
    }
  
  if (parent != NULL)
    {
      unity_webapps_action_info_unref (parent);
    }
}


static UnityWebappsActionInfo *
unity_webapps_action_info_new (UnityWebappsActionManager *manager,
			       const gchar *path,
			       DbusmenuMenuitem *item,
			       UnityWebappsActionInfo *parent)
{
  UnityWebappsActionInfo *info;
  
  info = g_malloc0 (sizeof (UnityWebappsActionInfo));
  
  info->manager = manager;
  info->path = g_strdup (path);
  
  info->parent = parent;
  g_object_set_qdata (G_OBJECT (item), g_quark_from_static_string("uwa-action"), info);
  
  if (info->parent != NULL)
    {
      unity_webapps_action_info_ref (info->parent);
    }

  info->item = item;
  
  info->ref_count = 1;
  
  return info;
}

static void
unity_webapps_action_info_free (UnityWebappsActionInfo *info)
{
  g_free (info->path);
  
  g_free (info);
}

static gboolean
interest_has_action (UnityWebappsActionManager *manager,
		     gint interest_id,
		     const gchar *path)
{
  GList *action_paths;
  
  action_paths = g_hash_table_lookup (manager->priv->infos_by_interest_id,
				      GINT_TO_POINTER (interest_id));
  
  if (action_paths == NULL)
    {
      return FALSE;
    }
  
  while (action_paths != NULL)
    {
      UnityWebappsActionInfo *info;
      
      info = (UnityWebappsActionInfo *)(action_paths->data);
      
      if (g_strcmp0 (info->path, path) == 0)
	return TRUE;
      
      action_paths = action_paths->next;
    }

  return FALSE;
}

static gint
interest_has_action_comparefunc (gpointer a, gpointer b)
{
  gint interest_id = GPOINTER_TO_INT (a);
  UnityWebappsHasActionData *data = (UnityWebappsHasActionData*) b;
  if (interest_has_action (data->manager, interest_id, data->path)) {
    return 0;
  }
  return 1;
}

static gboolean
interest_add_action (UnityWebappsActionManager *manager,
		     gint interest_id,
		     UnityWebappsActionInfo *info)
{
  GList *action_infos;
  gboolean interest_is_active;

#ifdef UNITY_WEBAPPS_ENABLE_DEBUG
  if (interest_has_action (manager, interest_id, info->path) == TRUE)
    {
      g_critical ("Trying to add a reference to an action where one exists...this should never happen");
      return FALSE;
    }
#endif
  
  action_infos = g_hash_table_lookup (manager->priv->infos_by_interest_id,
				      GINT_TO_POINTER (interest_id));
  
  action_infos = g_list_append (action_infos, info);
  
  g_hash_table_insert (manager->priv->infos_by_interest_id,
		       GINT_TO_POINTER (interest_id),
		       action_infos);
  
  interest_is_active = unity_webapps_interest_tracker_get_interest_is_active (manager->priv->interest_tracker, interest_id);

  if (manager->priv->track_activity == TRUE)
    {
      if ((interest_is_active == FALSE) && (info->ref_count == 1))
	{
	  dbusmenu_menuitem_property_set_bool (info->item, "visible", FALSE);
	}
      else if (interest_is_active == TRUE)
	{
	  dbusmenu_menuitem_property_set_bool (info->item, "visible", TRUE);
	}
    }
  
  UNITY_WEBAPPS_NOTE (ACTION, "Interest id %d is interested in action %s",
		      interest_id,
		      info->path);
  
  return TRUE;
}

static gboolean
interest_remove_action (UnityWebappsActionManager *manager,
			gint interest_id,
			UnityWebappsActionInfo *info)
{
  GList *action_infos;
  
  action_infos = g_hash_table_lookup (manager->priv->infos_by_interest_id,
				      GINT_TO_POINTER (interest_id));
  
  action_infos = g_list_remove (action_infos, info);
  
  g_hash_table_insert (manager->priv->infos_by_interest_id,
		       GINT_TO_POINTER (interest_id),
		       action_infos);
  
  UNITY_WEBAPPS_NOTE (ACTION, "Interest id %d is no longer interested in action %s",
		      interest_id, info->path);

  return TRUE;
}



static gchar **
unity_webapps_action_manager_split_action_path (UnityWebappsActionManager *manager, const gchar *action_path)
{
  gchar **components;
  
  if (manager->priv->use_hierarchy == FALSE)
    {
      components = g_malloc (2 * sizeof (gchar *));
      components[0] = g_strdup (action_path);
      components[1] = NULL;
      
      return components;
    }
  if (action_path[0] != '/')
    {
      return NULL;
    }
  
  components = g_strsplit (action_path+1, "/", -1);
  
  if (g_strv_length (components) > 3)
    {
      g_strfreev (components);
      return NULL;
    }
  
  return components;
}

static gchar *
unity_webapps_action_manager_normalize_path (UnityWebappsActionManager *action_manager,
					     const gchar *path)
{
  GString *normalized_path;
  gchar **components, *ret;
  gint i, len;
  
  components = unity_webapps_action_manager_split_action_path (action_manager, path);
  
  if (components == NULL)
    {
      return NULL;
    }
  
  len = g_strv_length (components);
  
  if (action_manager->priv->use_hierarchy == TRUE)
    {
      normalized_path = g_string_new ("/");
    }
  else
    {
      normalized_path = g_string_new ("");
    }
  
  for (i = 0; i < len; i++)
    {
      gchar *component;
      
      component = g_strstrip(components[i]);
      normalized_path = g_string_append (normalized_path, component);
      
      if ((i + 1) != len)
	{
	  normalized_path = g_string_append (normalized_path, "/");
	}
    }
  ret = normalized_path->str;

  g_string_free (normalized_path, FALSE);
  g_strfreev (components);
  
  return ret;
}

static void
on_menuitem_activated (DbusmenuMenuitem *item,
		       guint timestamp,
		       gpointer user_data)
{
  UnityWebappsActionInfo *info;
  
  info = (UnityWebappsActionInfo *)user_data;
  
  if (info->path == NULL)
    {
      UNITY_WEBAPPS_NOTE(ACTION, "Warning: Got a menu item activation (%p), which we do not have a path for.", item);
    }

  UNITY_WEBAPPS_NOTE (ACTION, "Dispatching action: %s", info->path);
  g_signal_emit (info->manager, signals[ACTION_INVOKED], 0, info->path);
}

static DbusmenuMenuitem *
menuitem_find_child_by_label (DbusmenuMenuitem *item,
			      const gchar *label)
{
  const GList *children;
  const GList *walk;
  
  children = dbusmenu_menuitem_get_children (item);
  
  for (walk = children;
       walk != NULL;
       walk = walk->next)
    {
      DbusmenuMenuitem *child;
      
      const gchar *child_label;
      
      child = (DbusmenuMenuitem *)walk->data;
      child_label = dbusmenu_menuitem_property_get (child, "label");
      
      if (g_strcmp0 (child_label, label) == 0)
	{
	  return child;
	}
    }
  
  return NULL;
}

static DbusmenuMenuitem *
add_menuitem_by_name (UnityWebappsActionManager *manager,
		      DbusmenuMenuitem *parent,
		      const gchar *name,
		      const gchar *full_path,
		      UnityWebappsActionInfo **out_info)
{
  DbusmenuMenuitem *action_item;
  UnityWebappsActionInfo *info, *parent_info;

  if (menuitem_find_child_by_label (parent, name) != NULL)
    {
      return menuitem_find_child_by_label (parent, name);
    }
  
  action_item = dbusmenu_menuitem_new ();

  dbusmenu_menuitem_property_set (action_item, DBUSMENU_MENUITEM_PROP_LABEL, name);
  dbusmenu_menuitem_property_set_bool (action_item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
  
  parent_info = g_object_get_qdata (G_OBJECT (parent), g_quark_from_static_string ("uwa-action"));
  info = unity_webapps_action_info_new (manager, full_path, action_item, parent_info);
  
  g_hash_table_insert (manager->priv->actions_by_path, g_strdup (full_path), info);
  
  g_signal_connect (action_item, "item-activated", G_CALLBACK(on_menuitem_activated), info);
  
  dbusmenu_menuitem_child_append (parent, action_item);
  
  if (out_info != NULL) 
    {
      *out_info = info;
    }
  
  return action_item;
}

static void
remove_menuitem_by_info (UnityWebappsActionManager *action_manager,
			 UnityWebappsActionInfo *info,
			 gint interest_id)
{
  if (interest_has_action (action_manager, interest_id, info->path) == FALSE)
    {
      return;
    }

  interest_remove_action (action_manager, interest_id, info);
  unity_webapps_action_info_unref (info);
}

static void
remove_menuitem_by_action_path (UnityWebappsActionManager *action_manager,
				const gchar *path,
				gint interest_id)
{
  UnityWebappsActionInfo *info;
  
  info = (UnityWebappsActionInfo *)g_hash_table_lookup (action_manager->priv->actions_by_path, path);
  
  if (info == NULL)
    return;
  
  remove_menuitem_by_info (action_manager, info, interest_id);
}

static void
on_interest_removed (UnityWebappsInterestManager *interest_manager,
		     UnityWebappsInterest *interest,
		     gpointer user_data)
{
  UnityWebappsActionManager *manager;
  GList *infos, *walk, *to_remove;
  
  manager = (UnityWebappsActionManager *)user_data;
  
  infos = g_hash_table_lookup (manager->priv->infos_by_interest_id,
			       GINT_TO_POINTER (interest->id));
  
  if (infos == NULL)
    return;
  
  to_remove = g_list_copy (infos);
  
  for (walk = to_remove; walk != NULL; walk = walk->next)
    {
      UnityWebappsActionInfo *info;
      
      info = (UnityWebappsActionInfo *)walk->data;

      UNITY_WEBAPPS_NOTE (ACTION, "Unreferencing action (%s) because it's owned with ID %d vanished",
			  info->path, interest->id);
      remove_menuitem_by_info (manager, info, interest->id);
    }
  
  g_list_free (to_remove);
  
  g_hash_table_remove (manager->priv->infos_by_interest_id,
		       GINT_TO_POINTER (interest->id));
}

static UnityWebappsActionInfo *
add_menuitem_by_action_path (UnityWebappsActionManager *action_manager,
			     const gchar *path)
{
  UnityWebappsActionInfo *info;
  DbusmenuMenuitem *parent_menuitem;
  GString *walking_path;
  gchar **components;
  gint i, length;
  gboolean floating;
  gboolean was_floating;
  
  info = (UnityWebappsActionInfo *) g_hash_table_lookup (action_manager->priv->actions_by_path,
							 path);
  
  if (info != NULL)
    {
      unity_webapps_action_info_ref (info);
      
      return info;
    }
  
  components = unity_webapps_action_manager_split_action_path (action_manager, path);
  
  if (components == NULL)
    {
      return NULL;
    }

  parent_menuitem = action_manager->priv->menu_item;
  
  length = g_strv_length (components);
  
  info = NULL;
  
  if (action_manager->priv->use_hierarchy == TRUE)
    {
      walking_path = g_string_new ("/");
    }
  else
    {
      walking_path = g_string_new ("");
    }
  
  floating = FALSE;
  was_floating = FALSE;

  for (i = 0; i < length; i++)
    {
      DbusmenuMenuitem *new_parent;
      gchar *component;
      
      component = components[i];
      
      walking_path = g_string_append (walking_path, component);
      
      new_parent = menuitem_find_child_by_label (parent_menuitem, component);
      
      if (new_parent == NULL)
	{
	  new_parent = add_menuitem_by_name (action_manager, parent_menuitem,
					     component, walking_path->str, NULL);
	  floating = TRUE;
	  if (new_parent == NULL)
	    {
	      goto out;
	    }
	  
	}
      
      info = g_object_get_qdata (G_OBJECT (parent_menuitem), g_quark_from_static_string ("uwa-action"));
      if (info != NULL && was_floating)
	{
	  unity_webapps_action_info_unref (info);
	}
	was_floating = floating;
	floating = FALSE;

	
      
      parent_menuitem = new_parent;

      //      info = g_object_get_qdata (G_OBJECT (parent_menuitem), g_quark_from_static_string ("uwa-action"));
      
      walking_path = g_string_append (walking_path, "/");
    }
  info = g_object_get_qdata (G_OBJECT (parent_menuitem), g_quark_from_static_string ("uwa-action"));
    
 out:
  g_string_free (walking_path, TRUE);
  g_strfreev (components);
  
  return info;
}

static void
unity_webapps_action_manager_dispose (GObject *object)
{
  UnityWebappsActionManager *manager;
  
  manager = UNITY_WEBAPPS_ACTION_MANAGER (object);
  
  if (manager->priv->menu_item)
    {
      g_object_unref (G_OBJECT (manager->priv->menu_item));
      manager->priv->menu_item = NULL;
    }
  if (manager->priv->menu_server)
    {
      g_object_unref (G_OBJECT (manager->priv->menu_server));
      manager->priv->menu_server = NULL;
    }
  if (manager->priv->interest_tracker)
    {
      g_object_unref (G_OBJECT (manager->priv->interest_tracker));
      manager->priv->interest_tracker = NULL;
    }
}

static void
unity_webapps_action_manager_finalize (GObject *object)
{
  UnityWebappsActionManager *manager;
  
  manager = UNITY_WEBAPPS_ACTION_MANAGER (object);
  
  g_hash_table_destroy (manager->priv->actions_by_path);
  g_hash_table_destroy (manager->priv->infos_by_interest_id);
  
  if (manager->priv->menu_path)
    {
      g_free (manager->priv->menu_path);
    }
}

static void
unity_webapps_action_manager_get_property (GObject *object, guint property_id,
					   GValue *value, GParamSpec *pspec)
{
  UnityWebappsActionManager *self;
  
  self = UNITY_WEBAPPS_ACTION_MANAGER (object);
  
  switch (property_id)
    {
    case PROP_MENU_OBJECT_PATH:
      g_value_set_string (value, self->priv->menu_path);
      break;
    case PROP_USE_HIERARCHY:
      g_value_set_boolean (value, self->priv->use_hierarchy);
      break;
    case PROP_TRACK_ACTIVITY:
      g_value_set_boolean (value, self->priv->track_activity);
      break;
    case PROP_INTEREST_TRACKER:
      g_value_set_object (value, G_OBJECT (self->priv->interest_tracker));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
unity_webapps_action_manager_create_menu_server (UnityWebappsActionManager *manager)
{
  if (manager->priv->menu_path == NULL)
    return;

  manager->priv->menu_server = dbusmenu_server_new (manager->priv->menu_path);

  dbusmenu_server_set_root (manager->priv->menu_server, manager->priv->menu_item);

}

static void
unity_webapps_action_manager_update_interest_action_visibility (UnityWebappsActionManager *manager,
								gint interest_id,
								gboolean visibility)
{
  GList *action_paths;

  action_paths = g_hash_table_lookup (manager->priv->infos_by_interest_id, 
				      GINT_TO_POINTER (interest_id));
  
  while (action_paths != NULL)
    {
      UnityWebappsActionInfo *action_info;
      
      action_info = (UnityWebappsActionInfo *)action_paths->data;
      
      dbusmenu_menuitem_property_set_bool (action_info->item, "visible", visibility);

      action_paths = action_paths->next;
    }
}

static void
unity_webapps_action_manager_interest_became_active (UnityWebappsInterestManager *interest_manager,
						     gint interest_id,
						     gpointer user_data)
{
  UnityWebappsActionManager *action_manager;
  
  action_manager = (UnityWebappsActionManager *)user_data;
  
  if (action_manager->priv->track_activity == FALSE)
    return;
  
  unity_webapps_action_manager_update_interest_action_visibility (action_manager, interest_id, TRUE);
}

static void
unity_webapps_action_manager_interest_lost_active (UnityWebappsInterestManager *manager,
						     gint interest_id,
						     gpointer user_data)
{
  UnityWebappsActionManager *action_manager;
  
  action_manager = (UnityWebappsActionManager *)user_data;
  
  if (action_manager->priv->track_activity == FALSE)
    return;
  
  unity_webapps_action_manager_update_interest_action_visibility (action_manager, interest_id, FALSE);
}


static void
unity_webapps_action_manager_initialize_interest_tracker (UnityWebappsActionManager *manager)
{
  manager->priv->interest_manager = unity_webapps_interest_tracker_get_interest_manager (manager->priv->interest_tracker);
  
  g_signal_connect (manager->priv->interest_manager,
		    "interest-removed",
		    G_CALLBACK (on_interest_removed),
		    manager);
  

  g_signal_connect_object (manager->priv->interest_tracker,
			   "interest-became-active",
			   G_CALLBACK (unity_webapps_action_manager_interest_became_active),
			   manager, 0);
  g_signal_connect_object (manager->priv->interest_tracker,
			   "interest-lost-active",
			   G_CALLBACK (unity_webapps_action_manager_interest_lost_active),
			   manager, 0);
}

static void
unity_webapps_action_manager_set_property (GObject *object, guint property_id,
					   const GValue *value, GParamSpec *pspec)
{
 UnityWebappsActionManager *self;
  
  self = UNITY_WEBAPPS_ACTION_MANAGER (object);
  
  switch (property_id)
    {
    case PROP_MENU_OBJECT_PATH:
      g_return_if_fail (self->priv->menu_path == NULL);
      self->priv->menu_path = g_value_dup_string (value);
      unity_webapps_action_manager_create_menu_server (self);
      break;
    case PROP_USE_HIERARCHY:
      self->priv->use_hierarchy = g_value_get_boolean (value);
      break;
    case PROP_TRACK_ACTIVITY:
      self->priv->track_activity = g_value_get_boolean (value);
      break;
    case PROP_INTEREST_TRACKER:
      self->priv->interest_tracker = g_value_dup_object (value);
      unity_webapps_action_manager_initialize_interest_tracker (self);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
unity_webapps_action_manager_class_init (UnityWebappsActionManagerClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  object_class->finalize = unity_webapps_action_manager_finalize;
  object_class->dispose = unity_webapps_action_manager_dispose;
  object_class->get_property = unity_webapps_action_manager_get_property;
  object_class->set_property = unity_webapps_action_manager_set_property;
  
  g_type_class_add_private (object_class, sizeof(UnityWebappsActionManagerPrivate));
  
  g_object_class_install_property (object_class, PROP_MENU_OBJECT_PATH,
				   g_param_spec_string ("menu-path", "Menu Object Path",
							"The Menu Object Path to export the menu at",
							NULL,
							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (object_class, PROP_USE_HIERARCHY,
				   g_param_spec_boolean ("use-hierarchy", "Use Hierarchy",
							"Whether to use a hierarchy of actions or flat structure",
							TRUE,
							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
  
  g_object_class_install_property (object_class, PROP_TRACK_ACTIVITY,
				   g_param_spec_boolean ("track-activity", "Track Activity",
							 "Whether to track activity and hide items from inactive interests",
							 TRUE,
							 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (object_class, PROP_INTEREST_TRACKER,
				   g_param_spec_object ("interest-tracker", "Interest Tracker",
							"The interest tracker to use when tracking activity",
							UNITY_WEBAPPS_TYPE_INTEREST_TRACKER,
							G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
  
  signals[ACTION_INVOKED] = g_signal_new ("action-invoked",
					  UNITY_WEBAPPS_TYPE_ACTION_MANAGER,
					  G_SIGNAL_RUN_LAST,
					  0,
					  NULL,
					  NULL,
					  NULL,
					  G_TYPE_NONE,
					  1,
					  G_TYPE_STRING);
}

static void
unity_webapps_action_manager_init (UnityWebappsActionManager *manager)
{
  manager->priv = UNITY_WEBAPPS_ACTION_MANAGER_GET_PRIVATE (manager);
  
  manager->priv->actions_by_path = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)unity_webapps_action_info_free);
  manager->priv->infos_by_interest_id = g_hash_table_new_full (g_direct_hash, g_direct_equal,
							       NULL, NULL);							       
  
  manager->priv->menu_item = dbusmenu_menuitem_new ();

  manager->priv->menu_path = NULL;
  manager->priv->menu_server = NULL;
  manager->priv->track_activity = TRUE;
  
  manager->priv->interest_tracker = NULL;

}

UnityWebappsActionManager *
unity_webapps_action_manager_new (UnityWebappsInterestTracker *tracker, 
				  const gchar *menu_path)
{
  return g_object_new (UNITY_WEBAPPS_TYPE_ACTION_MANAGER, 
		       "menu-path", menu_path,
		       "interest-tracker", tracker,
		       NULL);
}

UnityWebappsActionManager *
unity_webapps_action_manager_new_flat (UnityWebappsInterestTracker *tracker,
				       const gchar *menu_path)
{
  return g_object_new (UNITY_WEBAPPS_TYPE_ACTION_MANAGER,
		       "menu-path", menu_path,
		       "use-hierarchy", FALSE,
		       "interest-tracker", tracker,
		       NULL);
}

/*
unity_webapps_action_manager_get_default ()
{
  if (default_manager == NULL)
    {
      default_manager = g_object_new (UNITY_WEBAPPS_TYPE_ACTION_MANAGER, 
				      "menu-path", "/com/canonical/Unity/Webapps/Context/ApplicationActions",
				      NULL);
    }
  return default_manager;
}*/

void
unity_webapps_action_manager_add_action (UnityWebappsActionManager *manager,
					 const gchar *path,
					 gint interest_id)
{
  UnityWebappsActionInfo *info;
  gchar *normalized_path;
  
  normalized_path = unity_webapps_action_manager_normalize_path (manager, path);
  
  if (normalized_path == NULL)
    {
      return;
    }
  
  if (interest_has_action (manager, interest_id, normalized_path) == TRUE)
    {
      UNITY_WEBAPPS_NOTE (ACTION, "Ignoring request to add action as interest with ID %d has already referenced the action (%s)",
			  interest_id, normalized_path);
      return;
    }
  
  info = add_menuitem_by_action_path (manager, normalized_path);
  
  if (info == NULL)
    {
      UNITY_WEBAPPS_NOTE (ACTION, "Failed to add action: %s", normalized_path);
      
      return;
    }
  
  interest_add_action (manager, interest_id, info);
  
  g_free (normalized_path);
}

void
unity_webapps_action_manager_remove_action (UnityWebappsActionManager *manager,
					    const gchar *path,
					    gint interest_id)
{
  gchar *normalized_path;

  normalized_path = unity_webapps_action_manager_normalize_path (manager, path);
  
  if (normalized_path == NULL)
    {
      return;
    }

  remove_menuitem_by_action_path (manager, normalized_path, interest_id);
  
  g_free (normalized_path);
}

void
unity_webapps_action_manager_remove_all_actions (UnityWebappsActionManager *manager,
						 gint interest_id)
{
  GList *action_infos, *walk;
  
  action_infos = g_hash_table_lookup (manager->priv->infos_by_interest_id, GINT_TO_POINTER (interest_id));
  
  if (action_infos == NULL)
    return;
  
  action_infos = g_list_copy (action_infos);

  for (walk = action_infos; walk != NULL; walk = walk->next)
    {
      UnityWebappsActionInfo *info;
      
      info = (UnityWebappsActionInfo *)walk->data;
      
      unity_webapps_action_manager_remove_action (manager, info->path, interest_id);
    }
  
  g_list_free (action_infos);
}

DbusmenuMenuitem *
unity_webapps_action_manager_get_root (UnityWebappsActionManager *manager)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_ACTION_MANAGER (manager), NULL);
  
  return manager->priv->menu_item;
}

DbusmenuServer *
unity_webapps_action_manager_get_server (UnityWebappsActionManager *manager)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_ACTION_MANAGER (manager), NULL);
  
  return manager->priv->menu_server;
}


void
unity_webapps_action_manager_set_track_activity (UnityWebappsActionManager *manager,
						 gboolean track_activity)
{
  g_return_if_fail (UNITY_WEBAPPS_IS_ACTION_MANAGER (manager));
  
  manager->priv->track_activity = track_activity;
  g_object_notify (G_OBJECT (manager), "track-activity");
}

static gint
compare_action_infos (gconstpointer a, gconstpointer b)
{
  gchar *info_a, *info_b;
  
  info_a = (gchar *)a;
  info_b = (gchar *)b;
  
  return g_strcmp0 (info_a, info_b);
}

GVariant *
unity_webapps_action_manager_serialize (UnityWebappsActionManager *manager)
{
  GList *paths, *walk;
  GVariantBuilder b;

  g_return_val_if_fail (UNITY_WEBAPPS_IS_ACTION_MANAGER (manager), NULL);
  
  paths = g_hash_table_get_keys (manager->priv->actions_by_path);
  paths = g_list_sort (paths, compare_action_infos);
  
  g_variant_builder_init(&b, G_VARIANT_TYPE ("(a(sib))"));
  g_variant_builder_open (&b, G_VARIANT_TYPE ("a(sib)"));
  
  for (walk = paths; walk != NULL; walk = walk->next)
    {
      UnityWebappsActionInfo *info;
      GVariant *info_variant;
      
      
      info = (UnityWebappsActionInfo *)g_hash_table_lookup (manager->priv->actions_by_path,
							    (gchar *)walk->data);
      info_variant = g_variant_new ("(sib)", info->path, 
				    info->ref_count, 
				    dbusmenu_menuitem_property_get_bool (info->item, "visible") ,NULL);
      
      g_variant_builder_add_value (&b, info_variant);
    }
  
  g_list_free (paths);
  
  g_variant_builder_close (&b);
  return g_variant_builder_end (&b);  
}

gint
unity_webapps_action_manager_get_most_recent_interest_with_action (UnityWebappsActionManager *manager,
                                                                   const gchar* path)
{
  GList *interest;
  GList *most_recent_interests;
  UnityWebappsHasActionData *data;
  UnityWebappsInterestTracker *tracker;
  
  data = g_malloc (sizeof (*data));
  data->manager = manager;
  data->path = path;

  tracker = manager->priv->interest_tracker;
  most_recent_interests = unity_webapps_interest_tracker_get_most_recent_interests (tracker);
  interest = g_list_find_custom (most_recent_interests, 
                                 data, 
                                 (GCompareFunc) interest_has_action_comparefunc);

  g_free (data);

  if (interest == NULL)
    return -1;
  
  return GPOINTER_TO_INT (interest->data);
}
