/*
 * Copyright (C) 2008 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as 
 * published by the Free Software Foundation.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authored by Neil Jagdish Patel <neil.patel@canonical.com>
 *
 */


#include <string.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <gio/gio.h>
#include <gdk/gdkx.h>

#include <clutter-gtk/gtk-clutter-util.h>

#include "launcher-config.h"
#include "launcher-defines.h"
#include "launcher-util.h"

#include <libgnome/gnome-desktop-item.h>

/* Convert command line to argv array, stripping % conversions on the way */
#define MAX_ARGS 255

/*
 * Convert a desktop file exec string to an array of strings suitable for
 * passing to gdk_spawn_on_screen. Code from libtaku (matchbox-desktop-2).
 */
gchar**
launcher_util_exec_to_argv (const gchar *exec)
{
  const char *p;
  char *buf, *bufp, **argv;
  int nargs;
  gboolean escape, single_quote, double_quote;
  
  argv = g_new (char *, MAX_ARGS + 1);
  buf = g_alloca (strlen (exec) + 1);
  bufp = buf;
  nargs = 0;
  escape = single_quote = double_quote = FALSE;
  
  for (p = exec; *p; p++) {
    if (escape) {
      *bufp++ = *p;
      
      escape = FALSE;
    } else {
      switch (*p) {
      case '\\':
        escape = TRUE;

        break;
      case '%':
        /* Strip '%' conversions */
        if (p[1] && p[1] == '%')
          *bufp++ = *p;
        else if (p[1] && p[1] == 'c')
        {
          *bufp++ = *p;
        }
        
        p++;

        break;
      case '\'':
        if (double_quote)
          *bufp++ = *p;
        else
          single_quote = !single_quote;
        
        break;
      case '\"':
        if (single_quote)
          *bufp++ = *p;
        else
          double_quote = !double_quote;
        
        break;
      case ' ':
        if (single_quote || double_quote)
          *bufp++ = *p;
        else {
          *bufp = 0;
          
          if (nargs < MAX_ARGS)
            argv[nargs++] = g_strdup (buf);
          
          bufp = buf;
        }
        
        break;
      default:
        *bufp++ = *p;
        break;
      }
    }
  }
  
  if (bufp != buf) {
    *bufp = 0;
    
    if (nargs < MAX_ARGS)
      argv[nargs++] = g_strdup (buf);
  }
  
  argv[nargs] = NULL;
  
  return argv;
}


ClutterActor * 
launcher_util_texture_new_from_file (const gchar *filename)
{
  ClutterActor *texture;
  GdkPixbuf *pixbuf;
  GError *error = NULL;

  texture = clutter_texture_new ();

  pixbuf = gdk_pixbuf_new_from_file (filename, &error);
  if (error)
  {
    g_warning ("Unable to load %s: %s", filename, error->message);
    g_error_free (error);
  }
  else
    launcher_util_texture_set_from_pixbuf (CLUTTER_TEXTURE (texture), pixbuf);
  
  if (G_IS_OBJECT (pixbuf)) 
    g_object_unref (pixbuf);

  return texture;
}

/* From matchbox-desktop */
static char *
strip_extension (const char *file)
{
        char *stripped, *p;

        stripped = g_strdup (file);

        p = strrchr (stripped, '.');
        if (p &&
            (!strcmp (p, ".png") ||
             !strcmp (p, ".svg") ||
             !strcmp (p, ".xpm")))
	        *p = 0;

        return stripped;
}

/* Gets the pixbuf from a desktop file's icon name. Based on the same function
 * from matchbox-desktop
 */
GdkPixbuf *
get_icon (const gchar *name, guint size)
{
  static GtkIconTheme *theme = NULL;
  GdkPixbuf *pixbuf = NULL;
  GError *error = NULL;
  gchar *stripped = NULL;

  gint width, height;

  if (theme == NULL)
    theme = gtk_icon_theme_get_default ();

  if (name == NULL)
  {
    pixbuf = gtk_icon_theme_load_icon (theme, "application-x-executable",
                                       size, 0, NULL);
    return pixbuf;
  }

  if (g_path_is_absolute (name))
  {
    if (g_file_test (name, G_FILE_TEST_EXISTS))
    {
      pixbuf = gdk_pixbuf_new_from_file_at_scale (name, size, size, 
                                                  TRUE, &error);
      if (error)
      {
        /*g_warning ("Error loading icon: %s\n", error->message);*/
        g_error_free (error);
        error = NULL;
     }
      return pixbuf;
    } 
  }

  stripped = strip_extension (name);
  
  pixbuf = gtk_icon_theme_load_icon (theme,
                                     stripped,
                                     size,
                                     GTK_ICON_LOOKUP_FORCE_SVG, &error);
  if (error)
  {   
    /*g_warning ("Error loading icon: %s\n", error->message);*/
    g_error_free (error);
    error = NULL;
  }
  
  /* Always try and send back something */
  if (pixbuf == NULL)
    pixbuf = gtk_icon_theme_load_icon (theme, "stock_folder",
                                       size, 0, NULL);
  
  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);

  if (width != size || height != size)
  {
    GdkPixbuf *temp = pixbuf;
    pixbuf = gdk_pixbuf_scale_simple (temp, 
                                      size,
                                      size,
                                      GDK_INTERP_HYPER);
    g_object_unref (temp);
  }

  g_free (stripped);

 return pixbuf;
}



ClutterActor * 
launcher_util_texture_new_from_named_icon (const gchar *name)
{
  GdkPixbuf *pixbuf;
  ClutterActor *texture;
  LauncherConfig *cfg = launcher_config_get_default ();

  pixbuf = get_icon (name, cfg->shortcut_height *2);
  
  texture = launcher_util_texture_new_from_pixbuf (pixbuf);

  g_object_unref (pixbuf);

  return texture;
}

static void
_normalize (char *str)
{
	size_t len;
	int i;

	len = strlen(str);

	for (i = 0; i < len; i++) 
  {
		switch (str[i]) 
    {
			case ' ':
				str[i] = '-';
        break;
      case '/':
        str[i] = '-';

      default:
				break;
		}
	}

}

void           
launcher_util_create_favorite (LauncherMenuApplication *app)
{
  GError *error = NULL;
  GnomeDesktopItem *item = NULL;
  GnomeDesktopItem *fav = NULL;
  const gchar *item_file;
  gchar *appdir, *filename, *favfile;

  g_return_if_fail (app);

  item_file = launcher_menu_application_get_desktop_filename (app);
  item = gnome_desktop_item_new_from_file (item_file,
                                         GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS,
                                           NULL);
  if (item == NULL)
  {
    
    return;
  }

  fav = gnome_desktop_item_copy (item);
  gnome_desktop_item_set_string (fav, 
                                 GNOME_DESKTOP_ITEM_CATEGORIES,
                                 "Favorites");

  appdir = g_strdup_printf ("file://%s/.local/share/applications", 
                            g_get_home_dir ());
  filename = g_strdup_printf ("%s-favorite-%ld.desktop",
                              launcher_menu_application_get_name (app),
                              time (NULL));
  _normalize (filename);

  favfile = g_build_filename (appdir, filename, NULL);
                               
  gnome_desktop_item_save (fav, favfile, TRUE, &error);
  
  if (error)
  {
    g_warning ("Unable to create favorite: %s", error->message);
    g_error_free (error);
  }

  g_free (appdir);
  g_free (filename);
  g_free (favfile);

  gnome_desktop_item_unref (item);
  gnome_desktop_item_unref (fav);
}

void
launcher_util_remove_favorite (LauncherMenuApplication *app)
{
  GnomeDesktopItem *item;
  const gchar *path;
  gchar *filename;
  gchar *basename;
  GError *error = NULL;

  g_return_if_fail (app);

  path = launcher_menu_application_get_desktop_filename (app);

  if ((g_remove (path) == 0))
  {
    return;
  }
  
  item = gnome_desktop_item_new_from_file (path,
                                        GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS, 
                                        NULL);
  if (!item)
  {
    g_debug ("Unable to remove : %s, cannot open desktop file", path);
    return;
  }

  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_NO_DISPLAY, 
                                 "true");
  basename = g_path_get_basename (path);
  filename = g_build_filename ("file://",
                               g_get_home_dir (), 
                               ".local/share/applications",
                               basename,
                               NULL);
  gnome_desktop_item_save (item, filename, TRUE, &error);

  if (error)
  {
    g_warning ("Unable to remove favorite: %s", error->message);
    g_error_free (error);
  }
  g_free (basename);
  g_free (filename);

  gnome_desktop_item_unref (item);
}

void    
launcher_util_add_favorite    (const gchar *name,
                               const gchar *category,
                               const gchar *exec,
                               const gchar *icon)
{
  GError *error = NULL;
  GnomeDesktopItem *item = NULL;
  gchar *uri, *leaf;

  leaf = g_strdup_printf ("%s-fav-%ld.desktop", name, time (NULL));
  _normalize (leaf);
  uri = g_strdup_printf ("file://%s/.local/share/applications/%s",
                         g_get_home_dir (),
                         leaf);

  item = gnome_desktop_item_new ();

  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_NAME,
                                 name); 
  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_CATEGORIES,
                                 category);
  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_EXEC,
                                 exec);
  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_ICON,
                                 icon);
  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_TYPE,
                                 "Application");
  gnome_desktop_item_set_string (item, 
                                 GNOME_DESKTOP_ITEM_VERSION,
                                 "1.0");
                        
  g_debug ("Creating new favourite: %s", uri);

  gnome_desktop_item_save (item, uri, TRUE, &error);
  if (error)
  {
    g_warning ("Unable to create favorite: %s", error->message);
    g_error_free (error);
  }

  gnome_desktop_item_unref (item);

  g_free (leaf);
  g_free (uri);
}

/*
 * Taken from libtomboy, (C) 2008 Novell, LGPL v2 or later
 */

static void
tomboy_window_override_user_time (GtkWindow *window)
{
	guint32 ev_time = gtk_get_current_event_time();

	if (ev_time == 0) {
		/* 
		 * FIXME: Global keypresses use an event filter on the root
		 * window, which processes events before GDK sees them.
		 */
		//ev_time = tomboy_keybinder_get_current_event_time ();
    ev_time = CLUTTER_CURRENT_TIME;
	}
	if (ev_time == 0) {
		gint ev_mask = gtk_widget_get_events (GTK_WIDGET(window));
		if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK)) {
			gtk_widget_add_events (GTK_WIDGET (window),
					       GDK_PROPERTY_CHANGE_MASK);
		}

		/* 
		 * NOTE: Last resort for D-BUS or other non-interactive
		 *       openings.  Causes roundtrip to server.  Lame. 
		 */
		ev_time = gdk_x11_get_server_time (GTK_WIDGET(window)->window);
	}

	gdk_x11_window_set_user_time (GTK_WIDGET(window)->window, ev_time);
}



void 
launcher_util_present_window (GtkWindow *window)
{
	if (!GTK_WIDGET_REALIZED (window))
		gtk_widget_realize (GTK_WIDGET (window));

	tomboy_window_override_user_time (window);

	gtk_window_present (window);
}

guint32
launcher_util_present_time (gulong xid)
{
  GdkWindow *window;
  guint32 ev_time = gtk_get_current_event_time();

  window = gdk_window_foreign_new (xid);
  
	if (ev_time == 0) {
		/* 
		 * FIXME: Global keypresses use an event filter on the root
		 * window, which processes events before GDK sees them.
		 */
		//ev_time = tomboy_keybinder_get_current_event_time ();
    ev_time = CLUTTER_CURRENT_TIME;
	}
	if (ev_time == 0) {
		gint ev_mask = gdk_window_get_events (window);
		if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK)) {
			gdk_window_set_events (window, 
                             ev_mask | GDK_PROPERTY_CHANGE_MASK);
		}

		/* 
		 * NOTE: Last resort for D-BUS or other non-interactive
		 *       openings.  Causes roundtrip to server.  Lame. 
		 */
		ev_time = gdk_x11_get_server_time (window);
	}
  return ev_time;
}

/*
 * Fixes bug in gtk_clutter_texture_set_from_pixbuf
 * Remove this when clutter-gtk-0.8.2 releases 
 */
void     
launcher_util_texture_set_from_pixbuf (ClutterTexture *texture,
                                       GdkPixbuf      *pixbuf)
{
  GError *error;

  g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
  g_return_if_fail (GDK_IS_PIXBUF (pixbuf));

  error = NULL;
  clutter_texture_set_from_rgb_data (texture,
                                     gdk_pixbuf_get_pixels (pixbuf),
                                     gdk_pixbuf_get_has_alpha (pixbuf),
                                     gdk_pixbuf_get_width (pixbuf),
                                     gdk_pixbuf_get_height (pixbuf),
                                     gdk_pixbuf_get_rowstride (pixbuf),
                                     gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3,
                                     0,
                                     &error);
  if (error)
  {
    g_warning ("Unable to set the pixbuf: %s", error->message);
    g_error_free (error);
  }
}

/*
 * Fixes bug in gtk_clutter_new_from_pixbuf 
 * Remove this when clutter-gtk-0.8.2 releaes
 */
ClutterActor *
launcher_util_texture_new_from_pixbuf (GdkPixbuf *pixbuf)
{
  ClutterActor *retval;
  GError *error;

  g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);

  retval = clutter_texture_new ();

  error = NULL;
  clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (retval),
                                     gdk_pixbuf_get_pixels (pixbuf),
                                     gdk_pixbuf_get_has_alpha (pixbuf),
                                     gdk_pixbuf_get_width (pixbuf),
                                     gdk_pixbuf_get_height (pixbuf),
                                     gdk_pixbuf_get_rowstride (pixbuf),
                                     gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3,
                                     0,
                                     &error);
  if (error)
    {
      g_warning ("Unable to set the pixbuf: %s", error->message);
      g_error_free (error);
    }

  return retval; 

}


/*
 * USER DIRS LOADING STOLEN FROM GLIB
 */

/* adapted from xdg-user-dir-lookup.c
 *
 * Copyright (C) 2007 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions: 
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software. 
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
static gchar *
load_user_desktop_dir (void)
{
  gchar *config_file;
  gchar *data;
  gchar **lines;
  gint n_lines, i;
  gchar *folder = NULL;
  
  config_file = g_build_filename (g_get_user_config_dir (),
                                  "user-dirs.dirs",
                                  NULL);
  
  if (!g_file_get_contents (config_file, &data, NULL, NULL))
    {
      g_free (config_file);
      return NULL;
    }

  lines = g_strsplit (data, "\n", -1);
  n_lines = g_strv_length (lines);
  g_free (data);
  
  for (i = 0; i < n_lines; i++)
    {
      gchar *buffer = lines[i];
      gchar *d, *p;
      gint len;
      gboolean is_relative = FALSE;
      GUserDirectory directory;

      /* Remove newline at end */
      len = strlen (buffer);
      if (len > 0 && buffer[len - 1] == '\n')
      	buffer[len - 1] = 0;
      
      p = buffer;
      while (*p == ' ' || *p == '\t')
      	p++;
      
      if (strncmp (p, "XDG_DESKTOP_DIR", strlen ("XDG_DESKTOP_DIR")) == 0)
        {
          directory = G_USER_DIRECTORY_DESKTOP;
          p += strlen ("XDG_DESKTOP_DIR");
        }
      else
	      continue;

      while (*p == ' ' || *p == '\t')
      	p++;

      if (*p != '=')
      	continue;
      p++;

      while (*p == ' ' || *p == '\t')
      	p++;

      if (*p != '"')
      	continue;
      p++;

      if (strncmp (p, "$HOME", 5) == 0)
    	  {
	        p += 5;
	        is_relative = TRUE;
	      }
      else if (*p != '/')
      	continue;

      d = strrchr (p, '"');
      if (!d)
        continue;
      *d = 0;

      d = p;
      
      /* remove trailing slashes */
      len = strlen (d);
      if (d[len - 1] == '/')
        d[len - 1] = 0;
      
      if (is_relative)
        folder= g_build_filename (g_get_home_dir(), d, NULL);
      else
        folder = g_strdup (d);

      break;
    }

  g_strfreev (lines);
  g_free (config_file);

  return folder;
}

gchar *
launcher_util_get_desktop_dir (void)
{
  gchar *desktop;

  desktop = load_user_desktop_dir ();

  if (!desktop)
    desktop = g_build_filename (g_get_home_dir (), "Desktop", NULL);

  return desktop;
}
