/*
 * unity-webapps-icon-theme.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 <errno.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib/gstdio.h>
#include <gio/gio.h>

#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>

#include "unity-webapps-dirs.h"
#include "unity-webapps-gio-utils.h"
#include "unity-webapps-resource-cache.h" 
#include "../unity-webapps-debug.h"

const gchar *const UNITY_WEBAPPS_ICON_THEME_INDEX_HEADER = 
  "[Icon Theme]\n"
  "Name=Unity WebApps\n"
  "Comment=Webapps Icons\n"
  "Directories=apps/16, apps/22, apps/24, apps/32, apps/48, apps/64, apps/128, apps/192, \n"
  "[apps/16]\n"
  "Size=16\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/22]\n"
  "Size=22\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/24]\n"
  "Size=24\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/32]\n"
  "Size=32\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/48]\n"
  "Size=48\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/64]\n"
  "Size=64\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/128]\n"
  "Size=128\n"
  "Context=Applications\n"
  "Type=Fixed\n"
  "[apps/192]\n"
  "Size=192\n"
  "Context=Applications\n"
  "Type=Fixed\n";
  
  

#define include "unity-webapps-icon-theme.h"

static gboolean
theme_dir_has_index (const gchar *theme_dir)
{
  gchar *index_file;
  gboolean ret;
  
  index_file = g_build_filename (theme_dir, "index.theme", NULL);
  
  ret = g_file_test (index_file, G_FILE_TEST_EXISTS);
  
  g_free (index_file);
  
  return ret;
}

static gboolean
write_theme_index (const gchar *theme_dir)
{
  gchar *index_file;
  gboolean ret;
  GError *error;
  
  index_file = g_build_filename (theme_dir, "index.theme", NULL);
  
  error = NULL;
  
  ret = g_file_set_contents (index_file, UNITY_WEBAPPS_ICON_THEME_INDEX_HEADER, -1, &error);
  
  if (error != NULL)
    {
      UNITY_WEBAPPS_NOTE (CONTEXT, "Error writing Unity Webapps Icon Theme Index: %s", error->message);
      g_error_free (error);
    }
  
  g_free (index_file);
  
  return ret;
}

static gboolean
ensure_theme_directory (const gchar *theme_dir, const gchar *size)
{
  gchar *icon_dir;
  gint status;
  gboolean ret;
  
  if (size != NULL)
    {
      icon_dir = g_build_filename (theme_dir, "apps", size, NULL);
    }
  else
    {
      icon_dir = g_build_filename (theme_dir, "apps", NULL);
    }
  
  ret = TRUE;
  
  if (g_file_test (icon_dir, G_FILE_TEST_EXISTS) == TRUE)
    {
      goto out;
    }
  
  status = g_mkdir (icon_dir, 0700);
  
  if (status < -1)
    {
      g_critical ("Error making Unity webapps icon theme dir: %s", g_strerror (errno));
      
      ret = FALSE;
      
      goto out;
    }
  
 out:
  g_free (icon_dir);
  return ret;
  
}

static gboolean
ensure_theme_directories (const gchar *theme_dir)
{
  gboolean ret;

  ret = ensure_theme_directory (theme_dir, NULL);
  
  ret = ret && ensure_theme_directory (theme_dir, "16");
  ret = ret && ensure_theme_directory (theme_dir, "22");
  ret = ret && ensure_theme_directory (theme_dir, "24");
  ret = ret && ensure_theme_directory (theme_dir, "32");
  ret = ret && ensure_theme_directory (theme_dir, "48");
  ret = ret && ensure_theme_directory (theme_dir, "64");
  ret = ret && ensure_theme_directory (theme_dir, "128");
  ret = ret && ensure_theme_directory (theme_dir, "192");
  
  return ret;
}

static gboolean
create_empty_theme (const gchar *theme_dir)
{
  gboolean ret;
  
  ret = write_theme_index (theme_dir);

  ret = ret && ensure_theme_directories (theme_dir);

  return ret;
}

static gboolean
ensure_theme_directories_exist ()
{
  const gchar *theme_dir;
  gboolean ret;
  
  theme_dir = unity_webapps_dirs_get_icon_theme_dir ();
  
  ret = TRUE;

  if (theme_dir_has_index (theme_dir) == FALSE)
    {
      ret = create_empty_theme (theme_dir);
    }
  
  return ret;
}

gchar *
unity_webapps_icon_theme_get_application_icon_path (const gchar *icon_name)
{
  const gchar *icon_dir;
  gchar *local_icon_path;
  
  icon_dir = unity_webapps_dirs_get_icon_dir ();
  
  local_icon_path = g_build_filename (icon_dir, icon_name, NULL);
  
  
  return local_icon_path;
}

static gchar *
get_themed_icon_path (const gchar *full_icon_name, const gchar *size)
{
  gchar *path;
  const gchar *theme_dir;

  if (ensure_theme_directories_exist() == FALSE)
    {
      return NULL;
    }
  
  theme_dir = unity_webapps_dirs_get_icon_theme_dir ();
  path = g_build_filename (theme_dir, "apps", size, full_icon_name, NULL);
  
  return path;
}

static void
write_themed_icon(const gchar *full_icon_name,
		  const gchar *size,
		  GdkPixbuf *pb)
{
  gchar *path;
  
  path = get_themed_icon_path (full_icon_name, size);
  
  if (path == NULL)
    {
      UNITY_WEBAPPS_NOTE (CONTEXT, "Failed to get path to save themed icon at. Icon directory not readable?");
      return;
    }
  
  gdk_pixbuf_save (pb, path, "png", NULL, NULL); // TODO: Error
  
  g_free (path);
}

gboolean
unity_webapps_icon_theme_add_icon (const gchar *icon_name,
				   const gchar *icon_url,
				   gint size)
{
  GFile *resource_file;
  GFileInputStream *input_stream;
  gchar *resource_path, *sizes, *full_icon_name;
  GdkPixbuf *pb, *scaled_pb;
  
  resource_path = unity_webapps_resource_cache_lookup_uri (icon_url);
  
  // TODO: Better error handlign? When does this fail
  if (resource_path == NULL)
    {
      return FALSE;
    }
  
  resource_file = g_file_new_for_path (resource_path);
  input_stream = g_file_read (resource_file, NULL, NULL); // TODO: Error
  
  pb = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (input_stream), NULL, NULL);
  scaled_pb = gdk_pixbuf_scale_simple (pb, size, size, GDK_INTERP_HYPER);
  
  full_icon_name = g_strdup_printf("%s.png", icon_name);
  sizes = g_strdup_printf("%d", size);
  
  write_themed_icon (full_icon_name, sizes, scaled_pb);
  
  g_object_unref (G_OBJECT (pb));
  g_object_unref (G_OBJECT (scaled_pb));
  
  g_object_unref (G_OBJECT (input_stream));
  g_object_unref (G_OBJECT (resource_file));
  
  g_free (resource_path);
  g_free (full_icon_name);
  g_free (sizes);
  
  return TRUE;
}

static GdkPixbuf *
unity_webapps_icon_theme_get_themed_application_icon_pixbuf (const gchar *icon_name)
{
  GtkIconTheme *icon_theme;
  GdkPixbuf *ret;
  GError *error;
  
  icon_theme = gtk_icon_theme_new ();
  gtk_icon_theme_set_custom_theme (icon_theme, "unity-webapps-applications");
  
#ifdef UNITY_WEBAPPS_TEST_BUILD
  gtk_icon_theme_prepend_search_path (icon_theme, g_getenv ("UNITY_WEBAPPS_CONTEXT_ICON_THEME_DIR"));
#endif

  if (gtk_icon_theme_has_icon (icon_theme, icon_name) == FALSE)
    icon_name = "application-default-icon";
  
  error = NULL;

  ret = gtk_icon_theme_load_icon (icon_theme,
				  icon_name,
				  128,
				  0,
				  &error);
  
  
  if (error != NULL)
    {
      g_warning ("Failed to get themed application icon (%s): %s", icon_name, error->message);
      g_error_free (error);
    }

  g_object_unref (G_OBJECT (icon_theme));
  
  return ret;
}

static gboolean
unity_webapps_icon_theme_copy_themed_application_icon (const gchar *full_icon_name,
						       const gchar *resource_uri)
{
  GdkPixbuf *icon_pb;
  const gchar *icon_name;
  gchar *icon_path;
  gboolean ret;
  GError *error;
  
  icon_name = resource_uri + (7 * sizeof(gchar)); // Strip "icon://"
  
  icon_pb = unity_webapps_icon_theme_get_themed_application_icon_pixbuf (icon_name);
  
  if (icon_pb == NULL)
    {
      return FALSE;
    }
  
  icon_path = unity_webapps_icon_theme_get_application_icon_path (full_icon_name);
  
  error = NULL;
  ret = TRUE;
  
  gdk_pixbuf_save (icon_pb, icon_path, "png", &error, NULL);
  
  if (error != NULL)
    {
      g_warning ("Error saving application icon: %s", error->message);
      
      g_error_free (error);
      
      ret = FALSE;
    }
  
  g_free (icon_path);
  g_object_unref (G_OBJECT (icon_pb));
  
  return ret;
}

gchar *
unity_webapps_icon_theme_update_application_icon (const gchar *icon_name,
						  const gchar *resource_uri)
{
  GFile *resource_file;
  GFileInputStream *input_stream;
  GdkPixbuf *pb;
  gchar *resource_path, *icon_path, *full_icon_name;

  full_icon_name = g_strdup_printf ("%s.png", icon_name);
  
  if (g_str_has_prefix (resource_uri, "icon://"))
    {
      gboolean copied;
      copied = unity_webapps_icon_theme_copy_themed_application_icon (full_icon_name, resource_uri);
      
      return copied ? full_icon_name : NULL;
    }
  
  resource_path = unity_webapps_resource_cache_lookup_uri (resource_uri);
  icon_path = unity_webapps_icon_theme_get_application_icon_path (full_icon_name);
  
  if (resource_path == NULL)
    {
      UNITY_WEBAPPS_NOTE (CONTEXT, "Failed to update application icon");

      return full_icon_name;
    }

  resource_file = g_file_new_for_path (resource_path);
  
  if (resource_file == NULL)
    {
      g_warning ("Invalid icon URI: %s", resource_uri);
      g_free (resource_path);
      g_free (icon_path);

      return full_icon_name;
    }

  input_stream = g_file_read (resource_file, NULL, NULL); // TODO Error
  
  pb = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (input_stream), NULL, NULL);
  
  gdk_pixbuf_save (pb, icon_path, "png", NULL, NULL); // TODO: Error
  
  write_themed_icon (full_icon_name, "48", pb);
  
  
  g_object_unref (G_OBJECT (resource_file));
  g_object_unref (G_OBJECT (input_stream));

  g_object_unref (pb);
  
  g_free (resource_path);
  g_free (icon_path);
  
  return full_icon_name;
}
