/* cloudprovidersaccount.c
 *
 * Copyright (C) 2015 Carlos Soriano <csoriano@gnome.org>
 * Copyright (C) 2017 Julius Haertl <jus@bitgrid.net>
 *
 * This file 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.
 *
 * This file 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 General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cloudprovidersaccount.h"
#include "cloudprovidersprovider.h"
#include "cloudproviders-generated.h"
#include "enums.h"

struct _CloudProvidersAccount
{
  GObject parent;

  gchar *name;
  gchar *path;
  CloudProvidersAccountStatus status;
  gchar *status_details;
  GIcon *icon;
  GMenuModel *menu_model;
  GActionGroup *action_group;

  GDBusConnection *bus;
  CloudProvidersDbusAccount *proxy; // Owned by bus
};

G_DEFINE_TYPE (CloudProvidersAccount, cloud_providers_account, G_TYPE_OBJECT)

/**
 * CloudProvidersAccount:
 *
 * Base object for representing a single account for clients.
 *
 * CloudProvidersAccount is the basic object used to construct the integrator UI
 * and actions that a provider will present to the user, from the client side.
 * Integrators of the cloud providers can use this object to poll the
 * [class@Provider] menus, status and actions.
 */

enum
{
    PROP_0,
    PROP_NAME,
    PROP_ICON,
    PROP_STATUS,
    PROP_STATUS_DETAILS,
    PROP_MENU_MODEL,
    PROP_ACTION_GROUP,
    PROP_PATH,
    N_PROPS
};

static GParamSpec *properties [N_PROPS];

static void
on_name_changed (GObject               *object,
                 GParamSpec            *pspec,
                 CloudProvidersAccount *self)
{
    g_free (self->name);
    self->name = cloud_providers_dbus_account_dup_name (self->proxy);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]);
}

static void
on_status_changed (GObject               *object,
                   GParamSpec            *pspec,
                   CloudProvidersAccount *self)
{
    self->status = cloud_providers_dbus_account_get_status (self->proxy);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
}

static void
on_status_details_changed (GObject               *object,
                           GParamSpec            *pspec,
                           CloudProvidersAccount *self)
{
    g_free (self->status_details);
    self->status_details = cloud_providers_dbus_account_dup_status_details (self->proxy);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS_DETAILS]);
}

static void
on_icon_changed (GObject               *object,
                 GParamSpec            *pspec,
                 CloudProvidersAccount *self)
{
    GError *error = NULL;

    g_clear_object (&self->icon);
    self->icon = g_icon_new_for_string (cloud_providers_dbus_account_get_icon (self->proxy), &error);
    if (error != NULL)
    {
        g_printerr ("Error getting the icon in the client %s", error->message);
    }
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON]);
}

static void
on_path_changed (GObject               *object,
                 GParamSpec            *pspec,
                 CloudProvidersAccount *self)
{
    g_free (self->path);
    self->path = cloud_providers_dbus_account_dup_path (self->proxy);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PATH]);
}

static void
setup_proxy (CloudProvidersAccount *self)
{
    GMenuModel *menu_model;
    GActionGroup *action_group;
    GIcon *icon = NULL;
    GError *error = NULL;
    g_autofree gchar *bus_name = NULL;
    const gchar *object_path;

    g_signal_connect (self->proxy, "notify::name", G_CALLBACK (on_name_changed), self);
    g_signal_connect (self->proxy, "notify::status", G_CALLBACK (on_status_changed), self);
    g_signal_connect (self->proxy, "notify::status-details", G_CALLBACK (on_status_details_changed), self);
    g_signal_connect (self->proxy, "notify::icon", G_CALLBACK (on_icon_changed), self);
    g_signal_connect (self->proxy, "notify::path", G_CALLBACK (on_path_changed), self);

    icon = g_icon_new_for_string (cloud_providers_dbus_account_get_icon (self->proxy), &error);
    if (error != NULL)
    {
        g_printerr ("Error getting the icon in the client %s", error->message);
    }

    bus_name = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->proxy));
    object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (self->proxy));
    menu_model = (GMenuModel*) g_dbus_menu_model_get (self->bus,
                                                      bus_name,
                                                      object_path);
    action_group = (GActionGroup*) g_dbus_action_group_get (self->bus,
                                                            bus_name,
                                                            object_path);
    self->name = cloud_providers_dbus_account_dup_name (self->proxy);
    self->status = cloud_providers_dbus_account_get_status (self->proxy);
    self->status_details = cloud_providers_dbus_account_dup_status_details (self->proxy);
    self->icon = icon;
    self->path = cloud_providers_dbus_account_dup_path (self->proxy);
    self->menu_model = menu_model;
    self->action_group = action_group;

    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS_DETAILS]);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON]);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PATH]);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTION_GROUP]);
    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MENU_MODEL]);
}

static void
cloud_providers_account_get_property (GObject    *object,
                                      guint       prop_id,
                                      GValue     *value,
                                      GParamSpec *pspec)
{
    CloudProvidersAccount *self = CLOUD_PROVIDERS_ACCOUNT (object);

    switch (prop_id)
    {
        case PROP_NAME:
        {
            g_value_set_string (value, self->name);
        }
        break;

        case PROP_STATUS:
        {
            g_value_set_enum (value, self->status);
        }
        break;

        case PROP_STATUS_DETAILS:
        {
            g_value_set_string (value, self->status_details);
        }
        break;

        case PROP_ICON:
        {
            g_value_set_object (value, self->icon);
        }
        break;

        case PROP_PATH:
        {
            g_value_set_string (value, self->path);
        }
        break;

        case PROP_ACTION_GROUP:
        {
            g_value_set_object (value, self->action_group);
        }
        break;

        case PROP_MENU_MODEL:
        {
            g_value_set_object (value, self->menu_model);
        }
        break;

        default:
        {
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        }
    }
}

static void
cloud_providers_account_set_property (GObject      *object,
                                      guint         prop_id,
                                      const GValue *value,
                                      GParamSpec   *pspec)
{
    switch (prop_id)
    {
        default:
        {
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        }

    }
}

/**
 * cloud_providers_account_new
 * @proxy: DBus proxy
 *
 * A [class@Account] object is used to fetch details about cloud providers from DBus.
 * It is usually fetched from [method@Provider.get_accounts] as a list.
 *
 * Returns: A new [class@Account]
 */
CloudProvidersAccount*
cloud_providers_account_new (GDBusProxy *proxy)
{
  CloudProvidersAccount *self;

  g_return_val_if_fail (CLOUD_PROVIDERS_DBUS_IS_ACCOUNT (proxy), NULL);

  self = g_object_new (CLOUD_PROVIDERS_TYPE_ACCOUNT, NULL);

  self->proxy = CLOUD_PROVIDERS_DBUS_ACCOUNT (g_object_ref (proxy));
  self->bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (self->proxy));

  setup_proxy (self);

  return self;
}

static void
cloud_providers_account_dispose (GObject *object)
{
    CloudProvidersAccount *self = (CloudProvidersAccount *)object;

    g_clear_object (&self->icon);
    g_clear_object (&self->action_group);
    g_clear_object (&self->menu_model);
    if (self->proxy)
        g_signal_handlers_disconnect_by_data (self->proxy, self);
    g_clear_object (&self->proxy);

    G_OBJECT_CLASS (cloud_providers_account_parent_class)->dispose (object);
}

static void
cloud_providers_account_finalize (GObject *object)
{
    CloudProvidersAccount *self = (CloudProvidersAccount *)object;

    g_clear_pointer (&self->name, g_free);
    g_clear_pointer (&self->path, g_free);
    g_clear_pointer (&self->status_details, g_free);

    G_OBJECT_CLASS (cloud_providers_account_parent_class)->finalize (object);
}

static void
cloud_providers_account_class_init (CloudProvidersAccountClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->dispose = cloud_providers_account_dispose;
    object_class->finalize = cloud_providers_account_finalize;
    object_class->set_property = cloud_providers_account_set_property;
    object_class->get_property = cloud_providers_account_get_property;

    /**
     * CloudProvidersAccount:name:
     *
     * The name of the account.
     */
    properties [PROP_NAME] =
        g_param_spec_string ("name", NULL, NULL,
                             NULL,
                             (G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS));

    /**
     * CloudProvidersAccount:path:
     *
     * The path of the directory where files are located.
     */
    properties [PROP_PATH] =
        g_param_spec_string ("path", NULL, NULL,
                             NULL,
                             (G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS));

    /**
     * CloudProvidersAccount:status:
     *
     * Status of the account.
     */
    properties [PROP_STATUS] =
        g_param_spec_enum ("status", NULL, NULL,
                           CLOUD_TYPE_PROVIDERS_ACCOUNT_STATUS,
                           CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID,
                           (G_PARAM_READABLE |
                            G_PARAM_STATIC_STRINGS));

    /**
     * CloudProvidersAccount:status-details:
     *
     * The details of the account status.
     */
    properties [PROP_STATUS_DETAILS] =
        g_param_spec_string ("status-details", NULL, NULL,
                             NULL,
                             (G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS));

    /**
     * CloudProvidersAccount:icon:
     *
     * The icon representing the account.
     */
    properties [PROP_ICON] =
        g_param_spec_object ("icon", NULL, NULL,
                             G_TYPE_ICON,
                             (G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS));

    /**
     * CloudProvidersAccount:menu-model:
     *
     * The menu model associated with the account.
     */
    properties [PROP_MENU_MODEL] =
        g_param_spec_object ("menu-model", NULL, NULL,
                             G_TYPE_MENU_MODEL,
                             (G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS));

    /**
     * CloudProvidersAccount:action-group:
     *
     * The action group associated with the account and menu model.
     */
    properties [PROP_ACTION_GROUP] =
        g_param_spec_object ("action-group", NULL, NULL,
                             G_TYPE_ACTION_GROUP,
                             (G_PARAM_READABLE |
                              G_PARAM_STATIC_STRINGS));

    g_object_class_install_properties (object_class,
                                       N_PROPS,
                                       properties);
}

static void
cloud_providers_account_init (CloudProvidersAccount *self)
{
}

/**
 * cloud_providers_account_get_name:
 * @self: A cloud provider account
 *
 * Get the name of the account
 *
 * Returns: The name of the cloud provider account
 */
const gchar*
cloud_providers_account_get_name (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), NULL);

  return self->name;
}

/**
 * cloud_providers_account_get_status:
 * @self: A cloud provider account
 *
 * Get the status of the account
 *
 * Returns: The status of the cloud provider account
 */
CloudProvidersAccountStatus
cloud_providers_account_get_status (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID);

  return self->status;
}

/**
 * cloud_providers_account_get_status_details:
 * @self: A cloud provider account
 *
 * Get the status details of the account
 *
 * Returns: The status detail description of the cloud provider account
 */
const gchar*
cloud_providers_account_get_status_details (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), NULL);

  return self->status_details;
}

/**
 * cloud_providers_account_get_icon:
 * @self: A cloud provider account
 *
 * Get the icon of the account
 *
 * Returns: (transfer none) : The icon of the cloud provider account
 */
GIcon*
cloud_providers_account_get_icon (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), NULL);

  return self->icon;
}

/**
 * cloud_providers_account_get_menu_model:
 * @self: A cloud provider account
 *
 * Get the menu model exported for the account
 *
 * Returns: (transfer none) : The menu model exported by the cloud provider account
 */
GMenuModel*
cloud_providers_account_get_menu_model (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), NULL);

  return self->menu_model;
}

/**
 * cloud_providers_account_get_action_group:
 * @self: A cloud provider account
 *
 * Get the action group exported in addition to the [class@Gio.MenuModel] from
 * [method@Account.get_menu_model].
 *
 * Returns: (transfer none) : The action group exported by the cloud provider account
 */
GActionGroup*
cloud_providers_account_get_action_group (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), NULL);

  return self->action_group;
}

/**
 * cloud_providers_account_get_path:
 * @self: A cloud provider account
 *
 * Get the directory path of the account
 *
 * Returns: The directory path of the cloud provider account
 */
const gchar *
cloud_providers_account_get_path (CloudProvidersAccount *self)
{
  g_return_val_if_fail (CLOUD_PROVIDERS_IS_ACCOUNT (self), NULL);

  return self->path;
}

