/*
     This file is part of anastasis-gtk.
     Copyright (C) 2020 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_io.c
 * @brief
 * @author Christian Grothoff
 */
#include <gnunet/gnunet_util_lib.h>
#include "anastasis_gtk_util.h"
#include "anastasis-gtk_attributes.h"
#include "anastasis-gtk_dispatch.h"
#include "anastasis-gtk_helper.h"
#include <jansson.h>


/**
 * Return a file filter matching the files we store states in.
 *
 * @return the filter, ownership is passed to the caller
 */
static GListModel *
state_filters (void)
{
  GtkFileFilter *filter;
  GListStore *filters;

  filter = gtk_file_filter_new ();
  gtk_file_filter_set_name (filter,
                            _ ("Anastasis state files"));
  gtk_file_filter_add_pattern (filter,
                               "*.ana");
  filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
  g_list_store_append (filters,
                       filter);
  g_object_unref (filter);
  return G_LIST_MODEL (filters);
}


/**
 * Function called once the user picked the state file to open.
 *
 * @param source the file dialog
 * @param res the result of the user's choice
 * @param user_data unused
 */
static void
open_state_ready_cb (GObject *source,
                     GAsyncResult *res,
                     gpointer user_data)
{
  GFile *file;
  char *filename;

  (void) user_data;
  file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source),
                                      res,
                                      NULL);
  if (NULL == file)
    return; /* user aborted */
  filename = ANASTASIS_GTK_file_get_path_utf8 (file);
  g_object_unref (file);
  if (NULL == filename)
    return;
  AG_load (filename);
  GNUNET_free (filename);
}


/**
 * User clicked the "open" button.
 *
 * @param button the button
 * @param user_data unused
 */
void
anastasis_gtk_open_state_clicked_cb (GtkButton *button,
                                     gpointer user_data)
{
  GtkFileDialog *dialog;
  GListModel *filters;

  (void) user_data;
  dialog = gtk_file_dialog_new ();
  gtk_file_dialog_set_title (dialog,
                             _ ("Open saved Anastasis state"));
  filters = state_filters ();
  gtk_file_dialog_set_filters (dialog,
                               filters);
  g_object_unref (filters);
  gtk_file_dialog_open (dialog,
                        GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
                        NULL,
                        &open_state_ready_cb,
                        NULL);
  g_object_unref (dialog);
}


/**
 * Serialize state of currently shown use attribute editing frame to JSON.
 */
static void
save_user_attributes_collecting (void)
{
  json_t *ia;

  ia = AG_collect_attributes (true);
  if (NULL == ia)
  {
    GNUNET_break (0);
    return;
  }
  GNUNET_break (0 ==
                json_object_set (AG_redux_state,
                                 "identity_attributes",
                                 json_object_get (ia,
                                                  "identity_attributes")));
  json_decref (ia);
}


/**
 * Write the current state to @a filename, appending the ".ana" suffix if the
 * user did not provide it.
 *
 * @param filename where to write the state to, freed by this function
 */
static void
write_state (char *filename)
{
  {
    const char *ana;

    ana = strstr (filename,
                  ".ana");
    if ( (NULL == ana) ||
         (4 != strlen (ana)) )
    {
      char *tmp;

      GNUNET_asprintf (&tmp,
                       "%s.ana",
                       filename);
      GNUNET_free (filename);
      filename = tmp;
    }
  }
  if (0 !=
      json_dump_file (AG_redux_state,
                      filename,
                      JSON_COMPACT))
  {
    AG_error ("Failed to write state to `%s'\n",
              filename);
  }
  GNUNET_free (filename);
}


/**
 * The user answered the question whether they really want their secret written
 * to disk in cleartext.
 *
 * @param source the warning dialog
 * @param res the result of the user's choice
 * @param user_data the file name to write to
 */
static void
cleartext_warning_response_cb (GObject *source,
                              GAsyncResult *res,
                              gpointer user_data)
{
  char *filename = user_data;

  if (0 !=
      gtk_alert_dialog_choose_finish (GTK_ALERT_DIALOG (source),
                                      res,
                                      NULL))
  {
    /* user aborted */
    GNUNET_free (filename);
    return;
  }
  write_state (filename);
}


/**
 * Function called once the user picked the file to save the state in.
 *
 * @param source the file dialog
 * @param res the result of the user's choice
 * @param user_data unused
 */
static void
save_state_ready_cb (GObject *source,
                     GAsyncResult *res,
                     gpointer user_data)
{
  static const struct DispatchItem save_state[] = {
    { .state = "USER_ATTRIBUTES_COLLECTING",
      .action = &save_user_attributes_collecting },
    { .state = NULL,
      .action = NULL }
  };
  GFile *file;
  char *filename;

  (void) user_data;
  file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (source),
                                      res,
                                      NULL);
  if (NULL == file)
    return; /* user aborted */
  filename = ANASTASIS_GTK_file_get_path_utf8 (file);
  g_object_unref (file);
  if (NULL == filename)
    return;
  (void) AG_dispatch (save_state);

  /* check if we should warn the user about writing 'core_secret' to disk */
  {
    json_t *cs;

    cs = json_object_get (AG_redux_state,
                          "core_secret");
    if ( (NULL != cs) &&
         (! json_is_null (cs)) )
    {
      GtkAlertDialog *diag;
      GtkRoot *toplevel;
      const char *buttons[] = {
        _ ("_OK"),
        _ ("_Cancel"),
        NULL
      };

      toplevel = gtk_widget_get_root (
        GTK_WIDGET (GCG_get_main_window_object (
                      "anastasis_gtk_main_window")));
      diag = gtk_alert_dialog_new (
        "%s",
        _ ("This will write your secret to disk in cleartext!"));
      gtk_alert_dialog_set_buttons (diag,
                                    buttons);
      gtk_alert_dialog_set_cancel_button (diag,
                                          1);
      gtk_alert_dialog_set_default_button (diag,
                                           0);
      gtk_alert_dialog_choose (diag,
                               GTK_WINDOW (toplevel),
                               NULL,
                               &cleartext_warning_response_cb,
                               filename);
      g_object_unref (diag);
      return;
    }
  }

  /* all good, do writing! */
  write_state (filename);
}


/**
 * User clicked the "save as" button.
 *
 * @param button the button
 * @param user_data unused
 */
void
anastasis_gtk_main_window_save_as_button_clicked_cb (GtkButton *button,
                                                     gpointer user_data)
{
  GtkFileDialog *dialog;
  GListModel *filters;

  (void) user_data;
  dialog = gtk_file_dialog_new ();
  gtk_file_dialog_set_title (dialog,
                             _ ("Save Anastasis state"));
  filters = state_filters ();
  gtk_file_dialog_set_filters (dialog,
                               filters);
  g_object_unref (filters);
  gtk_file_dialog_set_initial_name (dialog,
                                    "untitled.ana");
  gtk_file_dialog_save (dialog,
                        GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))),
                        NULL,
                        &save_state_ready_cb,
                        NULL);
  g_object_unref (dialog);
}
