/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
 * Copyright (C) 2013 Red Hat
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * 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/>.
 *
 * Written by:
 *     Jasper St. Pierre <jstpierre@mecheye.net>
 */

#include "config.h"

#include "gis-account-page-enterprise.h"
#include "gnome-initial-setup.h"

#include <glib/gi18n.h>
#include <gio/gio.h>

#include <act/act-user-manager.h>

#include "um-realm-manager.h"
#include "um-utils.h"

static void        join_show_prompt    (GisAccountPageEnterprise *page,
                                        GError *error);

static void        on_join_login       (GObject *source,
                                        GAsyncResult *result,
                                        gpointer user_data);

static void        on_realm_joined     (GObject *source,
                                        GAsyncResult *result,
                                        gpointer user_data);

struct _GisAccountPageEnterprisePrivate
{
  GtkWidget *image;
  GtkWidget *login;
  GtkWidget *password;
  GtkWidget *domain;
  GtkWidget *domain_entry;
  GtkTreeModel *realms_model;

  GtkWidget *join_dialog;
  GtkWidget *join_name;
  GtkWidget *join_password;
  GtkWidget *join_domain;
  GtkWidget *join_computer;

  ActUserManager *act_client;
  ActUser *act_user;

  guint realmd_watch;
  UmRealmManager *realm_manager;
  gboolean domain_chosen;

  /* Valid during apply */
  UmRealmObject *realm;
  GCancellable *cancellable;
  gboolean join_prompted;

  GisPageApplyCallback apply_complete_callback;
  gpointer apply_complete_data;
};
typedef struct _GisAccountPageEnterprisePrivate GisAccountPageEnterprisePrivate;

G_DEFINE_TYPE_WITH_PRIVATE (GisAccountPageEnterprise, gis_account_page_enterprise, GTK_TYPE_BIN);

enum {
  VALIDATION_CHANGED,
  USER_CACHED,
  LAST_SIGNAL,
};

static guint signals[LAST_SIGNAL] = { 0 };

static void
validation_changed (GisAccountPageEnterprise *page)
{
  g_signal_emit (page, signals[VALIDATION_CHANGED], 0);
}

static void
apply_complete (GisAccountPageEnterprise *page,
                gboolean                  valid)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  priv->apply_complete_callback (NULL, valid, priv->apply_complete_data);
}

static void
show_error_dialog (GisAccountPageEnterprise *page,
                   const gchar *message,
                   GError *error)
{
  GtkWidget *dialog;

  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
    return;

  dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                   GTK_MESSAGE_ERROR,
                                   GTK_BUTTONS_CLOSE,
                                   "%s", message);

  if (error != NULL) {
    g_dbus_error_strip_remote_error (error);
    gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                              "%s", error->message);
  }

  g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
  gtk_window_present (GTK_WINDOW (dialog));
}

gboolean
gis_account_page_enterprise_validate (GisAccountPageEnterprise *page)
{
  const gchar *name;
  gboolean valid_name;
  gboolean valid_domain;
  GtkTreeIter iter;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  name = gtk_entry_get_text (GTK_ENTRY (priv->login));
  valid_name = is_valid_name (name);

  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->domain), &iter)) {
    gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->domain)),
                        &iter, 0, &name, -1);
  } else {
    name = gtk_entry_get_text (GTK_ENTRY (priv->domain_entry));
  }

  valid_domain = is_valid_name (name);
  return valid_name && valid_domain;
}

static void
on_permit_user_login (GObject *source,
                      GAsyncResult *result,
                      gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  UmRealmCommon *common;
  GError *error = NULL;
  gchar *login;

  common = UM_REALM_COMMON (source);
  um_realm_common_call_change_login_policy_finish (common, result, &error);
  if (error == NULL) {

    /*
     * Now tell the account service about this user. The account service
     * should also lookup information about this via the realm and make
     * sure all that is functional.
     */
    login = um_realm_calculate_login (common, gtk_entry_get_text (GTK_ENTRY (priv->login)));
    g_return_if_fail (login != NULL);

    g_debug ("Caching remote user: %s", login);

    priv->act_user = act_user_manager_cache_user (priv->act_client, login, NULL);
    g_signal_emit (page, signals[USER_CACHED], 0, priv->act_user, gtk_entry_get_text (GTK_ENTRY (priv->password)));
    apply_complete (page, TRUE);

    g_free (login);
  } else {
    show_error_dialog (page, _("Failed to register account"), error);
    g_message ("Couldn't permit logins on account: %s", error->message);
    g_error_free (error);
    apply_complete (page, FALSE);
  }
}

static void
enterprise_permit_user_login (GisAccountPageEnterprise *page, UmRealmObject *realm)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  UmRealmCommon *common;
  gchar *login;
  const gchar *add[2];
  const gchar *remove[1];
  GVariant *options;

  common = um_realm_object_get_common (realm);

  login = um_realm_calculate_login (common, gtk_entry_get_text (GTK_ENTRY (priv->login)));
  g_return_if_fail (login != NULL);

  add[0] = login;
  add[1] = NULL;
  remove[0] = NULL;

  g_debug ("Permitting login for: %s", login);
  options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);

  um_realm_common_call_change_login_policy (common, "",
                                            add, remove, options,
                                            priv->cancellable,
                                            on_permit_user_login,
                                            page);

  g_object_unref (common);
  g_free (login);
}

static void
on_set_static_hostname (GObject *source,
                        GAsyncResult *result,
                        gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GError *error = NULL;
  GVariant *retval;

  retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
  if (error != NULL) {
    join_show_prompt (page, error);
    g_error_free (error);
    return;
  }

  g_variant_unref (retval);

  /* Prompted for some admin credentials, try to use them to log in */
  um_realm_login (priv->realm,
                  gtk_entry_get_text (GTK_ENTRY (priv->join_name)),
                  gtk_entry_get_text (GTK_ENTRY (priv->join_password)),
                  priv->cancellable, on_join_login, page);
}

static void
on_join_response (GtkDialog *dialog,
                  gint response,
                  gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GDBusConnection *connection;
  GError *error = NULL;
  gchar hostname[128];
  const gchar *name;

  gtk_widget_hide (GTK_WIDGET (dialog));
  if (response != GTK_RESPONSE_OK) {
    apply_complete (page, FALSE);
    return;
  }

  name = gtk_entry_get_text (GTK_ENTRY (priv->join_computer));
  if (gethostname (hostname, sizeof (hostname)) == 0 &&
      !g_str_equal (name, hostname)) {
    g_debug ("Setting StaticHostname to '%s'", name);

    connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, priv->cancellable, &error);
    if (error != NULL) {
      apply_complete (page, FALSE);
      g_warning ("Could not get DBus connection: %s", error->message);
      g_error_free (error);
      return;
    }

    g_dbus_connection_call (connection, "org.freedesktop.hostname1",
                            "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
                            "SetStaticHostname",
                            g_variant_new ("(sb)", name, TRUE),
                            G_VARIANT_TYPE ("()"),
                            G_DBUS_CALL_FLAGS_NONE,
                            G_MAXINT, NULL, on_set_static_hostname, page);

  } else {
    name = gtk_entry_get_text (GTK_ENTRY (priv->join_name));
    g_debug ("Logging in as admin user: %s", name);

    /* Prompted for some admin credentials, try to use them to log in */
    um_realm_login (priv->realm, name,
                    gtk_entry_get_text (GTK_ENTRY (priv->join_password)),
                    NULL, on_join_login, page);
  }
}

static void
join_show_prompt (GisAccountPageEnterprise *page,
                  GError *error)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  UmRealmKerberosMembership *membership;
  UmRealmKerberos *kerberos;
  gchar hostname[128];
  const gchar *name;

  gtk_entry_set_text (GTK_ENTRY (priv->join_password), "");
  gtk_widget_grab_focus (GTK_WIDGET (priv->join_password));

  kerberos = um_realm_object_get_kerberos (priv->realm);
  membership = um_realm_object_get_kerberos_membership (priv->realm);

  gtk_label_set_text (GTK_LABEL (priv->join_domain),
                      um_realm_kerberos_get_domain_name (kerberos));

  if (gethostname (hostname, sizeof (hostname)) == 0)
    gtk_entry_set_text (GTK_ENTRY (priv->join_computer), hostname);

  clear_entry_validation_error (GTK_ENTRY (priv->join_name));
  clear_entry_validation_error (GTK_ENTRY (priv->join_password));

  if (!priv->join_prompted) {
    name = um_realm_kerberos_membership_get_suggested_administrator (membership);
    if (name && !g_str_equal (name, "")) {
      g_debug ("Suggesting admin user: %s", name);
      gtk_entry_set_text (GTK_ENTRY (priv->join_name), name);
    } else {
      gtk_widget_grab_focus (GTK_WIDGET (priv->join_name));
    }

  } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_HOSTNAME)) {
    g_debug ("Bad host name: %s", error->message);
    set_entry_validation_error (GTK_ENTRY (priv->join_computer), error->message);

  } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
    g_debug ("Bad admin password: %s", error->message);
    set_entry_validation_error (GTK_ENTRY (priv->join_password), error->message);

  } else {
    g_debug ("Admin login failure: %s", error->message);
    g_dbus_error_strip_remote_error (error);
    set_entry_validation_error (GTK_ENTRY (priv->join_name), error->message);
  }

  g_debug ("Showing admin password dialog");
  gtk_window_set_transient_for (GTK_WINDOW (priv->join_dialog),
                                GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))));
  gtk_window_set_modal (GTK_WINDOW (priv->join_dialog), TRUE);
  gtk_window_present (GTK_WINDOW (priv->join_dialog));

  priv->join_prompted = TRUE;
  g_object_unref (kerberos);
  g_object_unref (membership);

  /* And now we wait for on_join_response() */
}

static void
on_join_login (GObject *source,
               GAsyncResult *result,
               gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GError *error = NULL;
  GBytes *creds;

  um_realm_login_finish (priv->realm, result, &creds, &error);

  /* Logged in as admin successfully, use creds to join domain */
  if (error == NULL) {
    if (!um_realm_join_as_admin (priv->realm,
                                 gtk_entry_get_text (GTK_ENTRY (priv->join_name)),
                                 gtk_entry_get_text (GTK_ENTRY (priv->join_password)),
                                 creds, NULL, on_realm_joined, page)) {
      show_error_dialog (page, _("No supported way to authenticate with this domain"), NULL);
      g_message ("Authenticating as admin is not supported by the realm");
    }

    g_bytes_unref (creds);

  /* Couldn't login as admin, show prompt again */
  } else {
    join_show_prompt (page, error);
    g_message ("Couldn't log in as admin to join domain: %s", error->message);
    g_error_free (error);
  }
}

static void
on_realm_joined (GObject *source,
                 GAsyncResult *result,
                 gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GError *error = NULL;

  um_realm_join_finish (priv->realm, result, &error);

  /* Yay, joined the domain, register the user locally */
  if (error == NULL) {
    g_debug ("Joining realm completed successfully");
    enterprise_permit_user_login (page, priv->realm);

    /* Credential failure while joining domain, prompt for admin creds */
  } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN) ||
             g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD) ||
             g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_HOSTNAME)) {
    g_debug ("Joining realm failed due to credentials or host name");

    join_show_prompt (page, error);

    /* Other failure */
  } else {
    show_error_dialog (page, _("Failed to join domain"), error);
    g_message ("Failed to join the domain: %s", error->message);
    apply_complete (page, FALSE);
  }

  g_clear_error (&error);
}

static void
on_realm_login (GObject *source,
                GAsyncResult *result,
                gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GError *error = NULL;
  GBytes *creds = NULL;

  um_realm_login_finish (priv->realm, result, &creds, &error);

  /*
   * User login is valid, but cannot authenticate right now (eg: user needs
   * to change password at next login etc.)
   */
  if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_CANNOT_AUTH)) {
    g_clear_error (&error);
    creds = NULL;
  }

  if (error == NULL) {

    /* Already joined to the domain, just register this user */
    if (um_realm_is_configured (priv->realm)) {
      g_debug ("Already joined to this realm");
      enterprise_permit_user_login (page, priv->realm);

      /* Join the domain, try using the user's creds */
    } else if (creds == NULL ||
               !um_realm_join_as_user (priv->realm,
                                       gtk_entry_get_text (GTK_ENTRY (priv->login)),
                                       gtk_entry_get_text (GTK_ENTRY (priv->password)),
                                       creds, priv->cancellable,
                                       on_realm_joined,
                                       page)) {

      /* If we can't do user auth, try to authenticate as admin */
      g_debug ("Cannot join with user credentials");

      join_show_prompt (page, error);
    }

    g_bytes_unref (creds);

    /* A problem with the user's login name or password */
  } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN)) {
    g_debug ("Problem with the user's login: %s", error->message);
    set_entry_validation_error (GTK_ENTRY (priv->login), error->message);
    gtk_widget_grab_focus (priv->login);
    apply_complete (page, FALSE);

  } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
    g_debug ("Problem with the user's password: %s", error->message);
    set_entry_validation_error (GTK_ENTRY (priv->password), error->message);
    gtk_widget_grab_focus (priv->password);
    apply_complete (page, FALSE);

    /* Other login failure */
  } else {
    show_error_dialog (page, _("Failed to log into domain"), error);
    g_message ("Couldn't log in as user: %s", error->message);
    apply_complete (page, FALSE);
  }

  g_clear_error (&error);
}

static void
enterprise_check_login (GisAccountPageEnterprise *page)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  g_assert (priv->realm);

  um_realm_login (priv->realm,
                  gtk_entry_get_text (GTK_ENTRY (priv->login)),
                  gtk_entry_get_text (GTK_ENTRY (priv->password)),
                  priv->cancellable,
                  on_realm_login,
                  page);
}

static void
on_realm_discover_input (GObject *source,
                         GAsyncResult *result,
                         gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GError *error = NULL;
  GList *realms;

  realms = um_realm_manager_discover_finish (priv->realm_manager,
                                             result, &error);

  /* Found a realm, log user into domain */
  if (error == NULL) {
    g_assert (realms != NULL);
    priv->realm = g_object_ref (realms->data);
    enterprise_check_login (page);
    g_list_free_full (realms, g_object_unref);

  } else {
    /* The domain is likely invalid */
    g_dbus_error_strip_remote_error (error);
    g_message ("Couldn't discover domain: %s", error->message);
    gtk_widget_grab_focus (priv->domain_entry);
    set_entry_validation_error (GTK_ENTRY (priv->domain_entry), error->message);
    apply_complete (page, FALSE);
    g_error_free (error);
  }
}

static void
enterprise_add_user (GisAccountPageEnterprise *page)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GtkTreeIter iter;

  priv->join_prompted = FALSE;
  g_clear_object (&priv->realm);

  /* Already know about this realm, try to login as user */
  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->domain), &iter)) {
    gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->domain)),
                        &iter, 1, &priv->realm, -1);
    enterprise_check_login (page);

    /* Something the user typed, we need to discover realm */
  } else {
    um_realm_manager_discover (priv->realm_manager,
                               gtk_entry_get_text (GTK_ENTRY (priv->domain_entry)),
                               priv->cancellable,
                               on_realm_discover_input,
                               page);
  }
}

gboolean
gis_account_page_enterprise_apply (GisAccountPageEnterprise *page,
                                   GCancellable             *cancellable,
                                   GisPageApplyCallback      callback,
                                   gpointer                  data)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  priv->apply_complete_callback = callback;
  priv->apply_complete_data = data;
  priv->cancellable = g_object_ref (cancellable);
  enterprise_add_user (page);
  return TRUE;
}

static gchar *
realm_get_name (UmRealmObject *realm)
{
  UmRealmCommon *common;
  gchar *name;

  common = um_realm_object_get_common (realm);
  name = g_strdup (um_realm_common_get_name (common));
  g_object_unref (common);

  return name;
}

static gboolean
model_contains_realm (GtkTreeModel *model,
                      const gchar *realm_name)
{
  gboolean contains = FALSE;
  GtkTreeIter iter;
  gboolean match;
  gchar *name;
  gboolean ret;

  ret = gtk_tree_model_get_iter_first (model, &iter);
  while (ret) {
    gtk_tree_model_get (model, &iter, 0, &name, -1);
    match = (g_strcmp0 (name, realm_name) == 0);
    g_free (name);
    if (match) {
      g_debug ("ignoring duplicate realm: %s", realm_name);
      contains = TRUE;
      break;
    }
    ret = gtk_tree_model_iter_next (model, &iter);
  }

  return contains;
}

static void
enterprise_add_realm (GisAccountPageEnterprise *page,
                      UmRealmObject  *realm)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GtkTreeIter iter;
  gchar *name;

  name = realm_get_name (realm);

  /*
   * Don't add a second realm if we already have one with this name.
   * Sometimes realmd returns two realms for the same name, if it has
   * different ways to use that realm. The first one that realmd
   * returns is the one it prefers.
   */

  if (!model_contains_realm (GTK_TREE_MODEL (priv->realms_model), name)) {
    gtk_list_store_append (GTK_LIST_STORE (priv->realms_model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (priv->realms_model), &iter,
                        0, name,
                        1, realm,
                        -1);

    if (!priv->domain_chosen && um_realm_is_configured (realm))
      gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->domain), &iter);

    g_debug ("added realm to drop down: %s %s", name,
             g_dbus_object_get_object_path (G_DBUS_OBJECT (realm)));
  }

  g_free (name);
}

static void
on_manager_realm_added (UmRealmManager  *manager,
                        UmRealmObject   *realm,
                        gpointer         user_data)
{
  GisAccountPageEnterprise *page = user_data;
  enterprise_add_realm (page, realm);
}

static void
on_realm_manager_created (GObject *source,
                          GAsyncResult *result,
                          gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GError *error = NULL;
  GList *realms, *l;

  g_clear_object (&priv->realm_manager);
  priv->realm_manager = um_realm_manager_new_finish (result, &error);

  if (error != NULL) {
    g_warning ("Couldn't contact realmd service: %s", error->message);
    g_error_free (error);
    return;
  }

  /* Lookup all the realm objects */
  realms = um_realm_manager_get_realms (priv->realm_manager);
  for (l = realms; l != NULL; l = g_list_next (l))
    enterprise_add_realm (page, l->data);

  g_list_free (realms);
  g_signal_connect (priv->realm_manager, "realm-added",
                    G_CALLBACK (on_manager_realm_added), page);

  /* When no realms try to discover a sensible default, triggers realm-added signal */
  um_realm_manager_discover (priv->realm_manager, "", NULL, NULL, NULL);
  gtk_widget_set_visible (GTK_WIDGET (page), TRUE);
}

static void
on_realmd_appeared (GDBusConnection *connection,
                    const gchar *name,
                    const gchar *name_owner,
                    gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  um_realm_manager_new (NULL, on_realm_manager_created, page);
}

static void
on_realmd_disappeared (GDBusConnection *unused1,
                       const gchar *unused2,
                       gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  if (priv->realm_manager != NULL) {
    g_signal_handlers_disconnect_by_func (priv->realm_manager,
                                          on_manager_realm_added,
                                          page);
    g_clear_object (&priv->realm_manager);
  }

  gtk_widget_set_visible (GTK_WIDGET (page), FALSE);
}

static void
on_domain_changed (GtkComboBox *widget,
                   gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  priv->domain_chosen = TRUE;
  validation_changed (page);
  clear_entry_validation_error (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget))));
}

static void
on_entry_changed (GtkEditable *editable,
                  gpointer user_data)
{
  GisAccountPageEnterprise *page = user_data;
  validation_changed (page);
  clear_entry_validation_error (GTK_ENTRY (editable));
}

static void
gis_account_page_enterprise_realize (GtkWidget *widget)
{
  GisAccountPageEnterprise *page = GIS_ACCOUNT_PAGE_ENTERPRISE (widget);
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);
  GtkWidget *gis_page;

  gis_page = gtk_widget_get_ancestor (widget, GIS_TYPE_PAGE);
  g_object_bind_property (gis_page, "small-screen",
                          priv->image, "visible",
                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);

  GTK_WIDGET_CLASS (gis_account_page_enterprise_parent_class)->realize (widget);
}

static void
gis_account_page_enterprise_constructed (GObject *object)
{
  GisAccountPageEnterprise *page = GIS_ACCOUNT_PAGE_ENTERPRISE (object);
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  G_OBJECT_CLASS (gis_account_page_enterprise_parent_class)->constructed (object);

  priv->act_client = act_user_manager_get_default ();

  priv->realmd_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "org.freedesktop.realmd",
                                         G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
                                         on_realmd_appeared, on_realmd_disappeared,
                                         page, NULL);

  g_signal_connect (priv->join_dialog, "response",
                    G_CALLBACK (on_join_response), page);
  g_signal_connect (priv->domain, "changed",
                    G_CALLBACK (on_domain_changed), page);
  g_signal_connect (priv->login, "changed",
                    G_CALLBACK (on_entry_changed), page);
}

static void
gis_account_page_enterprise_dispose (GObject *object)
{
  GisAccountPageEnterprise *page = GIS_ACCOUNT_PAGE_ENTERPRISE (object);
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  if (priv->realmd_watch)
    g_bus_unwatch_name (priv->realmd_watch);

  priv->realmd_watch = 0;

  g_cancellable_cancel (priv->cancellable);

  g_clear_object (&priv->realm_manager);
  g_clear_object (&priv->realm);
  g_clear_object (&priv->cancellable);

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

static void
gis_account_page_enterprise_class_init (GisAccountPageEnterpriseClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->constructed = gis_account_page_enterprise_constructed;
  object_class->dispose = gis_account_page_enterprise_dispose;

  widget_class->realize = gis_account_page_enterprise_realize;

  gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-account-page-enterprise.ui");

  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, login);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, password);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, domain);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, domain_entry);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, realms_model);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, image);

  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_dialog);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_name);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_password);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_domain);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_computer);

  signals[VALIDATION_CHANGED] = g_signal_new ("validation-changed", GIS_TYPE_ACCOUNT_PAGE_ENTERPRISE,
                                              G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
                                              G_TYPE_NONE, 0);
  signals[USER_CACHED] = g_signal_new ("user-cached", GIS_TYPE_ACCOUNT_PAGE_ENTERPRISE,
                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
                                        G_TYPE_NONE, 2, ACT_TYPE_USER, G_TYPE_STRING);
}

static void
gis_account_page_enterprise_init (GisAccountPageEnterprise *page)
{
  gtk_widget_init_template (GTK_WIDGET (page));
}

void
gis_account_page_enterprise_shown (GisAccountPageEnterprise *page)
{
  GisAccountPageEnterprisePrivate *priv = gis_account_page_enterprise_get_instance_private (page);

  gtk_widget_grab_focus (priv->domain_entry);
}
