#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <stdio.h>

struct
{
  gchar    *id;
  gchar    *provider_type;
  gchar    *path;
  gboolean  printers_disabled;
} typedef TAccount;

GMainLoop *loop;
GList     *accounts = NULL;

static void
t_account_free (gpointer data)
{
  TAccount *account = (TAccount *) data;

  if (account != NULL)
    {
      g_free (account->id);
      g_free (account->provider_type);
      g_free (account->path);
      g_free (account);
    }
}

GList *
parse_managed_objects (GVariant *output)
{
  GVariant *objects;
  GList    *result = NULL;

  g_variant_get (output, "(@a{oa{sa{sv}}})",
                 &objects);

  if (objects)
    {
      GVariantIter *iter;
      GVariant     *object;

      g_variant_get (objects,
                     "a{oa{sa{sv}}}",
                     &iter);

      while ((object = g_variant_iter_next_value (iter)))
        {
          GVariantIter *iter2;
          GVariant     *object_variant, *service;
          gchar        *object_name;

          g_variant_get (object,
                         "{o@a{sa{sv}}}",
                         &object_name,
                         &object_variant);

          if (g_str_has_prefix (object_name, "/org/gnome/OnlineAccounts/Accounts/"))
            {
              printf ("object: %s\n", object_name);

              g_variant_get (object_variant,
                             "a{sa{sv}}",
                             &iter2);

              while ((service = g_variant_iter_next_value (iter2)))
                {
                  GVariantIter *iter3;
                  GVariant     *service_variant, *property;
                  gchar        *service_name;

                  g_variant_get (service,
                                 "{s@a{sv}}",
                                 &service_name,
                                 &service_variant);

                  if (g_str_has_prefix (service_name, "org.gnome.OnlineAccounts.Account"))
                    {
                      TAccount *account;
                      printf ("  service: %s\n", service_name);

                      account = g_new0 (TAccount, 1);

                      g_variant_get (service_variant,
                                     "a{sv}",
                                     &iter3);

                      account->path = g_strdup (object_name);
                      while ((property = g_variant_iter_next_value (iter3)))
                        {
                          GVariant *value;
                          GVariant *property_variant;
                          gchar    *property_name;

                          g_variant_get (property,
                                         "{s@v}",
                                         &property_name,
                                         &property_variant);

                          g_variant_get (property_variant,
                                         "v",
                                         &value);

                          if (g_strcmp0 (property_name, "Id") == 0)
                            account->id = g_strdup (g_variant_get_string (value, NULL));
                          else if (g_strcmp0 (property_name, "ProviderType") == 0)
                            account->provider_type = g_strdup (g_variant_get_string (value, NULL));
                          else if (g_strcmp0 (property_name, "PrintersDisabled") == 0)
                            account->printers_disabled = g_variant_get_boolean (value);

                          g_free (property_name);
                          g_variant_unref (property_variant);
                          g_variant_unref (value);
                          g_variant_unref (property);
                        }

                      if (g_strcmp0 (account->provider_type, "google") == 0)
                        result = g_list_append (result, account);
                      else
                        t_account_free (account);

                      g_variant_iter_free (iter3);
                    }

                  g_variant_unref (service_variant);
                  g_free (service_name);
                  g_variant_unref (service);
                }

              g_variant_iter_free (iter2);
            }

          g_variant_unref (object_variant);
          g_free (object_name);
          g_variant_unref (object);
        }

      g_variant_iter_free (iter);
      g_variant_unref (objects);
    }

  return result;
}

static void
cloudprint_got_oauth2_access_token_cb (GObject      *source,
                                       GAsyncResult *res,
                                       gpointer      user_data)
{
  GVariant *output;
  GError   *error = NULL;

  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);

  if (output != NULL)
    {
      TAccount *account = (TAccount *) user_data;
      gchar    *access_token = NULL;
      gint      expires_in = 0;

      g_variant_get (output, "(si)",
                     &access_token,
                     &expires_in);

      printf ("Access token for %s:\n", account->path);
      printf ("  access token: %s\n", access_token);
      printf ("  expires in:   %d\n", expires_in);

      g_free (access_token);
    }
  else
    {
      if (error->code != G_IO_ERROR_CANCELLED)
        g_warning ("%s", error->message);
      g_error_free (error);
    }

  g_timeout_add (0, (GSourceFunc) g_main_loop_quit, loop);

  g_object_unref (source);
}

static void
cloudprint_ensure_credentials_cb (GObject      *source,
                                  GAsyncResult *res,
                                  gpointer      user_data)
{
  GVariant *output;
  GError   *error = NULL;

  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);

  if (output != NULL)
    {
      TAccount *account = (TAccount *) user_data;
      gint      expires_in = 0;

      g_variant_get (output, "(i)",
                     &expires_in);

      g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source)),
                              "org.gnome.OnlineAccounts",
                              account->path,
                              "org.gnome.OnlineAccounts.OAuth2Based",
                              "GetAccessToken",
                              NULL,
                              NULL,
                              G_DBUS_CALL_FLAGS_NONE,
                              -1,
                              NULL,
                              cloudprint_got_oauth2_access_token_cb,
                              user_data);
    }
  else
    {
      if (error->code != G_IO_ERROR_CANCELLED)
        g_warning ("%s", error->message);
      g_error_free (error);
    }

  g_object_unref (source);
}

static void
cloudprint_goa_client_new_cb (GObject      *source,
                              GAsyncResult *res,
                              gpointer      user_data)
{
  GVariant *output;
  GError   *error = NULL;

  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);

  if (output != NULL)
    {
      TAccount *account;
      GList    *iter;

      accounts = parse_managed_objects (output);

      printf ("\n");
      for (iter = accounts; iter != NULL; iter = iter->next)
        {
          account = (TAccount *) iter->data;

          printf ("Id:               %s\n", account->id);
          printf ("ProviderType:     %s\n", account->provider_type);
          printf ("PrintersDisabled: %s\n", account->printers_disabled ? "TRUE" : "FALSE");
          printf ("\n");

          g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source)),
                                  "org.gnome.OnlineAccounts",
                                  account->path,
                                  "org.gnome.OnlineAccounts.Account",
                                  "EnsureCredentials",
                                  NULL,
                                  NULL,
                                  G_DBUS_CALL_FLAGS_NONE,
                                  -1,
                                  NULL,
                                  cloudprint_ensure_credentials_cb,
                                  account);
        }

      g_variant_unref (output);
    }
  else
    {
      if (error->code != G_IO_ERROR_CANCELLED)
        g_warning ("%s", error->message);
      g_error_free (error);
    }

  g_object_unref (source);
}

int
main (int argc, char *argv[])
{
  GDBusConnection *connection;

  loop = g_main_loop_new (NULL, TRUE);

  connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);

  g_dbus_connection_call (connection,
                          "org.gnome.OnlineAccounts",
                          "/org/gnome/OnlineAccounts",
                          "org.freedesktop.DBus.ObjectManager",
                          "GetManagedObjects",
                          NULL,
                          NULL,
                          G_DBUS_CALL_FLAGS_NONE,
                          -1,
                          NULL,
                          cloudprint_goa_client_new_cb,
                          NULL);

  g_main_loop_run (loop);

  g_main_loop_unref (loop);

  g_list_free_full (accounts, t_account_free);

  return 0;
}
