/*
 * unity-webapps-apt-package-mechanism.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 "unity-webapps-apt-package-mechanism.h"
#include "unity-webapps-package-mechanism.h"

#define I_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE
#include <packagekit-glib2/packagekit.h>



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

struct _UnityWebappsAptPackageMechanismPrivate {
  PkClient *pk_client;
};


G_DEFINE_TYPE(UnityWebappsAptPackageMechanism, unity_webapps_apt_package_mechanism, UNITY_WEBAPPS_TYPE_PACKAGE_MECHANISM)

#define UNITY_WEBAPPS_APT_PACKAGE_MECHANISM_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), UNITY_WEBAPPS_TYPE_APT_PACKAGE_MECHANISM, UnityWebappsAptPackageMechanismPrivate))

static void
unity_webapps_apt_package_mechanism_finalize (GObject *object)
{
  UnityWebappsAptPackageMechanism *self;
  
  self = UNITY_WEBAPPS_APT_PACKAGE_MECHANISM (object);
  
  g_object_unref (G_OBJECT (self->priv->pk_client));
}

typedef struct _apt_package_mechanism_status_data {
  UnityWebappsAptPackageMechanism *mechanism;
  gchar *name;    
  UnityWebappsPackageMechanismStatusCallback callback;
  gpointer user_data;
  
  gboolean install;
} apt_package_mechanism_status_data;

static void
apt_package_mechanism_package_installed (GObject *object,
					 GAsyncResult *res,
					 gpointer user_data)
{
  apt_package_mechanism_status_data *data;
  PkResults *results;
  GError *error;
  UnityWebappsPackageStatus status;
  
  data = (apt_package_mechanism_status_data *) user_data;
  
  status = UNITY_WEBAPPS_PACKAGE_STATUS_INSTALLED;
  
  error = NULL;

  results = pk_client_generic_finish (PK_CLIENT (object), res, &error);
  
  if (error != NULL)
    {
      status = UNITY_WEBAPPS_PACKAGE_STATUS_AVAILABLE;
      g_warning ("Error installing package: %s", error->message);
      g_error_free (error);
      
      goto out;
    }
  
 out:
  data->callback ((UnityWebappsPackageMechanism *)data->mechanism, data->name, status, data->user_data);
  g_free (data->name);
  g_free (data);
  if (results != NULL)
    {
      g_object_unref (G_OBJECT (results));
    }

}

static void
unity_webapps_apt_package_mechanism_install_pkpackage (apt_package_mechanism_status_data *data,
						     PkPackage *package)
{
  /*
    The package id can be a string like -
    "unity-webapps-reddit;2.4.16+13.10.20130924.2-0ubuntu1;all;Ubuntu"

    We want to strip the origin (Ubuntu) because it may result in a malformed
    apt package version when passed to aptdaemon.
  */

  const gchar *package_id = pk_package_get_id(package);

  gchar** split_id = pk_package_id_split(package_id);
  if (split_id != NULL) { 
    gchar* new_id = pk_package_id_build(split_id[PK_PACKAGE_ID_NAME],
                                        split_id[PK_PACKAGE_ID_VERSION],
                                        split_id[PK_PACKAGE_ID_ARCH],
                                        NULL);
    if (new_id != NULL) {
      GError *error = NULL;
      // Reset the id - this is possibly not ideal, but should not damage
      // anything and avoids leaking the new string
      pk_package_set_id(package, new_id, &error); // fail; fall through with old
      g_free(new_id);
    }
    g_strfreev(split_id);
  }

  gchar *package_ids[] = {NULL, NULL};
  package_ids[0] = (gchar *)pk_package_get_id (package);

  pk_client_install_packages_async (data->mechanism->priv->pk_client, FALSE, package_ids, NULL, NULL, NULL,
				    apt_package_mechanism_package_installed, data);
}

static void
apt_package_mechanism_status_resolved (GObject *object,
				       GAsyncResult *res,
				       gpointer user_data)
{
  PkResults *results;
  apt_package_mechanism_status_data *data;
  GPtrArray *packages;
  PkPackage *first_package;
  GError *error;
  UnityWebappsPackageStatus status;
  PkInfoEnum pk_status;

  status = UNITY_WEBAPPS_PACKAGE_STATUS_UNAVAILABLE;
  data = (apt_package_mechanism_status_data *)user_data;
  
  results = NULL;
  
  error = NULL;
  results = pk_client_generic_finish (PK_CLIENT(object), res, &error);
  
  if (error != NULL)
    {
      g_warning ("Error resolving package name: %s\n", error->message);
      g_error_free (error);
      goto out;
    }
  
  if (!results)
    {
      g_warning ("Error resolving package name (%s): empty results but no error\n", data->name);
      goto out;
    }

  packages = pk_results_get_package_array (results);
  if (!packages || packages->len == 0)
    {
      g_warning("Error resolving package name (%s): empty results\n", data->name);
      goto out;
    }

  // only consider the first package
  first_package = (PkPackage *)g_ptr_array_index (packages, 0);
  if (first_package == NULL)
    {
      g_warning ("No package: %s\n", data->name);
      goto out;
    }
  
  pk_status = pk_package_get_info (first_package);
  if (pk_status & PK_INFO_ENUM_AVAILABLE)
    {
      status = UNITY_WEBAPPS_PACKAGE_STATUS_AVAILABLE;
    }
  else if (pk_status & PK_INFO_ENUM_INSTALLED)
    {
      status = UNITY_WEBAPPS_PACKAGE_STATUS_INSTALLED;
    }
  
  if (data->install && status == UNITY_WEBAPPS_PACKAGE_STATUS_AVAILABLE)
    {
      unity_webapps_apt_package_mechanism_install_pkpackage (data, first_package);
    }
  
 out:
  if (data->install == FALSE && status == UNITY_WEBAPPS_PACKAGE_STATUS_AVAILABLE)
    {
      data->callback ((UnityWebappsPackageMechanism *)data->mechanism, data->name, status, data->user_data);
      g_free (data->name);
      g_free (data);
    }
  
  if (results != NULL)
    {
      g_object_unref (G_OBJECT (results));
    }
}

static gboolean
unity_webapps_apt_package_mechanism_common_resolve (UnityWebappsPackageMechanism *mechanism,
						    const gchar *name,
						    UnityWebappsPackageMechanismStatusCallback callback,
						    gpointer user_data,
						    gboolean install)
{
  UnityWebappsAptPackageMechanism *apt_mechanism;
  apt_package_mechanism_status_data *data;
  const gchar *packages[] = {NULL, NULL};

  apt_mechanism = (UnityWebappsAptPackageMechanism *)mechanism;
  
  data = g_malloc0 (sizeof (apt_package_mechanism_status_data));
  
  data->mechanism = apt_mechanism;
  data->name = g_strdup (name);
  data->callback = callback;
  data->user_data = user_data;
  
  data->install = install;
  
  packages[0] = name;
  
  pk_client_resolve_async (apt_mechanism->priv->pk_client, pk_bitfield_from_enums (PK_FILTER_ENUM_NEWEST, -1),
			   (gchar **)packages, NULL, NULL, NULL, apt_package_mechanism_status_resolved,
			   data);
  
  return TRUE;
}

static gboolean
unity_webapps_apt_package_mechanism_get_package_status (UnityWebappsPackageMechanism *mechanism,
							const gchar *name,
							UnityWebappsPackageMechanismStatusCallback callback,
							gpointer user_data)
{
  return unity_webapps_apt_package_mechanism_common_resolve (mechanism, name, callback, user_data, FALSE);
}

static gboolean
unity_webapps_apt_package_mechanism_install_package (UnityWebappsPackageMechanism *mechanism,
						     const gchar *name,
						     UnityWebappsPackageMechanismStatusCallback callback,
						     gpointer user_data)
{
  return unity_webapps_apt_package_mechanism_common_resolve (mechanism, name, callback, user_data, TRUE);
}

static void
unity_webapps_apt_package_mechanism_class_init (UnityWebappsAptPackageMechanismClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  UnityWebappsPackageMechanismClass *mechanism_class = UNITY_WEBAPPS_PACKAGE_MECHANISM_CLASS (klass);
  
  object_class->finalize = unity_webapps_apt_package_mechanism_finalize;
  
  mechanism_class->get_package_status = unity_webapps_apt_package_mechanism_get_package_status;
  mechanism_class->install_package = unity_webapps_apt_package_mechanism_install_package;

  g_type_class_add_private (object_class, sizeof(UnityWebappsAptPackageMechanismPrivate));
}

static void
unity_webapps_apt_package_mechanism_init (UnityWebappsAptPackageMechanism *self)
{
  self->priv = UNITY_WEBAPPS_APT_PACKAGE_MECHANISM_GET_PRIVATE (self);
  
  self->priv->pk_client = pk_client_new ();
}



UnityWebappsPackageMechanism *
unity_webapps_apt_package_mechanism_new ()
{
  return UNITY_WEBAPPS_PACKAGE_MECHANISM (g_object_new (UNITY_WEBAPPS_TYPE_APT_PACKAGE_MECHANISM, NULL));
}
