/*
 * unity-webapps-resource-factory.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 <gtk/gtk.h>

#include "unity-webapps-dirs.h"
#include "unity-webapps-resource-cache.h"
#include "unity-webapps-resource-factory.h"

#include "unity-webapps-gio-utils.h"

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

#define MAXIMUM_DOWNLOAD_SIZE 100*1024

static gboolean
check_file_too_large_or_empty (GFile *resource_file)
{
  GFileInfo *resource_file_info;
  gsize file_size;
  gboolean too_large;
  GError *error;
  
  UNITY_WEBAPPS_NOTE (RESOURCE, "Resource factory checking size of file: %s", g_file_get_uri (resource_file));
  
  error = NULL;
  
  resource_file_info = g_file_query_info (resource_file, "standard::size",
					  G_FILE_QUERY_INFO_NONE, NULL /* Cancellable */, &error);
  
  if (error != NULL)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Error querying URI info for resource size check: %s", error->message);
      g_error_free (error);
      
      return TRUE;
    }
  
  file_size = g_file_info_get_size (resource_file_info);
  
  too_large = file_size > MAXIMUM_DOWNLOAD_SIZE;
  
#ifdef UNITY_WEBAPPS_ENABLE_DEBUG
  if (too_large == TRUE)
    {
      UNITY_WEBAPPS_NOTE(RESOURCE, "Resource factory found file at URI was too large: %s", g_file_get_uri (resource_file));
    }
#endif
  
  g_object_unref (G_OBJECT (resource_file_info));
  
  return too_large;
}

static GFile *
http_make_file_from_resource_uri (const gchar *resource_uri)
{
  return g_file_new_for_uri (resource_uri);
}

static gchar *
resource_file_name (const gchar *resource_uri)
{
  const gchar *resource_dir;
  gchar *filename, *checksum;
  
  resource_dir = unity_webapps_dirs_get_resource_dir ();
  checksum = unity_webapps_resource_cache_checksum_for_uri (resource_uri);
  
  filename = g_build_filename (resource_dir, checksum, NULL);
  
  g_free (checksum);
  
  return filename;
}

static gboolean
parse_data_uri (const gchar *data_uri,
		gchar **mimetype,
		gchar **data)
{
  gchar **split;
  int result;
  
  g_assert (g_str_has_prefix (data_uri, "data:") == TRUE);

  split = g_strsplit (data_uri+5,
		      ";base64,",
		      2);

  result = g_strv_length (split);
  
  if (result != 2)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Failed to parse data uri: \n %s \n", data_uri);

      *mimetype = NULL;
      *data = NULL;
      
      g_strfreev (split);

      return FALSE;
    }
  
  *mimetype = split[0];
  *data = split[1];
  
  g_free (split);
  
  if (g_str_has_prefix (*mimetype, "image/") == FALSE)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Data URI does not have an image mimetype");

      g_free (*data);

      *mimetype = NULL;
      *data = NULL;
      
      return FALSE;
    }
  
  return TRUE;
}

static gboolean
save_data_to_file (const guchar *data,
		   gsize data_length,
		   const gchar *resource_name)
{
  GFile *resource_file;
  GFileOutputStream *output_stream;
  gsize bytes_written;
  gboolean saved;
  GError *error;
  
  resource_file = g_file_new_for_path (resource_name);
  
  error = NULL;
  
  output_stream = g_file_replace (resource_file, NULL,
				  FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
				  NULL, &error);
  
  if (error != NULL)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Error opening %s to save data URI: %s", resource_name, error->message);
      
      g_error_free (error);
      
      g_object_unref (G_OBJECT (resource_file));
      return FALSE;
    }
  
  saved = g_output_stream_write_all (G_OUTPUT_STREAM (output_stream), data,
				     data_length, &bytes_written, NULL,
				     &error);
  
  if (error != NULL)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Error writing to file (%s) in saving data URI: %s",
		 resource_name, error->message);
      g_error_free (error);
    }
  g_object_unref (G_OBJECT (output_stream));
  g_object_unref (G_OBJECT (resource_file));
  
  return saved;
  
}

static gboolean
data_uri_to_file (const gchar *resource_uri,
		  const gchar *resource_name)
{
  gchar *mimetype, *data;
  guchar *decoded;
  gsize decoded_length;
  gboolean parsed, saved;
  
  mimetype = NULL;
  data = NULL;
  
  parsed = parse_data_uri (resource_uri, &mimetype, &data);
  
  if (parsed == FALSE)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Failed to save data uri");
      return FALSE;
    }
  
  decoded = g_base64_decode (data, &decoded_length);
  g_assert (decoded != NULL);
  
  saved = save_data_to_file (decoded, decoded_length, resource_name);
  
  g_free (mimetype);
  g_free (data);
  g_free (decoded);

  return saved;
}

static GFile *
data_uri_make_file_from_resource_uri (const gchar *icon_uri)
{
  GFile *local_file;
  gchar *tmp_name;

  
  tmp_name = tempnam (NULL, "uwatf");
  
  data_uri_to_file (icon_uri, tmp_name);
  
  local_file = g_file_new_for_path (tmp_name);
  
  g_free (tmp_name);
  
  return local_file;
}

static gchar *
get_themed_resource_path (const gchar *icon_name)
{
  GtkIconTheme *theme;
  GtkIconInfo *info;
  gchar *icon_path;
  
  if (icon_name == NULL)
    return NULL;
  
  theme = gtk_icon_theme_get_default ();
  UNITY_WEBAPPS_NOTE (RESOURCE, "Resource factory looking up icon name: %s", icon_name);
  
  info = gtk_icon_theme_lookup_icon (theme, icon_name, 48, 0);
  
  if (info == NULL)
    {
      return NULL;
    }
  
  icon_path = g_strdup (gtk_icon_info_get_filename (info));
  
  gtk_icon_info_free (info);
  
  return icon_path;
}


static GFile *
icon_uri_make_file_from_resource_uri (const gchar *resource_uri)
{
  GFile *resource_file;
  gchar *resource_path;

  resource_path = get_themed_resource_path (resource_uri+7);
  
  resource_file = g_file_new_for_path (resource_path);
  
  g_free (resource_path);
  
  return resource_file;  
}

static GFile *
make_file_from_resource_uri (const gchar *resource_uri,
			 gboolean allow_themed_icons)
{
  GFile *resource_file;
  gboolean too_large;
  
  resource_file = NULL;
  
  UNITY_WEBAPPS_NOTE (RESOURCE, "Resource factory making file from resource URI (%s)", resource_uri);
  if (g_str_has_prefix (resource_uri, "http://") || g_str_has_prefix (resource_uri, "https://"))
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Making file from HTTP URI");
      resource_file = http_make_file_from_resource_uri (resource_uri);
    }
  else if (g_str_has_prefix (resource_uri, "data:"))
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Making file from data URI");
      if (strlen (resource_uri) > MAXIMUM_DOWNLOAD_SIZE)
	{
	  g_warning ("Icon URI greater than maximum download size (size of uri is %d bytes)", (gint)strlen (resource_uri));
	  resource_file = NULL;
	}
      else
	{
	  resource_file = data_uri_make_file_from_resource_uri (resource_uri);
	}
    }
  else if (g_str_has_prefix (resource_uri, "icon://") && allow_themed_icons)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Making file from theme URI");
      resource_file = icon_uri_make_file_from_resource_uri (resource_uri);
    }
  
  if (resource_file != NULL)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Succesfully created resource file for URI (%s): %s", resource_uri,
			  g_file_get_uri (resource_file));
      too_large = check_file_too_large_or_empty (resource_file);
      
      if (too_large == TRUE)
	{
	  UNITY_WEBAPPS_NOTE (RESOURCE, "Icon file is too large or empty %s", resource_uri);
	  g_object_unref (G_OBJECT (resource_file));
	  
	  resource_file = NULL;
	}
      
    }

  return resource_file;
}




gchar *
unity_webapps_resource_factory_resource_path_for_uri (const gchar *resource_uri, gboolean allow_themed_icons)
{
  GFile *resource_file, *local_file;
  gchar *resource_name;
  gboolean spliced;
  
  UNITY_WEBAPPS_NOTE (RESOURCE, "Resource Factory getting resource path for URI (%s)",
		      resource_uri);
  
  if (allow_themed_icons && g_str_has_prefix (resource_uri, "icon://"))
    {
      return get_themed_resource_path (resource_uri+7);
    }
  
  resource_file = make_file_from_resource_uri  (resource_uri, allow_themed_icons);

  if (resource_file == NULL)
    {
      return NULL;
    }
  
  resource_name = resource_file_name (resource_uri);
  local_file = g_file_new_for_path (resource_name);

  UNITY_WEBAPPS_NOTE (RESOURCE, "Resource Factory downloading file at %s to resource file", resource_uri);
  
  spliced = unity_webapps_gio_utils_splice_files (resource_file, local_file);
  
  if (spliced == FALSE)
    {
      UNITY_WEBAPPS_NOTE (RESOURCE, "Error saving resource file (%s) to %s", resource_uri, resource_name);
      
      g_free (resource_name);
      
      resource_name = NULL;
    }
  
  g_object_unref (G_OBJECT (resource_file));    
  g_object_unref (G_OBJECT (local_file));    
  
  return resource_name;
}
