/*
     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 "anastasis-gtk_rows.h"
#include <jansson.h>


/**
 * The user selected the 'forward' button. Move on with the
 * country selection.
 */
static void
forward_country_selecting (void)
{
  AGRow *row;
  json_t *arguments;

  row = AG_selected_row ("anastasis_gtk_country_selection");
  if (NULL == row)
  {
    GNUNET_break (0);
    return;
  }
  arguments = GNUNET_JSON_PACK (
    GNUNET_JSON_pack_string ("country",
                             AG_row_string (row,
                                            "name")),
    GNUNET_JSON_pack_string ("country_code",
                             AG_row_string (row,
                                            "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_window_destroy (GTK_WINDOW (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.ui",
    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 */
  {
    GtkRoot *toplevel;
    GtkWidget *widget;

#if ! HAVE_HPDF_H
    gtk_widget_set_visible (GTK_WIDGET (
        gtk_builder_get_object (
          builder,
          "anastasis_gtk_print_details_button")),
                           FALSE);
#endif
    widget = GTK_WIDGET (GCG_get_main_window_object (
                           "anastasis_gtk_auth_button_grid"));
    toplevel = gtk_widget_get_root (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 source the dialog
 * @param res the result of the user's choice, button 1 to proceed
 * @param user_data unused
 */
static void
confirm_multifactor_response_cb (GObject *source,
                                 GAsyncResult *res,
                                 gpointer user_data)
{
  (void) user_data;
  if (1 !=
      gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
                                      res,
                                      NULL))
  {
    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 source the dialog
 * @param res the result of the user's choice, unused
 * @param user_data unused
 */
static void
deny_singlefactor_response_cb (GObject *source,
                               GAsyncResult *res,
                               gpointer user_data)
{
  (void) user_data;
  (void) gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
                                         res,
                                         NULL);
  AG_thaw ();
}


/**
 * Return the main window, to be used as the parent of our dialogs.
 *
 * @return the main window
 */
static GtkWindow *
main_window (void)
{
  GtkWidget *widget;

  widget = GTK_WIDGET (GCG_get_main_window_object (
                         "anastasis_gtk_auth_button_grid"));
  return GTK_WINDOW (gtk_widget_get_root (widget));
}


/**
 * Launch dialog to question sanity of user providing
 * too few authentication methods.
 */
static void
question_sanity (void)
{
  GtkAlertDialog *ad;
  const char *buttons[] = {
    _ ("_Back"),
    _ ("_Next"),
    NULL
  };

  ad = gtk_alert_dialog_new (
    "%s",
    _ (
      "To keep your secret secure, we recommend you specify at least three authentication options before proceeding."));
  gtk_alert_dialog_set_detail (
    ad,
    _ ("Select \"back\" to add additional authentication options."));
  gtk_alert_dialog_set_buttons (ad,
                                buttons);
  gtk_alert_dialog_set_cancel_button (ad,
                                      0);
  gtk_alert_dialog_set_default_button (ad,
                                       1);
  gtk_alert_dialog_choose (ad,
                           main_window (),
                           NULL,
                           &confirm_multifactor_response_cb,
                           NULL);
  g_object_unref (ad);
}


/**
 * Launch dialog to deny 1-FA setups.
 */
static void
refuse_insanity (void)
{
  GtkAlertDialog *ad;
  const char *buttons[] = {
    _ ("_Back"),
    NULL
  };

  ad = gtk_alert_dialog_new (
    "%s",
    _ (
      "To keep your secret secure, we require at least two authentication options, and recommend you specify at least three authentication options before proceeding."));
  gtk_alert_dialog_set_detail (
    ad,
    _ ("Select \"back\" to add additional authentication options."));
  gtk_alert_dialog_set_buttons (ad,
                                buttons);
  gtk_alert_dialog_set_cancel_button (ad,
                                      0);
  gtk_alert_dialog_set_default_button (ad,
                                       0);
  gtk_alert_dialog_choose (ad,
                           main_window (),
                           NULL,
                           &deny_singlefactor_response_cb,
                           NULL);
  g_object_unref (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)
{
  AGRow *row;
  json_t *args;

  AG_freeze ();
  AG_stop_long_action ();
  row = AG_selected_row ("anastasis_gtk_secret_selection_treeselection");
  if (NULL == row)
  {
    GNUNET_break (0);
    AG_insensitive ("anastasis_gtk_main_window_forward_button");
    return;
  }
  args = GNUNET_JSON_PACK (
    GNUNET_JSON_pack_array_incref ("providers",
                                   AG_row_json (row,
                                                "providers")),
    GNUNET_JSON_pack_uint64 ("attribute_mask",
                             AG_row_uint (row,
                                          "attribute_mask")),
    GNUNET_JSON_pack_string ("secret_name",
                             AG_row_string (row,
                                            "secret_name")));
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Proceeding with policy version %u at provider %s\n",
              (unsigned int) AG_row_uint (row,
                                          "version"),
              AG_row_string (row,
                             "provider_url"));
  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);
}
