/*
 * unity-webapps-application-info.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 <string.h>
#include <gio/gio.h>
#include <glib/gstdio.h>
#include <libsoup/soup.h>

#include "unity-webapps-application-info.h"

#include "unity-webapps-dirs.h"
#include "unity-webapps-app-db.h"
#include "unity-webapps-icon-theme.h"

#include "../unity-webapps-debug.h"
#include "../unity-webapps-string-utils.h"
#include "../unity-webapps-desktop-infos.h"

struct _UnityWebappsApplicationInfoPrivate {
  gchar *name;
  gchar *domain;

  gchar *icon_url;
  gchar *mime_types;

  GRegex *whitespace_regex;
};

enum {
  PROP_0,
  PROP_NAME,
  PROP_DOMAIN,
  PROP_ICON_URL,
  PROP_MIME_TYPES
};

G_DEFINE_TYPE(UnityWebappsApplicationInfo, unity_webapps_application_info, G_TYPE_OBJECT)

#define UNITY_WEBAPPS_APPLICATION_INFO_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), UNITY_WEBAPPS_TYPE_APPLICATION_INFO, UnityWebappsApplicationInfoPrivate))

static gchar *unity_webapps_application_info_default_get_desktop_file_path (UnityWebappsApplicationInfo *info);
static gchar *unity_webapps_application_info_default_get_desktop_file_name (UnityWebappsApplicationInfo *info);
static gboolean unity_webapps_application_info_default_ensure_desktop_file (UnityWebappsApplicationInfo *info, 
									    GError **error);

static void
unity_webapps_application_info_finalize (GObject *object)
{
  UnityWebappsApplicationInfo *info;
  
  info = UNITY_WEBAPPS_APPLICATION_INFO (object);

  g_free (info->priv->name);
  g_free (info->priv->domain);
  g_free (info->priv->icon_url);
  g_regex_unref(info->priv->whitespace_regex);
}

static void
unity_webapps_application_info_get_property (GObject *object,
					     guint prop_id,
					     GValue *value,
					     GParamSpec *pspec)
{
  UnityWebappsApplicationInfo *info;
  
  info = UNITY_WEBAPPS_APPLICATION_INFO (object);
  
  switch (prop_id)
    {
    case PROP_NAME:
      g_value_set_string (value, info->priv->name);
      break;
    case PROP_DOMAIN:
      g_value_set_string (value, info->priv->domain);
      break;
    case PROP_ICON_URL:
      g_value_set_string (value, info->priv->icon_url);
      break;
    case PROP_MIME_TYPES:
      g_value_set_string (value, info->priv->mime_types);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
unity_webapps_application_info_set_property (GObject *object,
					     guint prop_id,
					     const GValue *value,
					     GParamSpec *pspec)
{
  UnityWebappsApplicationInfo *info;
  
  info = UNITY_WEBAPPS_APPLICATION_INFO (object);
  
  switch (prop_id)
    {
    case PROP_NAME:
      g_return_if_fail (info->priv->name == NULL);
      info->priv->name = g_value_dup_string (value);
      break;
    case PROP_DOMAIN:
      g_return_if_fail (info->priv->domain == NULL);
      info->priv->domain = g_value_dup_string (value);
      break;
    case PROP_ICON_URL:
      g_return_if_fail (info->priv->icon_url == NULL);
      info->priv->icon_url = g_value_dup_string (value);
      break;
    case PROP_MIME_TYPES:
      info->priv->mime_types = g_value_dup_string (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
unity_webapps_application_info_class_init (UnityWebappsApplicationInfoClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  object_class->finalize = unity_webapps_application_info_finalize;
  object_class->finalize = unity_webapps_application_info_finalize;
  
  object_class->get_property = unity_webapps_application_info_get_property;
  object_class->set_property = unity_webapps_application_info_set_property;
  
  klass->get_desktop_file_name = unity_webapps_application_info_default_get_desktop_file_name;
  klass->get_desktop_file_path = unity_webapps_application_info_default_get_desktop_file_path;
  klass->ensure_desktop_file = unity_webapps_application_info_default_ensure_desktop_file;

  g_object_class_install_property (object_class, PROP_NAME,
				   g_param_spec_string ("name",
							"Name",
							"The Application Name",
							NULL,
							G_PARAM_READWRITE | 
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_DOMAIN,
				   g_param_spec_string ("domain",
							"Domain",
							"Domain of the application",
							NULL,
							G_PARAM_READWRITE | 
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_ICON_URL,
				   g_param_spec_string ("icon-url",
							"Icon URL",
							"URI for the primary application icon",
							NULL,
							G_PARAM_READWRITE | 
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class, PROP_MIME_TYPES,
				   g_param_spec_string ("mime-types",
							"Mime Types",
							"Capable of opening files with the given content type",
							NULL,
							G_PARAM_READWRITE |
							G_PARAM_STATIC_STRINGS |
							G_PARAM_CONSTRUCT_ONLY));

  g_type_class_add_private (object_class, sizeof(UnityWebappsApplicationInfoPrivate));
}

static void
unity_webapps_application_info_init (UnityWebappsApplicationInfo *manager)
{
  manager->priv = UNITY_WEBAPPS_APPLICATION_INFO_GET_PRIVATE (manager);

  manager->priv->name = NULL;
  manager->priv->domain = NULL;
  manager->priv->icon_url = NULL;
  manager->priv->whitespace_regex = g_regex_new("\\s", 0, 0, NULL);
}

UnityWebappsApplicationInfo *
unity_webapps_application_info_new (const gchar *name, const gchar *domain, const gchar *icon_url, const gchar *mime_types)
{
  return g_object_new (UNITY_WEBAPPS_TYPE_APPLICATION_INFO, 
		       "name", name,
		       "domain", domain,
		       "icon-url", icon_url,
		       "mime-types", mime_types,
		       NULL);
}

const gchar *
unity_webapps_application_info_get_name (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);
  
  return info->priv->name;
}

const gchar *
unity_webapps_application_info_get_domain (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);
  
  return info->priv->domain;
}

const gchar *
unity_webapps_application_info_get_icon_url (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);

  return info->priv->icon_url;
}

gchar *
unity_webapps_application_info_get_canonical_name (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);

  return unity_webapps_string_utils_canonicalize_string (info->priv->name, TRUE);
}

gchar *
unity_webapps_application_info_get_canonical_domain (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);

  return unity_webapps_string_utils_canonicalize_string (info->priv->domain, TRUE);
}

gchar *
unity_webapps_application_info_get_canonical_full_name (UnityWebappsApplicationInfo *info)
{
  gchar *canonical_name, *canonical_domain, *canonical_full_name;

  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);
  
  canonical_name = unity_webapps_application_info_get_canonical_name (info);
  canonical_domain = unity_webapps_application_info_get_canonical_domain (info);
  
  canonical_full_name = g_strdup_printf ("%s%s", canonical_name, canonical_domain);
  
  g_free (canonical_name);
  g_free (canonical_domain);
  
  return canonical_full_name;
}

static gchar *
unity_webapps_application_info_default_get_desktop_file_name (UnityWebappsApplicationInfo *info)
{
  gchar *desktop_file_name, *desktop_basename;
  const gchar *name, *domain;

  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);

  name = unity_webapps_application_info_get_name (info);
  domain = unity_webapps_application_info_get_domain (info);

  desktop_basename = unity_webapps_desktop_infos_build_desktop_basename (name, domain);
  desktop_file_name = g_strdup_printf("%s.desktop", desktop_basename);

  g_free(desktop_basename);

  return desktop_file_name;
}

static gchar *
unity_webapps_application_info_default_get_desktop_file_path (UnityWebappsApplicationInfo *info)
{
  gchar *desktop_file_name, *path;
  
  desktop_file_name = unity_webapps_application_info_get_desktop_file_name (info);
  
  path = g_build_filename (unity_webapps_dirs_get_application_dir (), desktop_file_name, NULL);
  
  g_free (desktop_file_name);
  
  return path;
}

gchar *
unity_webapps_application_info_get_desktop_icon_name (UnityWebappsApplicationInfo *info, gboolean check_themed)
{
  gchar *icon_name;

  // Strip icon://
  if (g_str_has_prefix(info->priv->icon_url, "icon://"))
    {
      icon_name = g_strdup(info->priv->icon_url + 7); // sizeof(icon://)
    }
  else
   {
     icon_name = g_strdup(info->priv->icon_url);
   }

  return icon_name;
}

static gchar *
validate_mime_str (const gchar *in)
{
  gchar *out = NULL, **it;

  if (!in)
    return NULL;

  gchar **mimes = g_strsplit (in, ";", -1);
  GList *registered = g_content_types_get_registered ();

  for (it = mimes; it && *it; it++)
    {
      if (g_list_find_custom (registered, *it, (GCompareFunc)g_strcmp0))
	{
	  gchar *tmp = g_strconcat (*it, ";", out, NULL);
	  g_free (out);
	  out = tmp;
	}
    }
  g_list_free_full (registered, g_free);
  g_strfreev (mimes);

  return out;
}

static gchar *
unity_webapps_application_info_get_desktop_file_contents (UnityWebappsApplicationInfo *info)
{
  gint i;
  gchar **labels, **pages, **il, **ip;
  gchar *contents, *icon_name, *mime_types;
  gchar *name = unity_webapps_string_utils_canonicalize_string (info->priv->name, TRUE);
  gchar *base64_name = g_base64_encode ((guchar*)info->priv->name, strlen (info->priv->name));
  gchar *domain = info->priv->domain ? info->priv->domain : "";
  gchar *desktop_id = unity_webapps_desktop_infos_build_desktop_basename (name, domain);

  icon_name = unity_webapps_application_info_get_desktop_icon_name (info, TRUE);

  mime_types = validate_mime_str (info->priv->mime_types);
  if (!mime_types)
    mime_types = g_strdup ("");

  contents = g_strdup_printf ("[Desktop Entry]\nName=%s\nType=Application\nIcon=%s\nMimeType=%s\nActions=S0;S1;S2;S3;S4;S5;S6;S7;S8;S9;S10;\nExec=unity-webapps-runner -n '%s' -d '%s' %%u\nStartupWMClass=%s\n",
			      name, icon_name, mime_types,
			      base64_name,
			      info->priv->domain,
			      desktop_id);

  unity_webapps_app_db_get_actions (info->priv->name, info->priv->domain, &labels, &pages);

  for (i = 0, il = labels, ip = pages; il && *il; il++, i++, ip++)
    {
      gchar *label = unity_webapps_string_utils_canonicalize_string (*il, TRUE);
      if (!label)
	continue;

      SoupURI *uri = soup_uri_new (*ip);
      if (!uri)
	{
	  g_free (label);
	  continue;
	}
      gchar *page = soup_uri_to_string (uri, FALSE);
      soup_uri_free (uri);

      gchar *tmp = g_strdup_printf ("%s\n[Desktop Action S%d]\nName=%s\nOnlyShowIn=Unity;\nExec=xdg-open '%s'\n",
				    contents, i, label, page);

      g_free (contents);
      g_free (label);
      contents = tmp;
    }

  g_free (icon_name);
  g_free (mime_types);
  g_free (base64_name);
  g_free (name);
  g_free (desktop_id);
  g_strfreev (labels);
  g_strfreev (pages);

  return contents;
}

gboolean
unity_webapps_application_info_write_desktop_file_to_path (UnityWebappsApplicationInfo *info,
							   const gchar *desktop_file_path,
							   GError **error)
{
  gchar *desktop_file_contents;
  gboolean success;
  
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), FALSE);
  
  desktop_file_contents = unity_webapps_application_info_get_desktop_file_contents (info);
  
  success = g_file_set_contents (desktop_file_path, desktop_file_contents, -1, error);
  if (success == TRUE)
    {
      g_chmod(desktop_file_path, 0744);
    }

  UNITY_WEBAPPS_NOTE (APPLICATION_INFO, "Wrote desktop file: %s", desktop_file_path);
  
  g_free (desktop_file_contents);
  
  return success;
}

static gboolean
unity_webapps_application_info_default_ensure_desktop_file (UnityWebappsApplicationInfo *info, 
							    GError **error)
{
  gchar *desktop_file_path;
  gboolean success;

  desktop_file_path = unity_webapps_application_info_get_desktop_file_path (info);
  success = unity_webapps_application_info_write_desktop_file_to_path (info, desktop_file_path, error);

  g_free (desktop_file_path);

  return success;
}

gchar *
unity_webapps_application_info_save_icon_file (UnityWebappsApplicationInfo *info)
{
  gchar *icon_name, *icon_full_name;
  
  icon_name = unity_webapps_application_info_get_desktop_icon_name (info, TRUE);
  
  icon_full_name = unity_webapps_icon_theme_update_application_icon (icon_name, info->priv->icon_url);
  
  g_free (icon_name);
  
  return icon_full_name;
}

gchar *
unity_webapps_application_info_get_homepage (UnityWebappsApplicationInfo *info)
{
  gchar *homepage;

  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);
  
  homepage = unity_webapps_app_db_get_homepage (info->priv->name, info->priv->domain);
  
  if (homepage == NULL)
    {
      homepage = g_strdup_printf("http://%s", info->priv->domain);
    }
  
  return homepage;
}
					     

void
unity_webapps_application_info_set_homepage (UnityWebappsApplicationInfo *info,
					     const gchar *homepage)
{
  g_return_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info));
  
  unity_webapps_app_db_update_homepage (info->priv->name,
					info->priv->domain,
					homepage);
}

void
unity_webapps_application_info_add_icon_at_size (UnityWebappsApplicationInfo *info,
						 const gchar *icon_url,
						 gint size)
{
  gchar *icon_name;
  
  icon_name = unity_webapps_application_info_get_desktop_icon_name (info, FALSE);
  
  unity_webapps_icon_theme_add_icon (icon_name, icon_url, size);
  
  g_free (icon_name);
}

#define BROWSER_EXEC_STRING "xdg-open"

gboolean
unity_webapps_application_info_open_new_instance (UnityWebappsApplicationInfo *info)
{
  gchar *commandline, *homepage;
  GError *error;
  gboolean ret;

  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), FALSE);
  
  homepage = unity_webapps_application_info_get_homepage (info);
  g_assert (homepage);
  
  commandline = g_strdup_printf ("%s '%s'", BROWSER_EXEC_STRING,
				 homepage);
  
  error = NULL;
  
  g_spawn_command_line_async (commandline, &error);
  
  ret = TRUE;
  
  if (error != NULL)
    {
      UNITY_WEBAPPS_NOTE (APPLICATION_INFO, "Error invoking xdg-open to launch new application instance: %s", error->message);
      g_error_free (error);
      
      ret = FALSE;
    }
  
  g_free (homepage);
  g_free (commandline);
  
  return ret;  
}

gchar *
unity_webapps_application_info_get_desktop_file_name (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);
  
  if (UNITY_WEBAPPS_APPLICATION_INFO_GET_CLASS (info)->get_desktop_file_name != NULL)
    {
      return UNITY_WEBAPPS_APPLICATION_INFO_GET_CLASS (info)->get_desktop_file_name (info);
    }
  return NULL;
}

gchar *
unity_webapps_application_info_get_desktop_file_path (UnityWebappsApplicationInfo *info)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), NULL);
  
  if (UNITY_WEBAPPS_APPLICATION_INFO_GET_CLASS (info)->get_desktop_file_path != NULL)
    {
      return UNITY_WEBAPPS_APPLICATION_INFO_GET_CLASS (info)->get_desktop_file_path (info);
    }
  return NULL;
}

gboolean
unity_webapps_application_info_ensure_desktop_file (UnityWebappsApplicationInfo *info, 
						    GError **error)
{
  g_return_val_if_fail (UNITY_WEBAPPS_IS_APPLICATION_INFO (info), FALSE);
  
  if (UNITY_WEBAPPS_APPLICATION_INFO_GET_CLASS (info)->ensure_desktop_file != NULL)
    {
      return UNITY_WEBAPPS_APPLICATION_INFO_GET_CLASS (info)->ensure_desktop_file (info, error);
    }
  return FALSE;
}
