/*
 * unity-webapps-application-collector.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 <json-glib/json-glib.h>

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

#include "unity-webapps-application-collector.h"
#include "unity-webapps-application-manifest.h"
#include "unity-webapps-local-available-application.h"

struct _UnityWebappsApplicationCollectorPrivate {
  GHashTable *found_applications;
  gchar **path;
};

// TODO: The path to search should be a property of this object
enum {
  PROP_0,
  PROP_PATH
};

G_DEFINE_TYPE(UnityWebappsApplicationCollector, unity_webapps_application_collector, G_TYPE_OBJECT)

#define UNITY_WEBAPPS_APPLICATION_COLLECTOR_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), UNITY_WEBAPPS_TYPE_APPLICATION_COLLECTOR, UnityWebappsApplicationCollectorPrivate))


static void
unity_webapps_application_collector_get_property (GObject *object,
						 guint prop_id,
						 GValue *value,
						 GParamSpec *pspec)
{
  UnityWebappsApplicationCollector *self;
  
  self = UNITY_WEBAPPS_APPLICATION_COLLECTOR (object);
  
  switch (prop_id)
    {
    case PROP_PATH:
      g_value_set_boxed (value, self->priv->path);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
unity_webapps_application_collector_set_property (GObject *object,
						 guint prop_id,
						 const GValue *value,
						 GParamSpec *pspec)
{
  UnityWebappsApplicationCollector *self;
  
  self = UNITY_WEBAPPS_APPLICATION_COLLECTOR (object);
  
  switch (prop_id)
    {
    case PROP_PATH:
      g_assert (self->priv->path == NULL);
      self->priv->path = (gchar **)g_value_dup_boxed (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}


static void
unity_webapps_application_collector_finalize (GObject *object)
{
  UnityWebappsApplicationCollector *collector;

  collector = UNITY_WEBAPPS_APPLICATION_COLLECTOR (object);
  
  if (collector->priv->found_applications)
    {
      g_hash_table_destroy (collector->priv->found_applications);
    }
  g_strfreev (collector->priv->path);
}

static void
unity_webapps_application_collector_class_init (UnityWebappsApplicationCollectorClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  object_class->finalize = unity_webapps_application_collector_finalize;
  object_class->get_property = unity_webapps_application_collector_get_property;
  object_class->set_property = unity_webapps_application_collector_set_property;
  
  g_object_class_install_property (object_class, PROP_PATH,
				   g_param_spec_boxed ("path", "Path",
						       "Search path for the application collector",
						       G_TYPE_STRV,
						       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
						       G_PARAM_STATIC_STRINGS));
				   
  
  g_type_class_add_private (object_class, sizeof(UnityWebappsApplicationCollectorPrivate));
}


static void
unity_webapps_application_collector_init (UnityWebappsApplicationCollector *self)
{
  self->priv = UNITY_WEBAPPS_APPLICATION_COLLECTOR_GET_PRIVATE (self);
  
  self->priv->found_applications = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}

static gchar **
unity_webapps_application_collector_get_default_path ()
{
  const gchar *const *data_dirs;
  const gchar *user_dir;
  gchar **path;
  gint data_dirs_len, i;
  
  user_dir = unity_webapps_dirs_get_user_dir ();

  data_dirs = g_get_system_data_dirs();
  data_dirs_len = g_strv_length ((gchar **)data_dirs);
  
  path = g_malloc0 (sizeof(gchar *)*(2+data_dirs_len));

  path[0] = g_strdup (user_dir);

  for (i = 1; i < data_dirs_len+1; i++)
    {
      path[i] = g_build_filename (data_dirs[i-1], "unity-webapps", "userscripts", NULL);
    }
  path[i] = NULL;
  
  return path;
}

UnityWebappsApplicationCollector *
unity_webapps_application_collector_new_with_default_path ()
{
  UnityWebappsApplicationCollector *collector;
  gchar **path;
  
  path = unity_webapps_application_collector_get_default_path ();
  collector = g_object_new (UNITY_WEBAPPS_TYPE_APPLICATION_COLLECTOR, "path", path, NULL);
  g_strfreev (path);
  
  return collector;
}

UnityWebappsApplicationCollector *
unity_webapps_application_collector_new (const gchar **path)
{
  return g_object_new (UNITY_WEBAPPS_TYPE_APPLICATION_COLLECTOR, "path",
		       path, NULL);
}

static gboolean
unity_webapps_application_collector_check_path (UnityWebappsApplicationCollector *collector,
						const gchar *manifest_path)
{
  UnityWebappsApplicationManifest *manifest;
  UnityWebappsLocalAvailableApplication *app;
  const gchar *app_name;
  gboolean manifest_exists, parsed_manifest, ret;

  ret = TRUE;
  manifest = NULL;
  app = NULL;

  manifest_exists = g_file_test (manifest_path, G_FILE_TEST_EXISTS);

  if (manifest_exists == FALSE)
    {
      UNITY_WEBAPPS_NOTE (APPLICATION_REPOSITORY, "Failed to find manifest.json in app folder: %s", manifest_path);
      ret = FALSE;
      goto out;
    }

  manifest = unity_webapps_application_manifest_new ();
  parsed_manifest = unity_webapps_application_manifest_load_from_file (manifest, manifest_path);

  if (parsed_manifest == FALSE)
    {
      UNITY_WEBAPPS_NOTE (APPLICATION_REPOSITORY, "Failed to parse manifest.json from app folder: %s", manifest_path);
      ret = FALSE;
      goto out;
    }

  app_name = unity_webapps_application_manifest_get_package_name (manifest);
  app = (UnityWebappsLocalAvailableApplication *) unity_webapps_local_available_application_new (manifest);
  g_hash_table_replace (collector->priv->found_applications, g_strdup (app_name),
			g_object_ref (app));

 out:
  if (manifest != NULL)
    {
      g_object_unref (G_OBJECT (manifest));
    }
  if (app != NULL)
    {
      g_object_unref (G_OBJECT (app));
    }
  return ret;
}

static gboolean
unity_webapps_application_collector_search_app_folder (UnityWebappsApplicationCollector *collector,
						       const gchar *app_folder)
{
  gint i;
  gchar *manifest_path;
  gboolean ret;

  UNITY_WEBAPPS_NOTE (APPLICATION_REPOSITORY, "Application collector searching application folder: %s", app_folder);

  manifest_path = g_build_filename (app_folder, "manifest.json", NULL);
  ret = unity_webapps_application_collector_check_path (collector, manifest_path);
  g_free (manifest_path);

  if (ret)
    return ret;

  for (i = 0; TRUE; i++)
    {
      gboolean b;
      gchar *name = g_strdup_printf ("manifest-%d.json", i);
      manifest_path = g_build_filename (app_folder, name, NULL);
      b = unity_webapps_application_collector_check_path (collector, manifest_path);
      g_free (manifest_path);
      g_free (name);

      if (!b)
	{
	  break;
	}
      else
	{
	  ret = TRUE;
	}
    }

  return ret;
}

static gboolean
unity_webapps_application_collector_search_path_entry (UnityWebappsApplicationCollector *collector, 
						       const gchar *path_entry)
{
  GDir *dir;
  GError *error;
  const gchar *maybe_app_folder;
  
  UNITY_WEBAPPS_NOTE (APPLICATION_REPOSITORY, "Application collector searching path entry: %s", path_entry);

  error = NULL;
  
  dir = g_dir_open (path_entry, 0, &error);
  
  if (error != NULL)
    {
      g_debug ("Failed to open webapp application path dir %s: %s", path_entry, error->message);
      g_error_free (error);
      
      return FALSE;
    }
  
  // TODO: Error
  while ((maybe_app_folder = g_dir_read_name (dir)) != NULL)
    {
      gchar *dir_path;
      
      dir_path = g_build_filename (path_entry, maybe_app_folder, NULL);
      unity_webapps_application_collector_search_app_folder (collector, dir_path);
      g_free (dir_path);
    }
  
  g_dir_close (dir);
  
  return TRUE;
}

gboolean
unity_webapps_application_collector_search_path (UnityWebappsApplicationCollector *collector)
{
  // Path to search should really be property of object...with default path like
  // '$(unity_webapps_dirs_get_user_dir)/', /usr/local/share/unity-webapps', '/usr/share/unity-webapps'
  int i, len;
  
  len = g_strv_length ((gchar **)collector->priv->path);
  
  for (i = 0; i < len; i++)
    {
      const gchar *path_entry = (const gchar *) collector->priv->path[i];
      
      unity_webapps_application_collector_search_path_entry (collector, path_entry);
    }

  return TRUE;
}

// The collector owns the applications
GHashTable *
unity_webapps_application_collector_get_applications (UnityWebappsApplicationCollector *collector)
{
  return collector->priv->found_applications;
}
