/*
 * unity-webapps-application-manifest.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-application-manifest.h"

struct _UnityWebappsApplicationManifestPrivate {
  gchar *base_path;
  gchar *name;
  gchar *package_name;
  gchar *domain;
  
  gchar *integration_version;
  

  GPtrArray *scripts;
  GPtrArray *requires;
  GPtrArray *includes;
};

enum {
  PROP_0,
  PROP_NAME
};

G_DEFINE_TYPE(UnityWebappsApplicationManifest, unity_webapps_application_manifest, G_TYPE_OBJECT)

#define UNITY_WEBAPPS_APPLICATION_MANIFEST_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), UNITY_WEBAPPS_TYPE_APPLICATION_MANIFEST, UnityWebappsApplicationManifestPrivate))

static void
unity_webapps_application_manifest_get_property (GObject *object,
						 guint prop_id,
						 GValue *value,
						 GParamSpec *pspec)
{
  UnityWebappsApplicationManifest *self;
  
  self = UNITY_WEBAPPS_APPLICATION_MANIFEST (object);
  
  switch (prop_id)
    {
    case PROP_NAME:
      g_value_set_string (value, self->priv->name);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
unity_webapps_application_manifest_set_property (GObject *object,
						 guint prop_id,
						 const GValue *value,
						 GParamSpec *pspec)
{
  UnityWebappsApplicationManifest *self;
  
  self = UNITY_WEBAPPS_APPLICATION_MANIFEST (object);
  
  switch (prop_id)
    {
    case PROP_NAME:
      g_return_if_fail (self->priv->name == NULL);
      self->priv->name = g_value_dup_string (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
unity_webapps_application_manifest_finalize (GObject *object)
{
  UnityWebappsApplicationManifest *manifest;

  manifest = UNITY_WEBAPPS_APPLICATION_MANIFEST (object);
  
  g_free (manifest->priv->name);
  g_free (manifest->priv->base_path);
  g_free (manifest->priv->package_name);
  g_free (manifest->priv->integration_version);
  g_free (manifest->priv->domain);

  g_ptr_array_unref (manifest->priv->scripts);
  g_ptr_array_unref (manifest->priv->includes);
  
  if (manifest->priv->requires != NULL)
    g_ptr_array_unref (manifest->priv->requires);
}

static void
unity_webapps_application_manifest_class_init (UnityWebappsApplicationManifestClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  object_class->finalize = unity_webapps_application_manifest_finalize;
  object_class->get_property = unity_webapps_application_manifest_get_property;
  object_class->set_property = unity_webapps_application_manifest_set_property;
  
  g_type_class_add_private (object_class, sizeof(UnityWebappsApplicationManifestPrivate));
}


static void
unity_webapps_application_manifest_init (UnityWebappsApplicationManifest *self)
{
  self->priv = UNITY_WEBAPPS_APPLICATION_MANIFEST_GET_PRIVATE (self);
  
  self->priv->name = NULL;
  self->priv->package_name = NULL;
  self->priv->base_path = NULL;
  self->priv->integration_version = NULL;

  self->priv->scripts = NULL;
  self->priv->includes = NULL;
  self->priv->requires = NULL;
}

UnityWebappsApplicationManifest *
unity_webapps_application_manifest_new ()
{
  return g_object_new (UNITY_WEBAPPS_TYPE_APPLICATION_MANIFEST, NULL);
}

static gboolean
unity_webapps_application_manifest_gptrarray_from_jsonarray (UnityWebappsApplicationManifest *self,
                                                              JsonArray *jsonarray,
                                                              GPtrArray *gptrarray)
{
  gint i;
  guint len;

  // TODO ADD CHECK TO RETURN FALSE IF PASSED A BAD GPTR_ARRAY

  len = json_array_get_length (jsonarray);
  for (i = 0; i < len; i++)
    {
      const gchar *element;
      element = json_array_get_string_element (jsonarray, i);
      g_ptr_array_add (gptrarray, g_strdup(element));
    }
  g_ptr_array_add (gptrarray, NULL);

  return TRUE;
}

static gboolean
unity_webapps_application_manifest_load_scripts (UnityWebappsApplicationManifest *self,
                                                 JsonArray *scripts)
{
  self->priv->scripts = g_ptr_array_new_with_free_func (g_free);

  return unity_webapps_application_manifest_gptrarray_from_jsonarray (self, scripts, self->priv->scripts);
}

static gboolean
unity_webapps_application_manifest_load_requires (UnityWebappsApplicationManifest *self,
                                                  JsonArray *requires)
{
  self->priv->requires = g_ptr_array_new_with_free_func (g_free);

  return unity_webapps_application_manifest_gptrarray_from_jsonarray (self, requires, self->priv->requires);
}

static gboolean
unity_webapps_application_manifest_load_includes (UnityWebappsApplicationManifest *self,
						  JsonArray *includes)
{
  self->priv->includes = g_ptr_array_new_with_free_func (g_free); 
  
  return unity_webapps_application_manifest_gptrarray_from_jsonarray(self, includes, self->priv->includes);
}

gboolean
unity_webapps_application_manifest_load_string_property (UnityWebappsApplicationManifest *self,
							 JsonObject *root_object,
							 const gchar *property_name,
							 gchar **property)
{
  gboolean has_property;
  const gchar *property_value;
  
  has_property = json_object_has_member (root_object, property_name);
  
  if (has_property == FALSE)
    {
      g_warning ("Failed to find %s member of JSON manifest", property_name);
      return FALSE;
    }
  property_value = json_object_get_string_member (root_object, property_name);
  *property = g_strdup (property_value);
  return TRUE;
}

gboolean
unity_webapps_application_manifest_load_from_json (UnityWebappsApplicationManifest *self,
						   JsonParser *parser)
{
  JsonNode *root;
  JsonObject *root_object;
  JsonArray *scripts;
  JsonArray *includes;
  JsonArray *requires;
  gchar *tmp_package_name;
  gboolean has_scripts, has_includes, has_requires, parsed;
  
  parsed = TRUE;

  root = json_parser_get_root (parser);
  root_object = json_node_get_object (root);
  
  parsed = unity_webapps_application_manifest_load_string_property (self, root_object, "name", &self->priv->name);
  
  if (parsed == FALSE)
    {
      goto out;
    }

  parsed = unity_webapps_application_manifest_load_string_property (self, root_object, "domain", &self->priv->domain);

  parsed = unity_webapps_application_manifest_load_string_property (self, root_object, "package-name", &self->priv->package_name);

  if (parsed == FALSE)
    {
      goto out;
    }
  tmp_package_name = self->priv->package_name;
  self->priv->package_name = g_strconcat ("unity-webapps-", self->priv->package_name, NULL);
  g_free (tmp_package_name);
  
  tmp_package_name = self->priv->package_name;
  self->priv->package_name = g_utf8_strdown (tmp_package_name, -1);
  g_free (tmp_package_name);
  
  parsed = unity_webapps_application_manifest_load_string_property (self, root_object, "integration-version", &self->priv->integration_version);
  
  if (parsed == FALSE)
    {
      goto out;
    }
  
  has_scripts = json_object_has_member (root_object, "scripts");

  if (has_scripts == FALSE)
    {
      g_warning ("Failed to find scripts member of JSON manifest");
      parsed = FALSE;
      
      goto out;
    }

  scripts = json_object_get_array_member (root_object, "scripts");
  unity_webapps_application_manifest_load_scripts (self, scripts);

  has_includes = json_object_has_member (root_object, "includes");
  
  if (has_includes == FALSE)
    {
      g_warning ("Failed to find includes member of JSON manifest");
      parsed = FALSE;
      
      goto out;
    }
  
  includes = json_object_get_array_member (root_object, "includes");
  unity_webapps_application_manifest_load_includes (self, includes);
  
  has_requires = json_object_has_member (root_object, "requires");
  
  if (has_requires)
    {
      requires = json_object_get_array_member (root_object, "requires");
      unity_webapps_application_manifest_load_requires (self, requires);
    }

 out:  
  return parsed;
}

gboolean
unity_webapps_application_manifest_load_from_file (UnityWebappsApplicationManifest *self, 
						   const gchar *filename)

{
  JsonParser *parser;
  GError *error;
  gboolean loaded;

  self->priv->base_path = g_path_get_dirname (filename);

  parser = json_parser_new ();
  
  error = NULL;
  
  json_parser_load_from_file (parser, filename, &error);
  
  if (error != NULL)
    {
      g_warning ("Failed to parse manifest at %s as json: %s", filename, error->message);
      g_object_unref (G_OBJECT (parser));
      
      return FALSE;
    }
  
  loaded = unity_webapps_application_manifest_load_from_json (self, parser);
  
  g_object_unref (G_OBJECT (parser));
  return loaded;
}


gboolean
unity_webapps_application_manifest_load_from_data (UnityWebappsApplicationManifest *self, 
						   const gchar *data)

{
  JsonParser *parser;
  GError *error;
  gboolean loaded;

  self->priv->base_path = NULL;

  parser = json_parser_new ();
  
  error = NULL;
  
  json_parser_load_from_file (parser, data, &error);
  
  if (error != NULL)
    {
      g_warning ("Failed to parse manifest as json: %s", error->message);
      g_object_unref (G_OBJECT (parser));
      
      return FALSE;
    }
  
  loaded = unity_webapps_application_manifest_load_from_json (self, parser);
  
  g_object_unref (G_OBJECT (parser));
  return loaded;
}

const gchar *
unity_webapps_application_manifest_get_name (UnityWebappsApplicationManifest *manifest)
{
  return manifest->priv->name;
}

const gchar *
unity_webapps_application_manifest_get_domain (UnityWebappsApplicationManifest *manifest)
{
  return manifest->priv->domain;
}

const gchar *
unity_webapps_application_manifest_get_package_name (UnityWebappsApplicationManifest *manifest)
{
  return manifest->priv->package_name;
}

const gchar **
unity_webapps_application_manifest_get_scripts (UnityWebappsApplicationManifest *manifest)
{
  return (const gchar **)manifest->priv->scripts->pdata;
}

const gchar **
unity_webapps_application_manifest_get_requires (UnityWebappsApplicationManifest *manifest)
{
  return manifest->priv->requires ? (const gchar **)manifest->priv->requires->pdata : NULL;
}

const gchar **
unity_webapps_application_manifest_get_includes (UnityWebappsApplicationManifest *manifest)
{
  return (const gchar **)manifest->priv->includes->pdata;
}

const gchar *
unity_webapps_application_manifest_get_base_path (UnityWebappsApplicationManifest *manifest)
{
  return manifest->priv->base_path;
}

UnityWebappsApplicationManifest *
unity_webapps_application_manifest_new_from_file (const gchar *filename)
{
  UnityWebappsApplicationManifest *manifest;
  gboolean loaded;
  
  manifest = unity_webapps_application_manifest_new ();
  loaded = unity_webapps_application_manifest_load_from_file (manifest, filename);
  
  if (loaded == FALSE)
    {
      g_object_unref (G_OBJECT (manifest));
      return NULL;
    }
  return manifest;
}


UnityWebappsApplicationManifest *
unity_webapps_application_manifest_new_from_data (const gchar *data)
{
  UnityWebappsApplicationManifest *manifest;
  gboolean loaded;
  
  manifest = unity_webapps_application_manifest_new ();
  loaded = unity_webapps_application_manifest_load_from_data (manifest, data);
  
  if (loaded == FALSE)
    {
      g_object_unref (G_OBJECT (manifest));
      return NULL;
    }
  return manifest;
}

