/* -*- Mode: C; indent-tabs-mode: nil; tab-width: 2 -*-
 *
 * Copyright (C) 2009 Canonical Ltd.
 * Authors:
 *  Gustavo Sverzut Barbieri <gustavo.barbieri@canonical.com>
 *
 * This file is part of Netbook Launcher EFL.
 *
 * Netbook Launcher EFL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * Netbook Launcher EFL 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 Netbook Launcher EFL.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @note This file is meant to be used entirely from GLib thread!
 */

#include "netbook-launcher.h"
#include <gtk/gtk.h>
#include <string.h>

static GHashTable *_cache = NULL;
static GtkIconTheme *_theme = NULL;

struct icon_hash_key
{
  unsigned int size;
  unsigned int name_len;
  char name[];
};

static guint
_icon_cache_hash(gconstpointer key)
{
  const struct icon_hash_key *k = key;
  return g_str_hash(k->name);
}

static gboolean
_icon_cache_equal(gconstpointer key_a, gconstpointer key_b)
{
  const struct icon_hash_key *a = key_a, *b = key_b;
  return ((a->size == b->size) &&
          (a->name_len == b->name_len) &&
          (g_str_equal(a->name, b->name)));
}

static struct icon_hash_key *
_icon_cache_key_new(const char *name, unsigned int size)
{
  unsigned int name_len = strlen(name);
  struct icon_hash_key *key = g_try_malloc(sizeof(*key) + name_len + 1);
  if (!key)
    return NULL;
  key->size = size;
  key->name_len = name_len;
  memcpy(key->name, name, name_len + 1);
  return key;
}

static void
_icon_cache_key_destroy(gpointer key)
{
  struct icon_hash_key *k = key;
  g_free(k);
}

static void
_icon_cache_value_destroy(gpointer value)
{
  char *path = value;
  g_free(path);
}

static gchar *
_icon_path_from_theme_get(const char *name, unsigned int size)
{
  GtkIconInfo *info;
  const gchar *filename;
  gchar *ret;

  info = gtk_icon_theme_lookup_icon(_theme, name, size, 0);
  if (!info)
    return NULL;

  filename = gtk_icon_info_get_filename(info);
  if (g_file_test(filename,  G_FILE_TEST_EXISTS))
    ret = g_strdup(filename);
  else
    ret = NULL;

  gtk_icon_info_free(info);

  return ret;
}

/**
 * Gets the icon path from a desktop file's icon name. Based on the
 * function from matchbox-desktop
 *
 * @return: g_strdup() of icon path, or NULL. use g_free() on it.
 */
static gchar *
_icon_path_get(const char *name, unsigned int size)
{
  gchar *stripped, *p;

  /* Check to see if it's a URI being passed in */
  if (g_str_has_prefix(name, "file://"))
    {
      gchar *filename;

      filename = g_filename_from_uri(name, NULL, NULL);
      if (filename && g_file_test(filename, G_FILE_TEST_EXISTS))
        return filename;
    }

  /* See if it's an filename */
  if (g_path_is_absolute(name))
    {
      if (g_file_test(name, G_FILE_TEST_EXISTS))
        return g_strdup(name);
    }

  p = _icon_path_from_theme_get(name, size);
  if (p)
    return p;

  p = strrchr(name, '.');
  if (!p)
      return NULL;

  stripped = alloca(p - name + 1);
  memcpy(stripped, name, p - name);
  stripped[p - name] = '\0';
  p = _icon_path_from_theme_get(stripped, size);
  if (p)
    return p;

  return NULL;
}

Eina_Bool
icon_path_init(void)
{
  if (_cache)
    return 1;

  _cache = g_hash_table_new_full
    (_icon_cache_hash, _icon_cache_equal,
     _icon_cache_key_destroy, _icon_cache_value_destroy);
  if (!_cache)
  {
    g_critical("could not create cache!");
    return 0;
  }

  _theme = gtk_icon_theme_get_default();
  if (!_theme)
  {
    g_critical("could not get gtk icon theme!");
    g_hash_table_destroy(_cache);
    _cache = NULL;
    return 0;
  }

  return 1;
}

void
icon_path_shutdown(void)
{
  if (!_cache)
    return;
  g_hash_table_destroy(_cache);
  _cache = NULL;
  _theme = NULL;
}

static const char *
_icon_path_get_internal(const char *name, unsigned int size)
{
  struct icon_hash_key *key;
  char *path;

  key = _icon_cache_key_new(name, size);
  if (!key)
  {
    g_warning("could not create icon cache lookup key.");
    return NULL;
  }

  path = g_hash_table_lookup(_cache, key);
  if (path)
  {
    _icon_cache_key_destroy(key);
    return path;
  }

  path = _icon_path_get(name, size);
  if (!path)
  {
    _icon_cache_key_destroy(key);
    return NULL;
  }

  g_hash_table_insert(_cache, key, path);
  return path;
}

const char *
icon_path_get(const char *name, unsigned int size)
{
  const char *icon_path;

  if (!_cache)
  {
    g_warning("no icon cache.");
    return NULL;
  }

  if (!name)
  {
    g_warning("icon name == NULL");
    return NULL;
  }

  icon_path = _icon_path_get_internal(name, size);
  if (!icon_path)
    g_warning("no icon path for '%s'", name);

  return icon_path;
}

const char *
icon_gicon_path_get(GIcon *icon, unsigned int size)
{
  const char *icon_path = NULL;
  const gchar * const *names;

  if (!_cache)
  {
    g_warning("no icon cache.");
    return NULL;
  }

  if (!G_IS_ICON(icon))
  {
    g_warning("G_IS_ICON(icon) failed, icon=%p", icon);
    return NULL;
  }

  if (!G_IS_THEMED_ICON(icon))
  {
    gchar *str = g_icon_to_string(icon);
    g_warning("cannot get icon path for non-themed gicon: %s", str);
    g_free(str);
    return NULL;
  }

  names = g_themed_icon_get_names(G_THEMED_ICON(icon));
  if (!names)
  {
    gchar *str = g_icon_to_string(icon);
    g_warning("cannot get icon name gicon: %s", str);
    g_free(str);
    return NULL;
  }

  for (; (icon_path == NULL) && (*names != NULL); names++)
    icon_path = _icon_path_get_internal(*names, size);

  if (!icon_path)
  {
    gchar *str = g_icon_to_string(icon);
    g_warning("cannot get any icon path gicon: %s", str);
    g_free(str);
    return NULL;
  }

  return icon_path;
}

const char *
icon_mime_type_path_get(const char *mime_type, unsigned int size)
{
  const char *icon_path;
  gchar *content_type;
  GIcon *icon;

  if (!_cache)
  {
    g_warning("no icon cache.");
    return NULL;
  }

  if (!mime_type)
  {
    g_warning("mime_type == NULL");
    return NULL;
  }

  content_type = g_content_type_from_mime_type(mime_type);
  if (!content_type)
  {
    g_warning("no content type for mime-type: '%s'", mime_type);
    return NULL;
  }

  icon = g_content_type_get_icon(content_type);
  if (!icon)
  {
    g_warning("no gicon for content type '%s', mime-type '%s'",
              content_type, mime_type);
    g_free(content_type);
    return NULL;
  }
  g_free(content_type);

  icon_path = icon_gicon_path_get(icon, size);
  g_object_unref(icon);

  return icon_path;
}

const char *
icon_unr_path_get(const char *icon_name, unsigned int icon_size)
{
  unsigned int len;
  char buf[PATH_MAX] = "unr-";
  const char *icon_path;

  if (!icon_name)
  {
    g_warning("icon_name == NULL");
    return NULL;
  }

  len = strlen(icon_name);

  if (len < PATH_MAX - sizeof("unr-"))
  {
    memcpy(buf + sizeof("unr-") - 1, icon_name, len + 1);
    icon_path = icon_path_get(buf, icon_size);
    if (icon_path)
      return icon_path;
    else
    {
      icon_path = icon_path_get(icon_name, icon_size);
      if (icon_path)
        return icon_path;
    }
  }

  return icon_name;
}
