/*
     This file is part of anastasis-gtk.
     Copyright (C) 2020, 2021 Anastasis SARL

     Anastasis 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 3, or (at your
     option) any later version.

     Anastasis 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 Anastasis; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
*/
/**
 * @file src/anastasis/anastasis-gtk_handle-main-window-forward-clicked.c
 * @brief
 * @author Christian Grothoff
 * @author Dennis Neufeld
 */
#include "anastasis_gtk_config.h"
#define HAVE_USED_CONFIG_H 1

#include <gnunet/gnunet_util_lib.h>
#include "anastasis_gtk_util.h"
#include "anastasis-gtk_action.h"
#include "anastasis-gtk_attributes.h"
#include "anastasis-gtk_dispatch.h"
#include "anastasis-gtk_helper.h"
#include <jansson.h>


/**
 * The user selected the 'forward' button. Move on with the
 * country selection.
 */
static void
forward_country_selecting (void)
{
  GtkTreeIter iter;
  GtkTreeView *tv;
  GtkTreeModel *model;
  GtkTreeSelection *sel;
  gchar *country_name;
  gchar *country_code;
  json_t *arguments;

  tv = GTK_TREE_VIEW (GCG_get_main_window_object (
                        "anastasis_gtk_country_treeview"));
  sel = gtk_tree_view_get_selection (tv);
  if (! gtk_tree_selection_get_selected (sel,
                                         &model,
                                         &iter))
  {
    GNUNET_break (0);
    return;
  }
  gtk_tree_model_get (model,
                      &iter,
                      AG_CCMC_COUNTRY_NAME, &country_name,
                      AG_CCMC_COUNTRY_CODE, &country_code,
                      -1);
  arguments = GNUNET_JSON_PACK (
    GNUNET_JSON_pack_string ("country",
                             country_name),
    GNUNET_JSON_pack_string ("country_code",
                             country_code));
  g_free (country_name);
  g_free (country_code);
  AG_freeze ();
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "select_country",
                                  arguments,
                                  &AG_action_cb,
                                  NULL);
  json_decref (arguments);
}


/**
 * The user has entered their personal attributes and
 * confirmed they are correct. Move to the next state.
 * Note that #AG_freeze() was already called.
 */
static void
confirm_attributes (void)
{
  json_t *args;

  args = AG_collect_attributes (false);
  GNUNET_assert (NULL != args);
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "enter_user_attributes",
                                  args,
                                  &AG_action_cb,
                                  NULL);
  json_decref (args);
}


/**
 * Function called with the result of asking the user
 * if they are sure about the personal details they
 * entered.
 *
 * @param the dialog
 * @param response_id must be GTK_RESPONSE_OK to proceed
 * @param user_data the builder
 */
void
anastasis_gtk_user_sure_dialog_response_cb (
  GtkDialog *dialog,
  gint response_id,
  gpointer user_data)
{
  GtkBuilder *builder = user_data;

  gtk_widget_destroy (GTK_WIDGET (dialog));
  g_object_unref (G_OBJECT (builder));
  if (GTK_RESPONSE_OK != response_id)
  {
    AG_thaw ();
    return;
  }
  confirm_attributes ();
}


/**
 * Launch dialog to question certainty of user providing
 * personal details during backup.
 */
static void
question_certainty (void)
{
  GtkBuilder *builder;
  GtkDialog *ad;

  builder = ANASTASIS_GTK_get_new_builder (
    ANASTASIS_GTK_project_data (),
    "anastasis_gtk_user_sure.glade",
    NULL);
  if (NULL == builder)
  {
    GNUNET_break (0);
    return;
  }
  ad = GTK_DIALOG (gtk_builder_get_object (builder,
                                           "anastasis_gtk_user_sure_dialog"));
  if (NULL == ad)
  {
    GNUNET_break (0);
    g_object_unref (G_OBJECT (builder));
    return;
  }
  /* show dialog */
  {
    GtkWidget *toplevel;
    GtkWidget *widget;

#if ! HAVE_HPDF_H
    gtk_widget_hide (
      GTK_WIDGET (
        gtk_builder_get_object (
          builder,
          "anastasis_gtk_print_details_button")));
#endif
    widget = GTK_WIDGET (GCG_get_main_window_object (
                           "anastasis_gtk_auth_button_grid"));
    toplevel = gtk_widget_get_toplevel (widget);
    gtk_window_set_transient_for (GTK_WINDOW (ad),
                                  GTK_WINDOW (toplevel));
    gtk_window_present (GTK_WINDOW (ad));
  }
}


/**
 * The user clicked the 'next' button in the dialog where they enter
 * their personal attributes. If we are in the backup process, make
 * them first confirm that the attributes are correct and well-known
 * to them. Otherwise, simply directly proceed.
 */
void
AG_forward_user_attributes_collecting (void)
{
  bool in_backup;

  AG_freeze ();
  in_backup = (NULL != json_object_get (AG_redux_state,
                                        "backup_state"));
  if (in_backup)
    question_certainty ();
  else
    confirm_attributes ();
}


/**
 * Function called with the result of questioning the user
 * if they really want to proceed with less than three
 * authentication factors.
 *
 * @param the dialog
 * @param response_id must be GTK_RESPONSE_OK to proceed
 * @param user_data the builder
 */
void
anastasis_gtk_confirm_multifactor_dialog_response_cb (
  GtkDialog *dialog,
  gint response_id,
  gpointer user_data)
{
  GtkBuilder *builder = user_data;

  gtk_widget_destroy (GTK_WIDGET (dialog));
  g_object_unref (G_OBJECT (builder));
  if (GTK_RESPONSE_OK != response_id)
  {
    AG_thaw ();
    return;
  }
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "next",
                                  NULL,
                                  &AG_action_cb,
                                  NULL);
}


/**
 * Function called with the result of telling the
 * user that they cannot use 1-FA.
 *
 * @param the dialog
 * @param response_id must be GTK_RESPONSE_OK to proceed
 * @param user_data the builder
 */
void
anastasis_gtk_deny_singlefactor_dialog_response_cb (
  GtkDialog *dialog,
  gint response_id,
  gpointer user_data)
{
  GtkBuilder *builder = user_data;

  gtk_widget_destroy (GTK_WIDGET (dialog));
  g_object_unref (G_OBJECT (builder));
  AG_thaw ();
}


/**
 * Launch dialog to question sanity of user providing
 * too few authentication methods.
 */
static void
question_sanity (void)
{
  GtkBuilder *builder;
  GtkDialog *ad;

  builder = ANASTASIS_GTK_get_new_builder (
    ANASTASIS_GTK_project_data (),
    "anastasis_gtk_warn_multifactor.glade",
    NULL);
  if (NULL == builder)
  {
    GNUNET_break (0);
    return;
  }
  ad = GTK_DIALOG (gtk_builder_get_object (builder,
                                           "anastasis_gtk_confirm_multifactor_dialog"));
  if (NULL == ad)
  {
    GNUNET_break (0);
    g_object_unref (G_OBJECT (builder));
    return;
  }
  /* show dialog */
  {
    GtkWidget *toplevel;
    GtkWidget *widget;

    widget = GTK_WIDGET (GCG_get_main_window_object (
                           "anastasis_gtk_auth_button_grid"));
    toplevel = gtk_widget_get_toplevel (widget);
    gtk_window_set_transient_for (GTK_WINDOW (ad),
                                  GTK_WINDOW (toplevel));
    gtk_window_present (GTK_WINDOW (ad));
  }
}


/**
 * Launch dialog to deny 1-FA setups.
 */
static void
refuse_insanity (void)
{
  GtkBuilder *builder;
  GtkDialog *ad;

  builder = ANASTASIS_GTK_get_new_builder (
    ANASTASIS_GTK_project_data (),
    "anastasis_gtk_deny_singlefactor.glade",
    NULL);
  if (NULL == builder)
  {
    GNUNET_break (0);
    return;
  }
  ad = GTK_DIALOG (gtk_builder_get_object (builder,
                                           "anastasis_gtk_deny_singlefactor_dialog"));
  if (NULL == ad)
  {
    GNUNET_break (0);
    g_object_unref (G_OBJECT (builder));
    return;
  }
  /* show dialog */
  {
    GtkWidget *toplevel;
    GtkWidget *widget;

    widget = GTK_WIDGET (GCG_get_main_window_object (
                           "anastasis_gtk_auth_button_grid"));
    toplevel = gtk_widget_get_toplevel (widget);
    gtk_window_set_transient_for (GTK_WINDOW (ad),
                                  GTK_WINDOW (toplevel));
    gtk_window_present (GTK_WINDOW (ad));
  }
}


/**
 * The user has clicked 'next' in the 'authentications_editing' state.
 * Check if the number of authentication methods configured is above
 * a threshold. If no, warn before allowing to proceed.
 */
static void
forward_authentications_editing (void)
{
  json_t *methods;

  AG_freeze ();
  methods = json_object_get (AG_redux_state,
                             "authentication_methods");
  if (json_array_size (methods) < 2)
  {
    refuse_insanity ();
    return;
  }
  if (json_array_size (methods) < 3)
  {
    question_sanity ();
    return;
  }
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "next",
                                  NULL,
                                  &AG_action_cb,
                                  NULL);
}


/**
 * The user has pressed 'next' in POLICIES_EDITING state.
 * Proceed to secret editing.
 */
static void
forward_policies_reviewing (void)
{
  AG_freeze ();
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "next",
                                  NULL,
                                  &AG_action_cb,
                                  NULL);
}


static void
forward_secret_editing (void)
{
  AG_freeze ();
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "next",
                                  NULL,
                                  &AG_action_cb,
                                  NULL);
}


/**
 * The user has pressed 'next' after selecting a secret to recover.
 */
static void
forward_secret_selecting (void)
{
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreeIter iter;
  json_t *args;
  gchar *provider_url;
  gchar *secret_name;
  gint version;
  gint mask;
  const json_t *providers;

  AG_freeze ();
  AG_stop_long_action ();
  selection = GTK_TREE_SELECTION (
    GCG_get_main_window_object (
      "anastasis_gtk_secret_selection_treeselection"));

  if (! gtk_tree_selection_get_selected (selection,
                                         &model,
                                         &iter))
  {
    GNUNET_break (0);
    AG_insensitive ("anastasis_gtk_main_window_forward_button");
    return;
  }
  gtk_tree_model_get (model,
                      &iter,
                      AG_SSMC_PROVIDER_URL, &provider_url,
                      AG_SSMC_POLICY_VERSION, &version,
                      AG_SSMC_ATTRIBUTE_MASK, &mask,
                      AG_SSMC_SECRET_NAME, &secret_name,
                      AG_SSMC_POLICY_PROVIDER_JSON, &providers,
                      -1);
  args = GNUNET_JSON_PACK (
    GNUNET_JSON_pack_array_incref ("providers",
                                   (json_t *) providers),
    GNUNET_JSON_pack_uint64 ("attribute_mask",
                             mask),
    GNUNET_JSON_pack_string ("secret_name",
                             secret_name));
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Proceeding with policy version %u at provider %s\n",
              (unsigned int) version,
              provider_url);
  g_free (provider_url);
  g_free (secret_name);
  if (NULL != AG_pd)
  {
    ANASTASIS_policy_discovery_stop (AG_pd);
    AG_pd = NULL;
  }
  AG_ra = ANASTASIS_redux_action (AG_redux_state,
                                  "select_version",
                                  args,
                                  &AG_action_cb,
                                  NULL);
  json_decref (args);
}


/**
 * Callback invoked if the the "forward"-button is clicked.
 *
 * @param object
 * @param user_data unused
 */
void
anastasis_gtk_main_window_forward_clicked (GObject *object,
                                           gpointer user_data)
{
  struct DispatchItem actions[] = {
    { .state = "COUNTRY_SELECTING",
      .action = &forward_country_selecting },
    { .state = "USER_ATTRIBUTES_COLLECTING",
      .action = &AG_forward_user_attributes_collecting },
    { .state = "AUTHENTICATIONS_EDITING",
      .action = &forward_authentications_editing },
    { .state = "POLICIES_REVIEWING",
      .action = &forward_policies_reviewing },
    { .state = "SECRET_EDITING",
      .action = &forward_secret_editing },
    { .state = "SECRET_SELECTING",
      .action = &forward_secret_selecting },
    { .state = NULL,
      .action = NULL }
  };

  AG_insensitive ("anastasis_gtk_main_window_forward_button");
  AG_dispatch (actions);
}
